<?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>Posts on 夜云泊</title>
    <link>https://lifeislife.cn/posts/</link>
    <description>feedId:57980998056508425+userId:73222296380546048 Recent content in Posts on 夜云泊</description>
    <generator>Hugo -- 0.161.1</generator>
    <language>zh</language>
    <lastBuildDate>Sun, 08 Feb 2026 10:34:10 +0000</lastBuildDate>
    <atom:link href="https://lifeislife.cn/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>硬件仿真平台 (Palladium / ZeBu / HAPS) 和软件仿真平台 (QEMU) 的区别</title>
      <link>https://lifeislife.cn/posts/%E7%A1%AC%E4%BB%B6%E4%BB%BF%E7%9C%9F%E5%B9%B3%E5%8F%B0palladium/zebu/haps%E5%92%8C%E8%BD%AF%E4%BB%B6%E4%BB%BF%E7%9C%9F%E5%B9%B3%E5%8F%B0qemu%E7%9A%84%E5%8C%BA%E5%88%AB/</link>
      <pubDate>Sun, 08 Feb 2026 10:34:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E7%A1%AC%E4%BB%B6%E4%BB%BF%E7%9C%9F%E5%B9%B3%E5%8F%B0palladium/zebu/haps%E5%92%8C%E8%BD%AF%E4%BB%B6%E4%BB%BF%E7%9C%9F%E5%B9%B3%E5%8F%B0qemu%E7%9A%84%E5%8C%BA%E5%88%AB/</guid>
      <description>&lt;h3 id=&#34;核心区别翻译vs模拟&#34;&gt;核心区别——“翻译”vs“模拟”&lt;/h3&gt;
&lt;p&gt;最本质的区别在于它们运行的&lt;strong&gt;对象&lt;/strong&gt;不同：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;QEMU（软件仿真）：运行的是“行为模型”。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原理：&lt;/strong&gt; QEMU 是一个纯软件程序，运行在通用的服务器（x86 架构）上。它不关心芯片内部具体的电路是怎么连的，它只关心&lt;strong&gt;功能&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;比喻：&lt;/strong&gt; 就像你在电脑上玩“超级马里奥”模拟器。电脑并不含有任天堂的游戏机电路，它只是用软件模拟了“按 A 键跳跃”这个&lt;strong&gt;行为&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键词：&lt;/strong&gt; &lt;strong&gt;功能级（Functional）&lt;/strong&gt;。它知道“CPU 写了这个寄存器，灯就会亮”，但它不在乎电流是怎么流过去的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Palladium / ZeBu / HAPS（硬件仿真/原型）：运行的是“RTL 电路”。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原理：&lt;/strong&gt; 这些机器内部塞满了大量的专用芯片（FPGA 或 定制处理器）。我们将芯片设计的&lt;strong&gt;源代码（RTL）&lt;/strong&gt; 综合成电路网表，真刀真枪地烧录或者映射到这些机器里运行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;比喻：&lt;/strong&gt; 这就像是用乐高积木（FPGA/专用芯片）按照设计图纸，1:1 搭建了一个巨大的、虽然跑得慢但结构完全真实的“游戏机”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键词：&lt;/strong&gt; &lt;strong&gt;周期级（Cycle-accurate）&lt;/strong&gt;。它精确地模拟了每一个时钟周期内，信号在电线上的翻转。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;三大维度深度对比&#34;&gt;三大维度深度对比&lt;/h3&gt;
&lt;p&gt;为了方便理解，我将对比分为三个维度：&lt;strong&gt;速度&lt;/strong&gt;、&lt;strong&gt;真实度（精度）&lt;/strong&gt;、&lt;strong&gt;可观测性&lt;/strong&gt;。&lt;/p&gt;
&lt;h4 id=&#34;速度-speed&#34;&gt;速度 (Speed)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QEMU：&lt;/strong&gt; &lt;strong&gt;极快&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;因为它跳过了复杂的电路细节，直接用主机 CPU 指令模拟目标指令，速度可以达到数百 MIPS（每秒百万条指令）。&lt;/li&gt;
&lt;li&gt;用途：适合你们软件团队开发上层应用、操作系统（Linux/Android）启动、UI 交互。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HAPS (FPGA Prototyping)：&lt;/strong&gt; &lt;strong&gt;较快&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;它通常能跑到 5MHz - 100MHz。虽然比真实芯片（几 GHz）慢，但比下面的 Emulation 快。&lt;/li&gt;
&lt;li&gt;用途：适合驱动开发、长时间的压力测试、视频编解码测试。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Palladium / ZeBu (Emulation)：&lt;/strong&gt; &lt;strong&gt;较慢&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;通常在 500kHz - 2MHz 左右。启动一个 Android 可能需要几小时（虽然现在有混合模式加速，但纯硬件部分依然慢）。&lt;/li&gt;
&lt;li&gt;用途：芯片逻辑除错、极早期的驱动验证。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;真实度精度-accuracy--这是最关键的区别&#34;&gt;真实度/精度 (Accuracy) —— 这是最关键的区别&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QEMU：&lt;/strong&gt; &lt;strong&gt;时序是假的&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;在 QEMU 里，你写一个指令 &lt;code&gt;Delay(1ms)&lt;/code&gt;，它可能并不真的精确对应硬件的多少个时钟周期。它往往假设总线交互是瞬间完成的。&lt;/li&gt;
&lt;li&gt;风险：它测不出&lt;strong&gt;竞争冒险（Race Condition）&lt;strong&gt;或者&lt;/strong&gt;带宽瓶颈&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件平台 (Palladium/ZeBu/HAPS)：&lt;/strong&gt; &lt;strong&gt;时序是真的&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;它严格遵守电路设计的时序。如果你的代码需要等待硬件模块响应，而那个模块需要耗费 100 个时钟周期才能把数据准备好，硬件平台就会真的让你等 100 个周期。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;可观测性-visibility--也就是-debug-的难度&#34;&gt;可观测性 (Visibility) —— 也就是 Debug 的难度&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QEMU：&lt;/strong&gt; 你只能看到 CPU 寄存器、内存和你在代码里打印的 Log。你看不到芯片内部的一根“电线”是高电平还是低电平。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Palladium / ZeBu：&lt;/strong&gt; &lt;strong&gt;上帝视角&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;我可以随时“暂停”时间，查看芯片内部几十亿个晶体管中任意一个的状态，甚至可以回放（Waveform dumping）。这对于查硬件 Bug 是救命的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HAPS：&lt;/strong&gt; 比较难查。因为它为了追求速度，牺牲了可观测性。通常需要预埋一些探针才能看波形。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;实战拆解一个简单的-dma-搬运在不同平台的表现&#34;&gt;实战拆解：一个“简单”的 DMA 搬运，在不同平台的表现&lt;/h2&gt;
&lt;p&gt;光讲理论太枯燥，我们用一个经典的 &lt;strong&gt;“CPU 指挥 DMA 搬数据”&lt;/strong&gt; 的例子，来看看 QEMU 是怎么“欺骗”你的，而硬件仿真平台又是如何工作的。&lt;/p&gt;
&lt;p&gt;DMA 搬运一搬可以分为下面几步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CPU 往内存写一段数据。&lt;/li&gt;
&lt;li&gt;CPU 配置 DMA，把这段数据搬到另一个地方。&lt;/li&gt;
&lt;li&gt;DMA 搬完后，发一个中断告诉 CPU：“我干完了”。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;在-qemu-里的美好世界&#34;&gt;在 QEMU 里的美好世界&lt;/h3&gt;
&lt;p&gt;在 QEMU 的代码里，DMA 设备通常是一个 C++ 类。当你写入“开始”寄存器时，QEMU 内部可能就直接调用了一个 &lt;code&gt;memcpy()&lt;/code&gt; 函数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;总线拥堵？&lt;/strong&gt; 不存在的，QEMU 里内存读写瞬间完成。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存同步？&lt;/strong&gt; 没关系的，QEMU 往往默认 CPU 和 DMA 看到的是同一块内存，不需要你操心 Cache 一致性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结果：&lt;/strong&gt; 代码逻辑通顺，中断准时到达，数据完美无缺。你觉得你的驱动无懈可击。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;在硬件仿真平台真实电路里的残酷真相&#34;&gt;在硬件仿真平台（真实电路）里的残酷真相&lt;/h3&gt;
&lt;p&gt;当你把同一段代码放到 Palladium 上（这里跑的是真实的芯片电路设计图），情况完全变了。&lt;/p&gt;
&lt;h4 id=&#34;总线上的堵车事故axi-背压&#34;&gt;总线上的“堵车”事故（AXI 背压）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QEMU:&lt;/strong&gt; 一路畅通。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件真相：&lt;/strong&gt; DMA 发出写请求，但此时内存控制器正忙着处理 GPU 的请求，于是回了一个 &lt;code&gt;Wait&lt;/code&gt; 信号。如果你的 DMA 硬件设计在处理这个 &lt;code&gt;Wait&lt;/code&gt; 信号时状态机写得有问题，它可能就卡死在这里，傻等着永远不会来的“通行证”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后果：&lt;/strong&gt; &lt;strong&gt;死锁&lt;/strong&gt;。软件一直在等中断，但中断永远不会来。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;缓存里的谎言-cache-coherency&#34;&gt;缓存里的“谎言” (Cache Coherency)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QEMU:&lt;/strong&gt; CPU 写完数据，DMA 立刻就能读到最新的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件真相：&lt;/strong&gt; CPU 写的数据还在 CPU 的 L1 Cache 里，还没来得及写到 DDR 内存条上。此时 DMA 去读内存，读到的是旧数据（全是 0）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后果：&lt;/strong&gt; &lt;strong&gt;数据校验错误&lt;/strong&gt;。这是最坑的，因为系统没挂，但数据是错的，且极难复现。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;中断的生死时速-race-condition&#34;&gt;中断的“生死时速” (Race Condition)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QEMU:&lt;/strong&gt; 逻辑上的先后顺序，先发中断，再处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件真相：&lt;/strong&gt; DMA 拉高中断线需要 1 个时钟周期，信号经过总线传到 CPU 需要 10 个周期。就在这几纳秒的时间差里，如果你的驱动程序刚好去操作了清除中断的寄存器，或者总线出现了乱序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后果：&lt;/strong&gt; &lt;strong&gt;丢中断&lt;/strong&gt;。DMA 觉得我发了，CPU 觉得我没收到，两人面面相觑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;既然-qemu-这么多坑为什么还要用&#34;&gt;既然 QEMU 这么多坑，为什么还要用？&lt;/h2&gt;
&lt;p&gt;硬件仿真平台（Palladium/ZeBu）虽然真实，但也有致命缺点：&lt;strong&gt;贵、慢、难伺候。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它启动一次 Linux 可能要几个小时（QEMU 只要几秒）。&lt;/li&gt;
&lt;li&gt;想加个断点调试？流程极其繁琐。&lt;/li&gt;
&lt;li&gt;机器非常昂贵，通常全公司也没几台，得排队用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;最佳实践软硬结合的三步走&#34;&gt;最佳实践：软硬结合的“三步走”&lt;/h2&gt;
&lt;p&gt;作为一名聪明的开发者，你应该清楚每种工具的定位：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;早期开发 (QEMU):&lt;/strong&gt; 这里的目的是&lt;strong&gt;跑通软件逻辑&lt;/strong&gt;。验证任务调度、UI 交互、驱动的基本状态机。只要 QEMU 跑通了，说明你的代码逻辑大体是对的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中期验证 (硬件仿真/FPGA):&lt;/strong&gt; 代码逻辑没问题了，现在要验证&lt;strong&gt;时序和硬件交互&lt;/strong&gt;。把代码放到 Palladium 或 FPGA 上跑。这时候你会遇到上面说的死锁、数据错、超时。别慌，这正是硬件仿真平台的价值所在——帮你在流片前把这些深层 Bug 挖出来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终回流:&lt;/strong&gt; 当你在硬件平台上修复了 Bug（比如加了内存屏障指令），记得把这些改动同步回 QEMU 仓库，虽然 QEMU 不需要它也能跑，但保持代码一致性很重要。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;硬件三巨头&#34;&gt;硬件三巨头&lt;/h3&gt;
&lt;p&gt;同样作为硬件仿真平台，Palladium、ZeBu、HAPS 之间也有一些区别：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Palladium (Cadence 公司) / ZeBu (Synopsys 公司)：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;学名：&lt;/strong&gt; &lt;strong&gt;硬件仿真器 (Emulator)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt; 极贵（一台机器几千万甚至上亿人民币）。编译代码很快，Debug 能力最强。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;你的用法：&lt;/strong&gt; 芯片设计早期（RTL 刚写好），如果你的固件跑挂了，你需要找硬件设计人员看波形，这时候用这个。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HAPS (Synopsys 公司)：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;学名：&lt;/strong&gt; &lt;strong&gt;FPGA 原型验证系统 (FPGA Prototyping)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt; 也就是很多块高性能 FPGA 连在一起。跑得比 Emulator 快很多。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;你的用法：&lt;/strong&gt; 芯片设计中后期。主要用来给你做驱动开发、跑真实的操作系统。因为它跑得够快，你能感觉到系统的流畅度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;附录术语表&#34;&gt;附录：术语表&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RTL (Register Transfer Level)：&lt;/strong&gt; 寄存器传输级代码。这是硬件工程师写的代码（通常用 Verilog 或 VHDL 语言）。它是芯片设计的“源代码”。QEMU 不跑这个，硬件仿真平台跑这个。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流片 (Tape-out)：&lt;/strong&gt; 指芯片设计完成，把设计数据发送给晶圆厂（如台积电）进行制造的过程。流片极其昂贵，一旦失败损失巨大，所以必须在流片前用仿真平台测准。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FPGA (Field-Programmable Gate Array)：&lt;/strong&gt; 现场可编程门阵列。一种“万能芯片”。你可以通过编程改变它内部的电路连接。我们用它来模拟还没造出来的 ASIC 芯片。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时钟周期 (Clock Cycle)：&lt;/strong&gt; 芯片心脏跳动一次的时间。2GHz 的 CPU，一个周期就是 0.5 纳秒。硬件仿真就是精确模拟每一次跳动发生了什么。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网表 (Netlist)：&lt;/strong&gt; 类似于软件编译后的“汇编语言”或二进制。RTL 代码经过“综合（Synthesis）”工具处理后，就变成了由门电路（与门、非门、触发器）组成的网表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时钟域 (Clock Domain)：&lt;/strong&gt; 芯片里不同模块跑的速度不一样（比如 CPU 跑 2G，USB 模块跑 100M）。信号从 2G 传到 100M 的区域，需要特殊的同步处理，这往往是 Bug 的高发区。&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h3 id="核心区别翻译vs模拟">核心区别——“翻译”vs“模拟”</h3>
<p>最本质的区别在于它们运行的<strong>对象</strong>不同：</p>
<ol>
<li>
<p><strong>QEMU（软件仿真）：运行的是“行为模型”。</strong></p>
<ul>
<li><strong>原理：</strong> QEMU 是一个纯软件程序，运行在通用的服务器（x86 架构）上。它不关心芯片内部具体的电路是怎么连的，它只关心<strong>功能</strong>。</li>
<li><strong>比喻：</strong> 就像你在电脑上玩“超级马里奥”模拟器。电脑并不含有任天堂的游戏机电路，它只是用软件模拟了“按 A 键跳跃”这个<strong>行为</strong>。</li>
<li><strong>关键词：</strong> <strong>功能级（Functional）</strong>。它知道“CPU 写了这个寄存器，灯就会亮”，但它不在乎电流是怎么流过去的。</li>
</ul>
</li>
<li>
<p><strong>Palladium / ZeBu / HAPS（硬件仿真/原型）：运行的是“RTL 电路”。</strong></p>
<ul>
<li><strong>原理：</strong> 这些机器内部塞满了大量的专用芯片（FPGA 或 定制处理器）。我们将芯片设计的<strong>源代码（RTL）</strong> 综合成电路网表，真刀真枪地烧录或者映射到这些机器里运行。</li>
<li><strong>比喻：</strong> 这就像是用乐高积木（FPGA/专用芯片）按照设计图纸，1:1 搭建了一个巨大的、虽然跑得慢但结构完全真实的“游戏机”。</li>
<li><strong>关键词：</strong> <strong>周期级（Cycle-accurate）</strong>。它精确地模拟了每一个时钟周期内，信号在电线上的翻转。</li>
</ul>
</li>
</ol>
<h3 id="三大维度深度对比">三大维度深度对比</h3>
<p>为了方便理解，我将对比分为三个维度：<strong>速度</strong>、<strong>真实度（精度）</strong>、<strong>可观测性</strong>。</p>
<h4 id="速度-speed">速度 (Speed)</h4>
<ul>
<li><strong>QEMU：</strong> <strong>极快</strong>。
<ul>
<li>因为它跳过了复杂的电路细节，直接用主机 CPU 指令模拟目标指令，速度可以达到数百 MIPS（每秒百万条指令）。</li>
<li>用途：适合你们软件团队开发上层应用、操作系统（Linux/Android）启动、UI 交互。</li>
</ul>
</li>
<li><strong>HAPS (FPGA Prototyping)：</strong> <strong>较快</strong>。
<ul>
<li>它通常能跑到 5MHz - 100MHz。虽然比真实芯片（几 GHz）慢，但比下面的 Emulation 快。</li>
<li>用途：适合驱动开发、长时间的压力测试、视频编解码测试。</li>
</ul>
</li>
<li><strong>Palladium / ZeBu (Emulation)：</strong> <strong>较慢</strong>。
<ul>
<li>通常在 500kHz - 2MHz 左右。启动一个 Android 可能需要几小时（虽然现在有混合模式加速，但纯硬件部分依然慢）。</li>
<li>用途：芯片逻辑除错、极早期的驱动验证。</li>
</ul>
</li>
</ul>
<h4 id="真实度精度-accuracy--这是最关键的区别">真实度/精度 (Accuracy) —— 这是最关键的区别</h4>
<ul>
<li><strong>QEMU：</strong> <strong>时序是假的</strong>。
<ul>
<li>在 QEMU 里，你写一个指令 <code>Delay(1ms)</code>，它可能并不真的精确对应硬件的多少个时钟周期。它往往假设总线交互是瞬间完成的。</li>
<li>风险：它测不出<strong>竞争冒险（Race Condition）<strong>或者</strong>带宽瓶颈</strong>。</li>
</ul>
</li>
<li><strong>硬件平台 (Palladium/ZeBu/HAPS)：</strong> <strong>时序是真的</strong>。
<ul>
<li>它严格遵守电路设计的时序。如果你的代码需要等待硬件模块响应，而那个模块需要耗费 100 个时钟周期才能把数据准备好，硬件平台就会真的让你等 100 个周期。</li>
</ul>
</li>
</ul>
<h4 id="可观测性-visibility--也就是-debug-的难度">可观测性 (Visibility) —— 也就是 Debug 的难度</h4>
<ul>
<li><strong>QEMU：</strong> 你只能看到 CPU 寄存器、内存和你在代码里打印的 Log。你看不到芯片内部的一根“电线”是高电平还是低电平。</li>
<li><strong>Palladium / ZeBu：</strong> <strong>上帝视角</strong>。
<ul>
<li>我可以随时“暂停”时间，查看芯片内部几十亿个晶体管中任意一个的状态，甚至可以回放（Waveform dumping）。这对于查硬件 Bug 是救命的。</li>
</ul>
</li>
<li><strong>HAPS：</strong> 比较难查。因为它为了追求速度，牺牲了可观测性。通常需要预埋一些探针才能看波形。</li>
</ul>
<h2 id="实战拆解一个简单的-dma-搬运在不同平台的表现">实战拆解：一个“简单”的 DMA 搬运，在不同平台的表现</h2>
<p>光讲理论太枯燥，我们用一个经典的 <strong>“CPU 指挥 DMA 搬数据”</strong> 的例子，来看看 QEMU 是怎么“欺骗”你的，而硬件仿真平台又是如何工作的。</p>
<p>DMA 搬运一搬可以分为下面几步：</p>
<ol>
<li>CPU 往内存写一段数据。</li>
<li>CPU 配置 DMA，把这段数据搬到另一个地方。</li>
<li>DMA 搬完后，发一个中断告诉 CPU：“我干完了”。</li>
</ol>
<h3 id="在-qemu-里的美好世界">在 QEMU 里的美好世界</h3>
<p>在 QEMU 的代码里，DMA 设备通常是一个 C++ 类。当你写入“开始”寄存器时，QEMU 内部可能就直接调用了一个 <code>memcpy()</code> 函数。</p>
<ul>
<li><strong>总线拥堵？</strong> 不存在的，QEMU 里内存读写瞬间完成。</li>
<li><strong>缓存同步？</strong> 没关系的，QEMU 往往默认 CPU 和 DMA 看到的是同一块内存，不需要你操心 Cache 一致性。</li>
<li><strong>结果：</strong> 代码逻辑通顺，中断准时到达，数据完美无缺。你觉得你的驱动无懈可击。</li>
</ul>
<h3 id="在硬件仿真平台真实电路里的残酷真相">在硬件仿真平台（真实电路）里的残酷真相</h3>
<p>当你把同一段代码放到 Palladium 上（这里跑的是真实的芯片电路设计图），情况完全变了。</p>
<h4 id="总线上的堵车事故axi-背压">总线上的“堵车”事故（AXI 背压）</h4>
<ul>
<li><strong>QEMU:</strong> 一路畅通。</li>
<li><strong>硬件真相：</strong> DMA 发出写请求，但此时内存控制器正忙着处理 GPU 的请求，于是回了一个 <code>Wait</code> 信号。如果你的 DMA 硬件设计在处理这个 <code>Wait</code> 信号时状态机写得有问题，它可能就卡死在这里，傻等着永远不会来的“通行证”。</li>
<li><strong>后果：</strong> <strong>死锁</strong>。软件一直在等中断，但中断永远不会来。</li>
</ul>
<h4 id="缓存里的谎言-cache-coherency">缓存里的“谎言” (Cache Coherency)</h4>
<ul>
<li><strong>QEMU:</strong> CPU 写完数据，DMA 立刻就能读到最新的。</li>
<li><strong>硬件真相：</strong> CPU 写的数据还在 CPU 的 L1 Cache 里，还没来得及写到 DDR 内存条上。此时 DMA 去读内存，读到的是旧数据（全是 0）。</li>
<li><strong>后果：</strong> <strong>数据校验错误</strong>。这是最坑的，因为系统没挂，但数据是错的，且极难复现。</li>
</ul>
<h4 id="中断的生死时速-race-condition">中断的“生死时速” (Race Condition)</h4>
<ul>
<li><strong>QEMU:</strong> 逻辑上的先后顺序，先发中断，再处理。</li>
<li><strong>硬件真相：</strong> DMA 拉高中断线需要 1 个时钟周期，信号经过总线传到 CPU 需要 10 个周期。就在这几纳秒的时间差里，如果你的驱动程序刚好去操作了清除中断的寄存器，或者总线出现了乱序。</li>
<li><strong>后果：</strong> <strong>丢中断</strong>。DMA 觉得我发了，CPU 觉得我没收到，两人面面相觑。</li>
</ul>
<h2 id="既然-qemu-这么多坑为什么还要用">既然 QEMU 这么多坑，为什么还要用？</h2>
<p>硬件仿真平台（Palladium/ZeBu）虽然真实，但也有致命缺点：<strong>贵、慢、难伺候。</strong></p>
<ul>
<li>它启动一次 Linux 可能要几个小时（QEMU 只要几秒）。</li>
<li>想加个断点调试？流程极其繁琐。</li>
<li>机器非常昂贵，通常全公司也没几台，得排队用。</li>
</ul>
<h2 id="最佳实践软硬结合的三步走">最佳实践：软硬结合的“三步走”</h2>
<p>作为一名聪明的开发者，你应该清楚每种工具的定位：</p>
<ol>
<li><strong>早期开发 (QEMU):</strong> 这里的目的是<strong>跑通软件逻辑</strong>。验证任务调度、UI 交互、驱动的基本状态机。只要 QEMU 跑通了，说明你的代码逻辑大体是对的。</li>
<li><strong>中期验证 (硬件仿真/FPGA):</strong> 代码逻辑没问题了，现在要验证<strong>时序和硬件交互</strong>。把代码放到 Palladium 或 FPGA 上跑。这时候你会遇到上面说的死锁、数据错、超时。别慌，这正是硬件仿真平台的价值所在——帮你在流片前把这些深层 Bug 挖出来。</li>
<li><strong>最终回流:</strong> 当你在硬件平台上修复了 Bug（比如加了内存屏障指令），记得把这些改动同步回 QEMU 仓库，虽然 QEMU 不需要它也能跑，但保持代码一致性很重要。</li>
</ol>
<h3 id="硬件三巨头">硬件三巨头</h3>
<p>同样作为硬件仿真平台，Palladium、ZeBu、HAPS 之间也有一些区别：</p>
<ol>
<li><strong>Palladium (Cadence 公司) / ZeBu (Synopsys 公司)：</strong>
<ul>
<li><strong>学名：</strong> <strong>硬件仿真器 (Emulator)</strong>。</li>
<li><strong>特点：</strong> 极贵（一台机器几千万甚至上亿人民币）。编译代码很快，Debug 能力最强。</li>
<li><strong>你的用法：</strong> 芯片设计早期（RTL 刚写好），如果你的固件跑挂了，你需要找硬件设计人员看波形，这时候用这个。</li>
</ul>
</li>
<li><strong>HAPS (Synopsys 公司)：</strong>
<ul>
<li><strong>学名：</strong> <strong>FPGA 原型验证系统 (FPGA Prototyping)</strong>。</li>
<li><strong>特点：</strong> 也就是很多块高性能 FPGA 连在一起。跑得比 Emulator 快很多。</li>
<li><strong>你的用法：</strong> 芯片设计中后期。主要用来给你做驱动开发、跑真实的操作系统。因为它跑得够快，你能感觉到系统的流畅度。</li>
</ul>
</li>
</ol>
<h3 id="附录术语表">附录：术语表</h3>
<ul>
<li><strong>RTL (Register Transfer Level)：</strong> 寄存器传输级代码。这是硬件工程师写的代码（通常用 Verilog 或 VHDL 语言）。它是芯片设计的“源代码”。QEMU 不跑这个，硬件仿真平台跑这个。</li>
<li><strong>流片 (Tape-out)：</strong> 指芯片设计完成，把设计数据发送给晶圆厂（如台积电）进行制造的过程。流片极其昂贵，一旦失败损失巨大，所以必须在流片前用仿真平台测准。</li>
<li><strong>FPGA (Field-Programmable Gate Array)：</strong> 现场可编程门阵列。一种“万能芯片”。你可以通过编程改变它内部的电路连接。我们用它来模拟还没造出来的 ASIC 芯片。</li>
<li><strong>时钟周期 (Clock Cycle)：</strong> 芯片心脏跳动一次的时间。2GHz 的 CPU，一个周期就是 0.5 纳秒。硬件仿真就是精确模拟每一次跳动发生了什么。</li>
<li><strong>网表 (Netlist)：</strong> 类似于软件编译后的“汇编语言”或二进制。RTL 代码经过“综合（Synthesis）”工具处理后，就变成了由门电路（与门、非门、触发器）组成的网表。</li>
<li><strong>时钟域 (Clock Domain)：</strong> 芯片里不同模块跑的速度不一样（比如 CPU 跑 2G，USB 模块跑 100M）。信号从 2G 传到 100M 的区域，需要特殊的同步处理，这往往是 Bug 的高发区。</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>半年动态血糖仪骑行实验</title>
      <link>https://lifeislife.cn/posts/%E5%8D%8A%E5%B9%B4%E5%8A%A8%E6%80%81%E8%A1%80%E7%B3%96%E4%BB%AA%E9%AA%91%E8%A1%8C%E5%AE%9E%E9%AA%8C/</link>
      <pubDate>Mon, 19 Jan 2026 10:34:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%8D%8A%E5%B9%B4%E5%8A%A8%E6%80%81%E8%A1%80%E7%B3%96%E4%BB%AA%E9%AA%91%E8%A1%8C%E5%AE%9E%E9%AA%8C/</guid>
      <description>&lt;h1 id=&#34;半年动态血糖仪骑行实验为什么我吃胶升不了糖了&#34;&gt;半年动态血糖仪骑行实验：为什么我吃胶“升不了糖”了？&lt;/h1&gt;
&lt;p&gt;我一直挺喜欢骑车。骑久了就绕不开一个现实问题：&lt;strong&gt;什么时候该补？补多少？补完有没有用？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;过去我都是凭感觉——饿了、腿软了、想吃甜的了就来一口。但越骑越久，我开始好奇：所谓“补能量”，到底是在干嘛？说白了很多时候就是在&lt;strong&gt;把血糖拉起来&lt;/strong&gt;。那问题来了：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我在骑行中，究竟什么时候真的需要“抬血糖”？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;半年前，我干了件有点“自虐式认真”的事：给自己买了一个动态血糖仪（CGM），准备把“凭感觉补给”变成“看数据补给”。&lt;/p&gt;
&lt;h2 id=&#34;最初的计划用血糖曲线给能量胶打分&#34;&gt;最初的计划：用血糖曲线给能量胶打分&lt;/h2&gt;
&lt;p&gt;CGM 最大的魅力是：你能直观看到“吃进去的碳水”对血糖造成的变化。于是我很自然地把好奇心转向了能量胶：&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;strong&gt;WIN、SIS、康比特&lt;/strong&gt;。结果很“教科书”：&lt;br&gt;
基本都是&lt;strong&gt;摄入后约 10 分钟开始上升&lt;/strong&gt;，随后出现明显抬升。&lt;/p&gt;
&lt;p&gt;为了让测试不至于变成“看个热闹”，我还给自己定了一个很朴素的评价标准：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;升糖幅度&lt;/strong&gt;（能不能把血糖拉起来）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;维持时间&lt;/strong&gt;（能不能撑住一段输出）&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//2026/01/20/143f99d255065710047372ede1eeda01.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/143f99d255065710047372ede1eeda01.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;p&gt;然后我被琐事打断了。测试停了，CGM 也就没再折腾。&lt;/p&gt;
&lt;h2 id=&#34;半年后重启我以为是继续更新数据结果直接翻车&#34;&gt;半年后重启：我以为是继续更新数据，结果直接翻车&lt;/h2&gt;
&lt;p&gt;最近我又想起这件事：市面上还有很多胶没测，我决定把这个坑补上。&lt;/p&gt;
&lt;p&gt;于是周六早上，我按之前的方式测了一个新品牌：&lt;strong&gt;Gio&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;结果让我当场愣住：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;几乎没有升糖反应&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;我等了将近 &lt;strong&gt;40 分钟&lt;/strong&gt; 才看到一点点波动&lt;/li&gt;
&lt;li&gt;血糖大概从 &lt;strong&gt;4.7 → 5.5 mmol/L&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;按我半年前的标准：&lt;strong&gt;不达标&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但这事儿离谱就在于：能量胶这么甜，怎么可能“没效果”？&lt;br&gt;
我不信邪，周六下午又测了一次——&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//2026/01/20/563fe0871bacda69ab9806df3ff6e58d.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/563fe0871bacda69ab9806df3ff6e58d.jpg&#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//2026/01/20/d57574085f85728f13e053d0e70fae59.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/d57574085f85728f13e053d0e70fae59.jpg&#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;h2 id=&#34;复测老朋友-win我状态很好但血糖依旧毫无波澜&#34;&gt;复测老朋友 WIN：我状态很好，但血糖依旧毫无波澜&lt;/h2&gt;
&lt;p&gt;周日早上醒来，我状态很好，精神也清爽。我决定做一个最直接的排查：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;用半年前测过、当时表现“很正常”的 WIN 再测一次。&lt;br&gt;
看看是我身体变了，还是胶变了，还是方法变了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;结果：&lt;strong&gt;WIN 也不升了。&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//2026/01/20/1e6a5d9b075a73a436b0730e445767db.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/1e6a5d9b075a73a436b0730e445767db.jpg&#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//2026/01/20/aca38638a8923eca00cfc5546dfdbf8d.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/aca38638a8923eca00cfc5546dfdbf8d.jpg&#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;p&gt;后面明显血糖提升是我已经停止骑行了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那种感觉很微妙：&lt;br&gt;
不是“数据不好看”的失落，而是“我一直以为这个方法靠谱，现在它好像失效了”的错愕。&lt;/p&gt;
&lt;p&gt;我当时的心理活动大概是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 Gio 不升糖，可能是胶的问题&lt;/li&gt;
&lt;li&gt;但 WIN 也不升，那就很难怪胶&lt;/li&gt;
&lt;li&gt;我今天状态又很好，不像身体出毛病&lt;/li&gt;
&lt;li&gt;那到底发生了什么？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;把曲线丢给-chatgpt一个我之前没认真想过的可能&#34;&gt;把曲线丢给 ChatGPT：一个我之前没认真想过的可能&lt;/h2&gt;
&lt;p&gt;我把半年前和最近的曲线发给 ChatGPT 让它帮我分析。它给了一个核心判断，听起来反直觉但很合理：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;不是你吸收变差了，而是你“用得更快了”。&lt;/strong&gt;&lt;br&gt;
糖进来了，但还没来得及在血液里堆高，就被肌肉直接拿去用掉了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;它解释的方向主要是这些（我用自己的话压缩一下）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;训练适应后，肌肉即时摄糖能力变强&lt;/strong&gt;：同样一包胶，过去会“血糖冲高”，现在可能直接被消耗掉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运动状态下肌肉收缩也能吸糖&lt;/strong&gt;（不完全靠胰岛素那条路），骑得越规律这条路越“顺手”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CGM 测到的是“血液/组织液里剩多少”&lt;/strong&gt;，不等于“你吃进去了多少”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话总结就是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;半年前是“摄入 &amp;gt; 消耗”，所以曲线抬头明显；&lt;br&gt;
现在更像“摄入 ≈ 消耗”，所以曲线看起来风平浪静。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当时我看到这套解释，第一反应不是“懂了”，而是“那我怎么确认不是瞎猜？”&lt;/p&gt;
&lt;h2 id=&#34;我做了一个关键对照静坐吃胶血糖立刻起飞&#34;&gt;我做了一个关键对照：静坐吃胶，血糖立刻起飞&lt;/h2&gt;
&lt;p&gt;周一上班，我干脆做了个更粗暴但有效的对照实验：&lt;br&gt;
&lt;strong&gt;不骑车、不运动、静坐状态下吃一根 Gio。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;结果非常清晰：&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//2026/01/20/32187d7b86e8debc5134516798cfce3f.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/32187d7b86e8debc5134516798cfce3f.jpg&#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;CGM 也没坏&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那剩下的解释就更集中：&lt;strong&gt;运动状态改变了“血糖呈现方式”&lt;/strong&gt;。不是没吃进去，而是“进来就被用掉”，所以你在曲线上看不到当年那种抬升。&lt;/p&gt;
&lt;h2 id=&#34;我真正的收获我那套测评标准其实脆弱得很&#34;&gt;我真正的收获：我那套“测评标准”，其实脆弱得很&lt;/h2&gt;
&lt;p&gt;这次翻车让我重新认识了一件事：&lt;strong&gt;人体的变量多到离谱&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我半年前以为自己设计了一个挺科学的测评框架：看升糖幅度、看维持时间，然后给能量胶打分。现在回头看，这个框架至少有一个致命前提：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你的身体状态得相对稳定，你得能控制足够多的变量。&lt;/p&gt;
&lt;/blockquote&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;strong&gt;太难控制变量&lt;/strong&gt;，结论很容易被误读。&lt;/p&gt;
&lt;h2 id=&#34;接下来我准备怎么继续这个坑我还想挖&#34;&gt;接下来我准备怎么继续（这个坑我还想挖）&lt;/h2&gt;
&lt;p&gt;这次之后，我不太想把“能量胶评测”继续做成那种“谁升糖猛谁赢”的榜单了。更有意思的方向可能是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;运动 vs 静息&lt;/strong&gt; 同一支胶的差异（像这次一样）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不同强度区间（Z2、爬坡、间歇）&lt;/strong&gt; 同样摄入的曲线变化&lt;/li&gt;
&lt;li&gt;把“血糖是否抬头”从结论，降级为一个观察维度&lt;/li&gt;
&lt;li&gt;更关注：&lt;strong&gt;骑行主观感受、功率输出、胃部舒适度&lt;/strong&gt;与血糖的对应关系&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我现在越来越觉得：CGM 更像一面镜子，它照出来的不只是“胶怎么样”，更多是“我现在的身体处在什么状态”。&lt;/p&gt;
&lt;h2 id=&#34;结尾我以为我在测胶结果是在测自己&#34;&gt;结尾：我以为我在测胶，结果是在测自己&lt;/h2&gt;
&lt;p&gt;这次最让我佩服的不是某个品牌的胶，也不是某条漂亮曲线，而是人体这套系统的精密程度：&lt;/p&gt;
&lt;p&gt;我以为“吃胶＝升血糖”，结果运动状态下它可能变成“吃胶＝直接被肌肉拿走用掉”。&lt;br&gt;
我以为我的方法很科学，结果它只是在一个特定阶段刚好有效。&lt;/p&gt;
&lt;p&gt;这大概就是我喜欢做这种长期小实验的原因：&lt;br&gt;
骑行没有标准答案，但数据会逼你承认——你还远远没弄懂自己。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="半年动态血糖仪骑行实验为什么我吃胶升不了糖了">半年动态血糖仪骑行实验：为什么我吃胶“升不了糖”了？</h1>
<p>我一直挺喜欢骑车。骑久了就绕不开一个现实问题：<strong>什么时候该补？补多少？补完有没有用？</strong></p>
<p>过去我都是凭感觉——饿了、腿软了、想吃甜的了就来一口。但越骑越久，我开始好奇：所谓“补能量”，到底是在干嘛？说白了很多时候就是在<strong>把血糖拉起来</strong>。那问题来了：</p>
<blockquote>
<p>我在骑行中，究竟什么时候真的需要“抬血糖”？</p>
</blockquote>
<p>半年前，我干了件有点“自虐式认真”的事：给自己买了一个动态血糖仪（CGM），准备把“凭感觉补给”变成“看数据补给”。</p>
<h2 id="最初的计划用血糖曲线给能量胶打分">最初的计划：用血糖曲线给能量胶打分</h2>
<p>CGM 最大的魅力是：你能直观看到“吃进去的碳水”对血糖造成的变化。于是我很自然地把好奇心转向了能量胶：</p>
<ul>
<li>不同品牌的胶，升糖速度一样吗？</li>
<li>峰值谁更高？</li>
<li>谁能撑得更久？</li>
</ul>
<p>我当时测了三个品牌：<strong>WIN、SIS、康比特</strong>。结果很“教科书”：<br>
基本都是<strong>摄入后约 10 分钟开始上升</strong>，随后出现明显抬升。</p>
<p>为了让测试不至于变成“看个热闹”，我还给自己定了一个很朴素的评价标准：</p>
<ul>
<li><strong>升糖幅度</strong>（能不能把血糖拉起来）</li>
<li><strong>维持时间</strong>（能不能撑住一段输出）</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//2026/01/20/143f99d255065710047372ede1eeda01.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/143f99d255065710047372ede1eeda01.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>
<p>然后我被琐事打断了。测试停了，CGM 也就没再折腾。</p>
<h2 id="半年后重启我以为是继续更新数据结果直接翻车">半年后重启：我以为是继续更新数据，结果直接翻车</h2>
<p>最近我又想起这件事：市面上还有很多胶没测，我决定把这个坑补上。</p>
<p>于是周六早上，我按之前的方式测了一个新品牌：<strong>Gio</strong>。</p>
<p>结果让我当场愣住：</p>
<ul>
<li><strong>几乎没有升糖反应</strong></li>
<li>我等了将近 <strong>40 分钟</strong> 才看到一点点波动</li>
<li>血糖大概从 <strong>4.7 → 5.5 mmol/L</strong></li>
<li>按我半年前的标准：<strong>不达标</strong></li>
</ul>
<p>但这事儿离谱就在于：能量胶这么甜，怎么可能“没效果”？<br>
我不信邪，周六下午又测了一次——<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//2026/01/20/563fe0871bacda69ab9806df3ff6e58d.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/563fe0871bacda69ab9806df3ff6e58d.jpg" 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//2026/01/20/d57574085f85728f13e053d0e70fae59.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/d57574085f85728f13e053d0e70fae59.jpg" 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>
<h2 id="复测老朋友-win我状态很好但血糖依旧毫无波澜">复测老朋友 WIN：我状态很好，但血糖依旧毫无波澜</h2>
<p>周日早上醒来，我状态很好，精神也清爽。我决定做一个最直接的排查：</p>
<blockquote>
<p>用半年前测过、当时表现“很正常”的 WIN 再测一次。<br>
看看是我身体变了，还是胶变了，还是方法变了。</p>
</blockquote>
<p>结果：<strong>WIN 也不升了。</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//2026/01/20/1e6a5d9b075a73a436b0730e445767db.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/1e6a5d9b075a73a436b0730e445767db.jpg" 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//2026/01/20/aca38638a8923eca00cfc5546dfdbf8d.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/aca38638a8923eca00cfc5546dfdbf8d.jpg" 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>
<p>后面明显血糖提升是我已经停止骑行了</p>
</blockquote>
<p>那种感觉很微妙：<br>
不是“数据不好看”的失落，而是“我一直以为这个方法靠谱，现在它好像失效了”的错愕。</p>
<p>我当时的心理活动大概是：</p>
<ul>
<li>如果 Gio 不升糖，可能是胶的问题</li>
<li>但 WIN 也不升，那就很难怪胶</li>
<li>我今天状态又很好，不像身体出毛病</li>
<li>那到底发生了什么？</li>
</ul>
<h2 id="把曲线丢给-chatgpt一个我之前没认真想过的可能">把曲线丢给 ChatGPT：一个我之前没认真想过的可能</h2>
<p>我把半年前和最近的曲线发给 ChatGPT 让它帮我分析。它给了一个核心判断，听起来反直觉但很合理：</p>
<blockquote>
<p><strong>不是你吸收变差了，而是你“用得更快了”。</strong><br>
糖进来了，但还没来得及在血液里堆高，就被肌肉直接拿去用掉了。</p>
</blockquote>
<p>它解释的方向主要是这些（我用自己的话压缩一下）：</p>
<ul>
<li><strong>训练适应后，肌肉即时摄糖能力变强</strong>：同样一包胶，过去会“血糖冲高”，现在可能直接被消耗掉</li>
<li><strong>运动状态下肌肉收缩也能吸糖</strong>（不完全靠胰岛素那条路），骑得越规律这条路越“顺手”</li>
<li><strong>CGM 测到的是“血液/组织液里剩多少”</strong>，不等于“你吃进去了多少”</li>
</ul>
<p>一句话总结就是：</p>
<blockquote>
<p>半年前是“摄入 &gt; 消耗”，所以曲线抬头明显；<br>
现在更像“摄入 ≈ 消耗”，所以曲线看起来风平浪静。</p>
</blockquote>
<p>当时我看到这套解释，第一反应不是“懂了”，而是“那我怎么确认不是瞎猜？”</p>
<h2 id="我做了一个关键对照静坐吃胶血糖立刻起飞">我做了一个关键对照：静坐吃胶，血糖立刻起飞</h2>
<p>周一上班，我干脆做了个更粗暴但有效的对照实验：<br>
<strong>不骑车、不运动、静坐状态下吃一根 Gio。</strong></p>
<p>结果非常清晰：<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//2026/01/20/32187d7b86e8debc5134516798cfce3f.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2026/01/20/32187d7b86e8debc5134516798cfce3f.jpg" 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>CGM 也没坏</li>
</ul>
<p>那剩下的解释就更集中：<strong>运动状态改变了“血糖呈现方式”</strong>。不是没吃进去，而是“进来就被用掉”，所以你在曲线上看不到当年那种抬升。</p>
<h2 id="我真正的收获我那套测评标准其实脆弱得很">我真正的收获：我那套“测评标准”，其实脆弱得很</h2>
<p>这次翻车让我重新认识了一件事：<strong>人体的变量多到离谱</strong>。</p>
<p>我半年前以为自己设计了一个挺科学的测评框架：看升糖幅度、看维持时间，然后给能量胶打分。现在回头看，这个框架至少有一个致命前提：</p>
<blockquote>
<p>你的身体状态得相对稳定，你得能控制足够多的变量。</p>
</blockquote>
<p>但现实是：</p>
<ul>
<li>同一个人，不同训练周期、不同疲劳程度、不同睡眠、不同压力</li>
<li>同一种胶，可能会呈现完全不同的血糖曲线</li>
<li>甚至同一个周末，上午和下午都能不一样</li>
</ul>
<p>所以也就不难理解：为什么大厂很少做这种“用血糖曲线评测能量胶效果”的公开对比。不是他们不想，是因为这事儿<strong>太难控制变量</strong>，结论很容易被误读。</p>
<h2 id="接下来我准备怎么继续这个坑我还想挖">接下来我准备怎么继续（这个坑我还想挖）</h2>
<p>这次之后，我不太想把“能量胶评测”继续做成那种“谁升糖猛谁赢”的榜单了。更有意思的方向可能是：</p>
<ul>
<li><strong>运动 vs 静息</strong> 同一支胶的差异（像这次一样）</li>
<li><strong>不同强度区间（Z2、爬坡、间歇）</strong> 同样摄入的曲线变化</li>
<li>把“血糖是否抬头”从结论，降级为一个观察维度</li>
<li>更关注：<strong>骑行主观感受、功率输出、胃部舒适度</strong>与血糖的对应关系</li>
</ul>
<p>我现在越来越觉得：CGM 更像一面镜子，它照出来的不只是“胶怎么样”，更多是“我现在的身体处在什么状态”。</p>
<h2 id="结尾我以为我在测胶结果是在测自己">结尾：我以为我在测胶，结果是在测自己</h2>
<p>这次最让我佩服的不是某个品牌的胶，也不是某条漂亮曲线，而是人体这套系统的精密程度：</p>
<p>我以为“吃胶＝升血糖”，结果运动状态下它可能变成“吃胶＝直接被肌肉拿走用掉”。<br>
我以为我的方法很科学，结果它只是在一个特定阶段刚好有效。</p>
<p>这大概就是我喜欢做这种长期小实验的原因：<br>
骑行没有标准答案，但数据会逼你承认——你还远远没弄懂自己。</p>
]]></content:encoded>
    </item>
    <item>
      <title>微信小程序开发完整流程指南</title>
      <link>https://lifeislife.cn/posts/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91%E5%AE%8C%E6%95%B4%E6%B5%81%E7%A8%8B%E6%8C%87%E5%8D%97/</link>
      <pubDate>Sat, 22 Nov 2025 00:00:00 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91%E5%AE%8C%E6%95%B4%E6%B5%81%E7%A8%8B%E6%8C%87%E5%8D%97/</guid>
      <description>&lt;p&gt;微信小程序开发不仅仅是写代码，还涉及服务器购买、域名备案、小程序备案、认证等一系列流程性工作。本文记录了从零开始开发微信小程序的完整流程，包括踩坑记录和注意事项，希望能帮助其他开发者少走弯路。&lt;/p&gt;
&lt;h2 id=&#34;服务器购买&#34;&gt;服务器购买&lt;/h2&gt;
&lt;h3 id=&#34;选择云服务商&#34;&gt;选择云服务商&lt;/h3&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;li&gt;百度云&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;服务器配置建议&#34;&gt;服务器配置建议&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;小型个人小程序推荐配置：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU：1 核或 2 核&lt;/li&gt;
&lt;li&gt;内存：2GB&lt;/li&gt;
&lt;li&gt;带宽：1-3Mbps&lt;/li&gt;
&lt;li&gt;系统盘：40GB&lt;/li&gt;
&lt;li&gt;操作系统：CentOS 7.x 或 Ubuntu 18.04+&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;费用参考：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;新用户通常有优惠，年费在 100-500 元不等&lt;/li&gt;
&lt;li&gt;建议先购买 1 年，测试稳定后再续费&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;购买注意事项&#34;&gt;购买注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;选择离目标用户较近的地域节点，可以降低延迟&lt;/li&gt;
&lt;li&gt;新用户可关注各大云平台的首购优惠&lt;/li&gt;
&lt;li&gt;学生用户可申请学生优惠&lt;/li&gt;
&lt;li&gt;注意查看带宽费用，避免后期流量费用过高&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;域名购买&#34;&gt;域名购买&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://wanwang.aliyun.com/newdomain/1yuan?spm=5176.8048432.J_1403848460.2.714769a3RIQTOp&amp;amp;keyword=06031612&#34;&gt;购买域名 1 元起_.xin 首年 0 元_域名优惠活动 - 阿里云权益中心&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;选择合适的域名&#34;&gt;选择合适的域名&lt;/h3&gt;
&lt;p&gt;小程序用户看不到域名，所以选择便宜的即可。用生日组合纯数字.xyz 域名最便宜。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;选购技巧：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择一个最便宜的域名时，注册时选择 2 年看看后期续费费用&lt;/li&gt;
&lt;li&gt;如果一年 18 元，两年 120 元，说明第二年费用恢复原价，太贵了不要买&lt;/li&gt;
&lt;li&gt;推荐选择续费价格稳定的域名后缀&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;常见便宜域名后缀：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.xyz&lt;/code&gt;：首年最便宜，但续费可能较贵&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.top&lt;/code&gt;：价格适中&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.site&lt;/code&gt;：价格适中&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.club&lt;/code&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//2025/11/18/f7cd9f9011c7b32a640c612df85d5df3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/18/f7cd9f9011c7b32a640c612df85d5df3.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//2025/11/18/389f251903c6ac923c999c86bc14c58a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/18/389f251903c6ac923c999c86bc14c58a.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;下单时创建一个新的信息模板，避免之前创建过的填的信息不完善。购买之后会直接提交到域名注册局审核，信息不完善容易审核不通过。&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//2025/11/18/11d11bf95e226b7e0e354b75d2175938.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/18/11d11bf95e226b7e0e354b75d2175938.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;ul&gt;
&lt;li&gt;姓名必须与身份证一致&lt;/li&gt;
&lt;li&gt;地址填写详细，精确到门牌号&lt;/li&gt;
&lt;li&gt;电话号码保持畅通，可能会接到审核电话&lt;/li&gt;
&lt;li&gt;邮箱填写常用邮箱&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;域名实名认证&#34;&gt;域名实名认证&lt;/h3&gt;
&lt;p&gt;等待几小时（一般在 6 小时内），域名注册局实名认证成功，即可进行下一步备案。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实名认证材料：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;个人：身份证正反面照片&lt;/li&gt;
&lt;li&gt;企业：营业执照、法人身份证&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;域名解析&#34;&gt;域名解析&lt;/h3&gt;
&lt;p&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//2025/12/04/983aa81ac07af6a88b5ea59a69193528.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/983aa81ac07af6a88b5ea59a69193528.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;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//2025/12/04/e356bc511e1442a7d5be9b465121c41e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/e356bc511e1442a7d5be9b465121c41e.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;添加解析记录，第四步的IP地址就是你购买服务器后，服务器提供商给你的公网IP地址：&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//2025/12/04/2e15af90548937c053cb193a207591fc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/2e15af90548937c053cb193a207591fc.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;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//2025/12/04/a5c4ec7ac6ba8cdf4c59854d4986e0e8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/a5c4ec7ac6ba8cdf4c59854d4986e0e8.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;h2 id=&#34;ssl证书购买与部署&#34;&gt;SSL证书购买与部署&lt;/h2&gt;
&lt;p&gt;微信小程序必须使用Https协议，所以需要购买SSL证书。阿里云有免费的证书可以测试使用，但是有效期是3个月，需要定期续费。&lt;/p&gt;
&lt;p&gt;从以下路径找到免费证书申请入口，你也可以在阿里云顶部搜索框搜索SSL，也能找到。&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//2025/12/04/7c9ec5e3e3b37d5a2c871ba83ed15ae5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/7c9ec5e3e3b37d5a2c871ba83ed15ae5.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//2025/12/04/91edd3abcc92baad15388995f85d0615.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/91edd3abcc92baad15388995f85d0615.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//2025/12/04/2574a342a1a6e414ca03ba543cb67c3f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/2574a342a1a6e414ca03ba543cb67c3f.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;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//2025/12/04/9ef0b88be1877b6808cae5b2ab01e3e0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/9ef0b88be1877b6808cae5b2ab01e3e0.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//2025/12/08/b3c2b2c556035d5702008da8240a8c89.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/08/b3c2b2c556035d5702008da8240a8c89.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;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//2025/12/04/1bcc2238828851e38524a1b1b2a4041f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/1bcc2238828851e38524a1b1b2a4041f.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;点击部署，阿里云轻量ESC不支持一键部署，只能手动部署：&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//2025/12/08/62a19ac3e263f686d409c16e34ec3f31.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/08/62a19ac3e263f686d409c16e34ec3f31.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;根据你的服务器类型选择SSL证书下载，我是用的Nginx。&lt;/p&gt;
&lt;p&gt;部署我求助了AI工具，需要配置Nginx，我确实不会，这里我也不太好描述，还是自行问AI工具吧。&lt;/p&gt;
&lt;h2 id=&#34;icp-备案&#34;&gt;ICP 备案&lt;/h2&gt;
&lt;p&gt;点击&lt;a href=&#34;https://beian.aliyun.com/?spm=5176.62f6de69.J_4VYgf18xNlTAyFFbOuOQe.d_beian&#34;&gt;网站备案_ICP 备案_备案迁移_App 备案_小程序备案_备案 - 阿里云&lt;/a&gt;开始备案。&lt;/p&gt;
&lt;h3 id=&#34;准备材料&#34;&gt;准备材料&lt;/h3&gt;
&lt;p&gt;需要准备身份证，根据提示填写个人信息即可。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;所需材料清单：&lt;/strong&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;li&gt;域名证书&lt;/li&gt;
&lt;li&gt;服务器证明（购买凭证）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;备案流程&#34;&gt;备案流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;填写备案信息&lt;/strong&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;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;域名备注十分重要，如果是商业用途就需要上传营业执照等信息，这个我没经验，请自行研究。如果是个人用途，需要注明个人使用。比如我的域名其实是为了小程序的后端使用的，我备注的内容是xxx小程序后端API使用。 这样写是无法通过审核的，默认是有商业用途。所以我改成了：&lt;strong&gt;个人工具后端&lt;/strong&gt;。
如果你是用于部署个人博客，有些省份是不能备案的，这个请你最好提前在购买域名的厂商客服那咨询一下。
有一些关键字是不能出现的，比如新闻、出版、教育、医疗等需要前置审批的内容。如果你涉及这些也需要咨询一下厂商客服。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;上传资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;按要求上传各类证件照片&lt;/li&gt;
&lt;li&gt;确保照片清晰可见&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;真实性核验&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;部分地区需要视频核验&lt;/li&gt;
&lt;li&gt;按照要求进行人脸识别&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;提交审核&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;云服务商初审（1-2 个工作日）&lt;/li&gt;
&lt;li&gt;管局审核（3-20 个工作日，各地不同）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;重要提醒&#34;&gt;重要提醒&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;不能和小程序备案同步进行！&lt;/strong&gt; ICP 备案同一个主体（人）只能进行一个备案，需要当前备案结束后才能进行下一个。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;备案时长：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首次备案：通常需要 10-20 个工作日&lt;/li&gt;
&lt;li&gt;不同省份审核时间不同&lt;/li&gt;
&lt;li&gt;遇到节假日会延长&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;我两次备案都是在10天完成。如果你在阿里云购买的服务器也是在阿里云购买域名备案，在域名关联服务器时候选的是阿里云的，那么阿里云会送服务器时长。备案花了多少条就会送多少天。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;备案网站命名规范：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;个人网站不能包含企业、行业等字样&lt;/li&gt;
&lt;li&gt;不能涉及新闻、出版、教育、医疗等需要前置审批的内容&lt;/li&gt;
&lt;li&gt;建议使用通用名称，如&amp;quot;XX 个人博客&amp;quot;、&amp;ldquo;XX 个人工具&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;公安联网备案&#34;&gt;公安联网备案&lt;/h2&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//2025/12/22/cde749a18a58deeafa4089699699b325.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/22/cde749a18a58deeafa4089699699b325.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;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//2025/12/22/34fac0556854cc60c4727091779991d2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/22/34fac0556854cc60c4727091779991d2.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;a href=&#34;https://help.aliyun.com/zh/icp-filing/basic-icp-service/the-public-security-network-for-the-record-information-fill-in-the-guide&#34;&gt;分步完成网站与APP公安联网备案申请-备案-阿里云&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//2025/12/22/ba4afe1a8ef7eb136b0843db980457fc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/22/ba4afe1a8ef7eb136b0843db980457fc.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;h2 id=&#34;小程序备案&#34;&gt;小程序备案&lt;/h2&gt;
&lt;p&gt;访问&lt;a href=&#34;https://mp.weixin.qq.com/wxamp/home/guide?lang=zh_CN&amp;amp;token=1108861003&#34;&gt;微信小程序&lt;/a&gt;公众平台进行备案。&lt;/p&gt;
&lt;h3 id=&#34;备案流程-1&#34;&gt;备案流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;补充小程序信息&lt;/strong&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;li&gt;服务范围等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;先提交微信官方审核&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;微信会审核小程序信息是否合规&lt;/li&gt;
&lt;li&gt;审核时间通常为 1-3 个工作日&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;审核通过自动提交到工信部审核&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工信部审核通过后，小程序备案完成&lt;/li&gt;
&lt;li&gt;审核时间通常为 7-20 个工作日&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;审核不通过的处理&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;审核不通过会电话联系，告知需要修改的地方&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意：电话会被小米手机标记为广告电话，请在提交审核这几天及时接听广告电话，避免错过！&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;小程序平台会站内信告知需要修改的地方，但是不如电话里说的清楚&lt;/li&gt;
&lt;li&gt;电话中不明白的可以及时问，但是错过电话就打不回去了&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;strong&gt;但是小程序备注需要注明：给自己使用。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;比如我开发了一个 Strava 贴纸合成到照片的小程序&lt;/li&gt;
&lt;li&gt;我的备注是：&lt;strong&gt;用于给自己将两张图片合成为一张图片&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;备注填写技巧：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;明确说明是个人使用&lt;/li&gt;
&lt;li&gt;功能描述简洁明了&lt;/li&gt;
&lt;li&gt;避免涉及商业用途&lt;/li&gt;
&lt;li&gt;避免涉及社交、支付等敏感功能&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;小程序备案常见问题&#34;&gt;小程序备案常见问题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Q1: 个人小程序可以选择哪些类目？&lt;/strong&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;li&gt;教育类（非学历教育）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Q2: 个人小程序有哪些限制？&lt;/strong&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;li&gt;不能发布商业广告&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Q3: 小程序备案失败常见原因&lt;/strong&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;li&gt;小程序名称不符合规范&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;小程序认证&#34;&gt;小程序认证&lt;/h2&gt;
&lt;p&gt;小程序认证分为&lt;strong&gt;个人认证&lt;/strong&gt;和&lt;strong&gt;企业认证&lt;/strong&gt;，两者权限有较大差异。&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;ul&gt;
&lt;li&gt;无法使用微信支付&lt;/li&gt;
&lt;li&gt;无法使用卡券功能&lt;/li&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;strong&gt;优势：&lt;/strong&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;h3 id=&#34;企业认证&#34;&gt;企业认证&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;认证费用：&lt;/strong&gt; 300 元/年（微信官方收取）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;所需材料：&lt;/strong&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;li&gt;企业邮箱&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;认证流程：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;登录小程序后台，点击&amp;quot;微信认证&amp;quot;&lt;/li&gt;
&lt;li&gt;填写企业信息&lt;/li&gt;
&lt;li&gt;上传营业执照等资料&lt;/li&gt;
&lt;li&gt;选择认证方式：
&lt;ul&gt;
&lt;li&gt;对公账户打款验证（0.01 元，需 1-3 个工作日）&lt;/li&gt;
&lt;li&gt;法人微信扫码验证（即时验证，推荐）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;等待审核（通常 1-3 个工作日）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;企业认证的优势：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开通微信支付功能&lt;/li&gt;
&lt;li&gt;提升用户信任度&lt;/li&gt;
&lt;li&gt;解锁更多 API 接口&lt;/li&gt;
&lt;li&gt;无访问用户数限制&lt;/li&gt;
&lt;li&gt;可以使用附近的小程序功能&lt;/li&gt;
&lt;li&gt;可以发布卡券&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;认证注意事项&#34;&gt;认证注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;企业认证必须是已注册的企业&lt;/strong&gt;，个体工商户也可以&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;年审：&lt;/strong&gt; 企业认证每年需要重新认证，费用 300 元&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;认证后不可更改主体&lt;/strong&gt;，请谨慎选择&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;认证信息要真实&lt;/strong&gt;，虚假信息会导致认证失败甚至封号&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;开发环境配置&#34;&gt;开发环境配置&lt;/h2&gt;
&lt;h3 id=&#34;下载微信开发者工具&#34;&gt;下载微信开发者工具&lt;/h3&gt;
&lt;p&gt;访问&lt;a href=&#34;https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html&#34;&gt;微信开发者工具下载页面&lt;/a&gt;，根据操作系统下载对应版本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;支持平台：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 64 位&lt;/li&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;申请-appid&#34;&gt;申请 AppID&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;登录&lt;a href=&#34;https://mp.weixin.qq.com/&#34;&gt;微信公众平台&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;进入小程序后台&lt;/li&gt;
&lt;li&gt;在&amp;quot;开发&amp;quot; -&amp;gt; &amp;ldquo;开发管理&amp;rdquo; -&amp;gt; &amp;ldquo;开发设置&amp;quot;中查看 AppID&lt;/li&gt;
&lt;li&gt;复制 AppID 备用&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;创建第一个小程序项目&#34;&gt;创建第一个小程序项目&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开微信开发者工具&lt;/li&gt;
&lt;li&gt;扫码登录&lt;/li&gt;
&lt;li&gt;选择&amp;quot;小程序项目&amp;rdquo;&lt;/li&gt;
&lt;li&gt;点击&amp;quot;+&amp;ldquo;创建项目&lt;/li&gt;
&lt;li&gt;填写项目信息：
&lt;ul&gt;
&lt;li&gt;项目名称&lt;/li&gt;
&lt;li&gt;目录（选择一个空文件夹）&lt;/li&gt;
&lt;li&gt;AppID（填写之前复制的 AppID）&lt;/li&gt;
&lt;li&gt;开发模式（选择&amp;quot;小程序&amp;rdquo;）&lt;/li&gt;
&lt;li&gt;后端服务（选择&amp;quot;不使用云服务&amp;quot;，如需要后期可更改）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;点击&amp;quot;新建&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;服务器配置&#34;&gt;服务器配置&lt;/h2&gt;
&lt;h3 id=&#34;配置服务器域名&#34;&gt;配置服务器域名&lt;/h3&gt;
&lt;p&gt;小程序只能与配置过的服务器域名进行网络通信。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;配置步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;登录&lt;a href=&#34;https://mp.weixin.qq.com/&#34;&gt;微信公众平台&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;进入&amp;quot;开发&amp;quot; -&amp;gt; &amp;ldquo;开发管理&amp;rdquo; -&amp;gt; &amp;ldquo;开发设置&amp;rdquo; -&amp;gt; &amp;ldquo;服务器域名&amp;rdquo;&lt;/li&gt;
&lt;li&gt;点击&amp;quot;修改&amp;quot;&lt;/li&gt;
&lt;li&gt;分别配置：
&lt;ul&gt;
&lt;li&gt;request 合法域名（用于 wx.request）&lt;/li&gt;
&lt;li&gt;socket 合法域名（用于 wx.connectSocket）&lt;/li&gt;
&lt;li&gt;uploadFile 合法域名（用于 wx.uploadFile）&lt;/li&gt;
&lt;li&gt;downloadFile 合法域名（用于 wx.downloadFile）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;域名要求：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必须是 HTTPS 协议&lt;/li&gt;
&lt;li&gt;域名必须备案&lt;/li&gt;
&lt;li&gt;域名不能使用 IP 地址&lt;/li&gt;
&lt;li&gt;域名不能带端口号&lt;/li&gt;
&lt;li&gt;一个月内最多修改 5 次&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;ssl-证书申请&#34;&gt;SSL 证书申请&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;免费证书来源：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Let&amp;rsquo;s Encrypt（推荐，免费，90 天有效期）&lt;/li&gt;
&lt;li&gt;阿里云免费证书（1 年有效期）&lt;/li&gt;
&lt;li&gt;腾讯云免费证书（1 年有效期）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;证书申请步骤（以阿里云为例）：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;登录阿里云控制台&lt;/li&gt;
&lt;li&gt;搜索&amp;quot;SSL 证书&amp;quot;&lt;/li&gt;
&lt;li&gt;选择&amp;quot;免费证书&amp;quot;&lt;/li&gt;
&lt;li&gt;填写域名信息&lt;/li&gt;
&lt;li&gt;选择 DNS 验证或文件验证&lt;/li&gt;
&lt;li&gt;完成验证后下载证书&lt;/li&gt;
&lt;li&gt;在服务器上配置证书（Nginx/Apache）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;nginx-配置示例&#34;&gt;Nginx 配置示例&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ssl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;yourdomain.com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/path/to/cert.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate_key&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/path/to/key.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_session_cache&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;shared:SSL:1m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_session_timeout&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_ciphers&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;HIGH:!aNULL:!MD5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://localhost:3000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Host&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$remote_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# HTTP自动跳转HTTPS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;yourdomain.com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;301&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;https://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$server_name$request_uri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;小程序发布流程&#34;&gt;小程序发布流程&lt;/h2&gt;
&lt;h3 id=&#34;代码开发&#34;&gt;代码开发&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在微信开发者工具中进行开发&lt;/li&gt;
&lt;li&gt;使用模拟器和真机预览测试&lt;/li&gt;
&lt;li&gt;确保代码符合微信小程序规范&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;代码上传&#34;&gt;代码上传&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在开发者工具中点击&amp;quot;上传&amp;quot;&lt;/li&gt;
&lt;li&gt;填写版本号和项目备注&lt;/li&gt;
&lt;li&gt;上传成功后可以在小程序后台看到&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;用户隐私保护指引设置&#34;&gt;用户隐私保护指引设置&lt;/h3&gt;
&lt;p&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//2025/11/24/30f931394d5afe72ab5ce867f70a580d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/24/30f931394d5afe72ab5ce867f70a580d.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;!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//2025/11/24/cc9fc5694f14aad01b48999c363aa55d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/24/cc9fc5694f14aad01b48999c363aa55d.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;p&gt;为了能弹出对话框让用户同意隐私获取，需要在&lt;code&gt;app.json&lt;/code&gt;中加入这行代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;__usePrivacyCheck__&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样小程序会在使用到获取隐私的接口时自动弹出来。&lt;/p&gt;
&lt;p&gt;如果你的小程序定制化程度高，你也可以选择在你想要的时候弹出来，那么你需要参考官方文档&lt;a href=&#34;https://mp.weixin.qq.com/wxopen/announce?action=getannouncement&amp;amp;announce_id=1694692148&amp;amp;version=23&amp;amp;lang=zh_CN&#34;&gt;微信公众平台&lt;/a&gt;，自行修改。&lt;/p&gt;
&lt;h3 id=&#34;提交审核&#34;&gt;提交审核&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;登录&lt;a href=&#34;https://mp.weixin.qq.com/&#34;&gt;微信公众平台&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;进入&amp;quot;版本管理&amp;quot;&lt;/li&gt;
&lt;li&gt;选择刚上传的版本&lt;/li&gt;
&lt;li&gt;点击&amp;quot;提交审核&amp;quot;&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;li&gt;提交等待审核&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;审核时长：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一般 1-7 个工作日&lt;/li&gt;
&lt;li&gt;首次审核可能较慢&lt;/li&gt;
&lt;li&gt;节假日会延长&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;审核不通过处理&#34;&gt;审核不通过处理&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;常见驳回原因：&lt;/strong&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;li&gt;页面存在违规内容&lt;/li&gt;
&lt;li&gt;测试账号无法登录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;处理方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查看驳回原因&lt;/li&gt;
&lt;li&gt;根据原因修改代码或说明&lt;/li&gt;
&lt;li&gt;重新提交审核&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;发布上线&#34;&gt;发布上线&lt;/h3&gt;
&lt;p&gt;审核通过后：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在&amp;quot;版本管理&amp;quot;中找到已通过审核的版本&lt;/li&gt;
&lt;li&gt;点击&amp;quot;发布&amp;quot;&lt;/li&gt;
&lt;li&gt;确认发布&lt;/li&gt;
&lt;li&gt;等待几分钟，小程序即可被用户搜索和使用&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;开发注意事项&#34;&gt;开发注意事项&lt;/h2&gt;
&lt;h3 id=&#34;开发规范&#34;&gt;开发规范&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;代码规范&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 ES6 语法&lt;/li&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;
&lt;p&gt;&lt;strong&gt;性能优化&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图片压缩，使用 webp 格式&lt;/li&gt;
&lt;li&gt;合理使用分包加载&lt;/li&gt;
&lt;li&gt;避免频繁的 setData 操作&lt;/li&gt;
&lt;li&gt;及时清理定时器和监听器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用户体验&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加 loading 提示&lt;/li&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;/ol&gt;
&lt;h3 id=&#34;常见问题&#34;&gt;常见问题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Q1: 开发时如何调试 HTTPS 请求？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开发者工具中可以勾选&amp;quot;不校验合法域名&amp;quot;&lt;/li&gt;
&lt;li&gt;但上线前必须配置好合法域名&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Q2: 如何处理跨域问题？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;小程序不存在跨域问题&lt;/li&gt;
&lt;li&gt;但需要在服务器域名白名单中&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Q3: 小程序包大小限制？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主包不能超过 2MB&lt;/li&gt;
&lt;li&gt;所有分包总大小不能超过 20MB（使用分包优化后可达到更大）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Q4: 如何快速预览真机效果？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;点击&amp;quot;预览&amp;quot;生成二维码&lt;/li&gt;
&lt;li&gt;微信扫码即可在手机上预览&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;微信小程序开发涉及的流程比较多，需要耐心完成每一步。主要流程总结如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ &lt;strong&gt;购买服务器和域名&lt;/strong&gt;（注意选择性价比高的）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;ICP 备案&lt;/strong&gt;（10-20 个工作日，不能与小程序备案同时进行）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;小程序备案&lt;/strong&gt;（7-20 个工作日，个人备案注意备注&amp;quot;给自己使用&amp;quot;）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;小程序认证&lt;/strong&gt;（企业认证 300 元/年，个人可不认证）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;配置开发环境&lt;/strong&gt;（下载开发者工具，申请 AppID）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;配置服务器域名&lt;/strong&gt;（申请 SSL 证书，配置 HTTPS）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;开发与测试&lt;/strong&gt;（遵循开发规范，注意性能优化）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;提交审核与发布&lt;/strong&gt;（耐心等待审核，及时处理驳回）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;时间规划建议：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;备案阶段：预留 1 个月时间&lt;/li&gt;
&lt;li&gt;开发阶段：根据项目复杂度安排&lt;/li&gt;
&lt;li&gt;审核发布：预留 1 周时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;预算建议（个人小程序）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;服务器：100-500 元/年&lt;/li&gt;
&lt;li&gt;域名：10-100 元/年&lt;/li&gt;
&lt;li&gt;SSL 证书：免费&lt;/li&gt;
&lt;li&gt;小程序注册：免费（个人）&lt;/li&gt;
&lt;li&gt;小程序认证：可选（企业 300 元/年）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;希望这篇文章能帮助到正在开发小程序的朋友们，祝大家开发顺利！如有问题欢迎交流讨论。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>微信小程序开发不仅仅是写代码，还涉及服务器购买、域名备案、小程序备案、认证等一系列流程性工作。本文记录了从零开始开发微信小程序的完整流程，包括踩坑记录和注意事项，希望能帮助其他开发者少走弯路。</p>
<h2 id="服务器购买">服务器购买</h2>
<h3 id="选择云服务商">选择云服务商</h3>
<p>常见的云服务商有：</p>
<ul>
<li>阿里云（推荐）</li>
<li>腾讯云</li>
<li>华为云</li>
<li>百度云</li>
</ul>
<h3 id="服务器配置建议">服务器配置建议</h3>
<p><strong>小型个人小程序推荐配置：</strong></p>
<ul>
<li>CPU：1 核或 2 核</li>
<li>内存：2GB</li>
<li>带宽：1-3Mbps</li>
<li>系统盘：40GB</li>
<li>操作系统：CentOS 7.x 或 Ubuntu 18.04+</li>
</ul>
<p><strong>费用参考：</strong></p>
<ul>
<li>新用户通常有优惠，年费在 100-500 元不等</li>
<li>建议先购买 1 年，测试稳定后再续费</li>
</ul>
<h3 id="购买注意事项">购买注意事项</h3>
<ol>
<li>选择离目标用户较近的地域节点，可以降低延迟</li>
<li>新用户可关注各大云平台的首购优惠</li>
<li>学生用户可申请学生优惠</li>
<li>注意查看带宽费用，避免后期流量费用过高</li>
</ol>
<h2 id="域名购买">域名购买</h2>
<p><a href="https://wanwang.aliyun.com/newdomain/1yuan?spm=5176.8048432.J_1403848460.2.714769a3RIQTOp&amp;keyword=06031612">购买域名 1 元起_.xin 首年 0 元_域名优惠活动 - 阿里云权益中心</a></p>
<h3 id="选择合适的域名">选择合适的域名</h3>
<p>小程序用户看不到域名，所以选择便宜的即可。用生日组合纯数字.xyz 域名最便宜。</p>
<p><strong>选购技巧：</strong></p>
<ul>
<li>选择一个最便宜的域名时，注册时选择 2 年看看后期续费费用</li>
<li>如果一年 18 元，两年 120 元，说明第二年费用恢复原价，太贵了不要买</li>
<li>推荐选择续费价格稳定的域名后缀</li>
</ul>
<p><strong>常见便宜域名后缀：</strong></p>
<ul>
<li><code>.xyz</code>：首年最便宜，但续费可能较贵</li>
<li><code>.top</code>：价格适中</li>
<li><code>.site</code>：价格适中</li>
<li><code>.club</code>：价格适中</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//2025/11/18/f7cd9f9011c7b32a640c612df85d5df3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/18/f7cd9f9011c7b32a640c612df85d5df3.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//2025/11/18/389f251903c6ac923c999c86bc14c58a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/18/389f251903c6ac923c999c86bc14c58a.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>下单时创建一个新的信息模板，避免之前创建过的填的信息不完善。购买之后会直接提交到域名注册局审核，信息不完善容易审核不通过。</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//2025/11/18/11d11bf95e226b7e0e354b75d2175938.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/18/11d11bf95e226b7e0e354b75d2175938.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>
<ul>
<li>姓名必须与身份证一致</li>
<li>地址填写详细，精确到门牌号</li>
<li>电话号码保持畅通，可能会接到审核电话</li>
<li>邮箱填写常用邮箱</li>
</ul>
<h3 id="域名实名认证">域名实名认证</h3>
<p>等待几小时（一般在 6 小时内），域名注册局实名认证成功，即可进行下一步备案。</p>
<p><strong>实名认证材料：</strong></p>
<ul>
<li>个人：身份证正反面照片</li>
<li>企业：营业执照、法人身份证</li>
</ul>
<h3 id="域名解析">域名解析</h3>
<p>登录域名服务商，找到域名解析，添加解析记录。比如我在阿里云买的，从这里进入：</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//2025/12/04/983aa81ac07af6a88b5ea59a69193528.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/983aa81ac07af6a88b5ea59a69193528.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>
<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//2025/12/04/e356bc511e1442a7d5be9b465121c41e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/e356bc511e1442a7d5be9b465121c41e.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>添加解析记录，第四步的IP地址就是你购买服务器后，服务器提供商给你的公网IP地址：</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//2025/12/04/2e15af90548937c053cb193a207591fc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/2e15af90548937c053cb193a207591fc.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>
<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//2025/12/04/a5c4ec7ac6ba8cdf4c59854d4986e0e8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/a5c4ec7ac6ba8cdf4c59854d4986e0e8.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>
<h2 id="ssl证书购买与部署">SSL证书购买与部署</h2>
<p>微信小程序必须使用Https协议，所以需要购买SSL证书。阿里云有免费的证书可以测试使用，但是有效期是3个月，需要定期续费。</p>
<p>从以下路径找到免费证书申请入口，你也可以在阿里云顶部搜索框搜索SSL，也能找到。</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//2025/12/04/7c9ec5e3e3b37d5a2c871ba83ed15ae5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/7c9ec5e3e3b37d5a2c871ba83ed15ae5.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//2025/12/04/91edd3abcc92baad15388995f85d0615.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/91edd3abcc92baad15388995f85d0615.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//2025/12/04/2574a342a1a6e414ca03ba543cb67c3f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/2574a342a1a6e414ca03ba543cb67c3f.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>
<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//2025/12/04/9ef0b88be1877b6808cae5b2ab01e3e0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/9ef0b88be1877b6808cae5b2ab01e3e0.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//2025/12/08/b3c2b2c556035d5702008da8240a8c89.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/08/b3c2b2c556035d5702008da8240a8c89.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>
<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//2025/12/04/1bcc2238828851e38524a1b1b2a4041f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/04/1bcc2238828851e38524a1b1b2a4041f.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>点击部署，阿里云轻量ESC不支持一键部署，只能手动部署：</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//2025/12/08/62a19ac3e263f686d409c16e34ec3f31.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/08/62a19ac3e263f686d409c16e34ec3f31.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>根据你的服务器类型选择SSL证书下载，我是用的Nginx。</p>
<p>部署我求助了AI工具，需要配置Nginx，我确实不会，这里我也不太好描述，还是自行问AI工具吧。</p>
<h2 id="icp-备案">ICP 备案</h2>
<p>点击<a href="https://beian.aliyun.com/?spm=5176.62f6de69.J_4VYgf18xNlTAyFFbOuOQe.d_beian">网站备案_ICP 备案_备案迁移_App 备案_小程序备案_备案 - 阿里云</a>开始备案。</p>
<h3 id="准备材料">准备材料</h3>
<p>需要准备身份证，根据提示填写个人信息即可。</p>
<p><strong>所需材料清单：</strong></p>
<ul>
<li>身份证正反面照片（清晰、完整）</li>
<li>手持身份证照片</li>
<li>幕布照片（部分云服务商需要）</li>
<li>域名证书</li>
<li>服务器证明（购买凭证）</li>
</ul>
<h3 id="备案流程">备案流程</h3>
<ol>
<li><strong>填写备案信息</strong>
<ul>
<li>主体信息（个人或企业信息）</li>
<li>网站信息（网站名称、域名等）</li>
<li>接入信息（服务器信息）</li>
</ul>
</li>
</ol>
<blockquote>
<p>域名备注十分重要，如果是商业用途就需要上传营业执照等信息，这个我没经验，请自行研究。如果是个人用途，需要注明个人使用。比如我的域名其实是为了小程序的后端使用的，我备注的内容是xxx小程序后端API使用。 这样写是无法通过审核的，默认是有商业用途。所以我改成了：<strong>个人工具后端</strong>。
如果你是用于部署个人博客，有些省份是不能备案的，这个请你最好提前在购买域名的厂商客服那咨询一下。
有一些关键字是不能出现的，比如新闻、出版、教育、医疗等需要前置审批的内容。如果你涉及这些也需要咨询一下厂商客服。</p>
</blockquote>
<ol start="2">
<li>
<p><strong>上传资料</strong></p>
<ul>
<li>按要求上传各类证件照片</li>
<li>确保照片清晰可见</li>
</ul>
</li>
<li>
<p><strong>真实性核验</strong></p>
<ul>
<li>部分地区需要视频核验</li>
<li>按照要求进行人脸识别</li>
</ul>
</li>
<li>
<p><strong>提交审核</strong></p>
<ul>
<li>云服务商初审（1-2 个工作日）</li>
<li>管局审核（3-20 个工作日，各地不同）</li>
</ul>
</li>
</ol>
<h3 id="重要提醒">重要提醒</h3>
<p><strong>不能和小程序备案同步进行！</strong> ICP 备案同一个主体（人）只能进行一个备案，需要当前备案结束后才能进行下一个。</p>
<p><strong>备案时长：</strong></p>
<ul>
<li>首次备案：通常需要 10-20 个工作日</li>
<li>不同省份审核时间不同</li>
<li>遇到节假日会延长</li>
</ul>
<blockquote>
<p>我两次备案都是在10天完成。如果你在阿里云购买的服务器也是在阿里云购买域名备案，在域名关联服务器时候选的是阿里云的，那么阿里云会送服务器时长。备案花了多少条就会送多少天。</p>
</blockquote>
<p><strong>备案网站命名规范：</strong></p>
<ul>
<li>个人网站不能包含企业、行业等字样</li>
<li>不能涉及新闻、出版、教育、医疗等需要前置审批的内容</li>
<li>建议使用通用名称，如&quot;XX 个人博客&quot;、&ldquo;XX 个人工具&rdquo;</li>
</ul>
<h2 id="公安联网备案">公安联网备案</h2>
<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//2025/12/22/cde749a18a58deeafa4089699699b325.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/22/cde749a18a58deeafa4089699699b325.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>
<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//2025/12/22/34fac0556854cc60c4727091779991d2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/22/34fac0556854cc60c4727091779991d2.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><a href="https://help.aliyun.com/zh/icp-filing/basic-icp-service/the-public-security-network-for-the-record-information-fill-in-the-guide">分步完成网站与APP公安联网备案申请-备案-阿里云</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//2025/12/22/ba4afe1a8ef7eb136b0843db980457fc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/12/22/ba4afe1a8ef7eb136b0843db980457fc.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>
<h2 id="小程序备案">小程序备案</h2>
<p>访问<a href="https://mp.weixin.qq.com/wxamp/home/guide?lang=zh_CN&amp;token=1108861003">微信小程序</a>公众平台进行备案。</p>
<h3 id="备案流程-1">备案流程</h3>
<ol>
<li>
<p><strong>补充小程序信息</strong></p>
<ul>
<li>小程序名称</li>
<li>简介</li>
<li>服务类目</li>
<li>服务范围等</li>
</ul>
</li>
<li>
<p><strong>先提交微信官方审核</strong></p>
<ul>
<li>微信会审核小程序信息是否合规</li>
<li>审核时间通常为 1-3 个工作日</li>
</ul>
</li>
<li>
<p><strong>审核通过自动提交到工信部审核</strong></p>
<ul>
<li>工信部审核通过后，小程序备案完成</li>
<li>审核时间通常为 7-20 个工作日</li>
</ul>
</li>
<li>
<p><strong>审核不通过的处理</strong></p>
<ul>
<li>审核不通过会电话联系，告知需要修改的地方</li>
<li><strong>注意：电话会被小米手机标记为广告电话，请在提交审核这几天及时接听广告电话，避免错过！</strong></li>
<li>小程序平台会站内信告知需要修改的地方，但是不如电话里说的清楚</li>
<li>电话中不明白的可以及时问，但是错过电话就打不回去了</li>
</ul>
</li>
</ol>
<h3 id="个人小程序备案说明">个人小程序备案说明</h3>
<p><strong>重要提示：</strong> 普通小工具类小程序不需要公司也不需要营业执照，用身份证提交就行。</p>
<p><strong>但是小程序备注需要注明：给自己使用。</strong></p>
<p><strong>示例：</strong></p>
<ul>
<li>比如我开发了一个 Strava 贴纸合成到照片的小程序</li>
<li>我的备注是：<strong>用于给自己将两张图片合成为一张图片</strong></li>
</ul>
<p><strong>备注填写技巧：</strong></p>
<ul>
<li>明确说明是个人使用</li>
<li>功能描述简洁明了</li>
<li>避免涉及商业用途</li>
<li>避免涉及社交、支付等敏感功能</li>
</ul>
<h3 id="小程序备案常见问题">小程序备案常见问题</h3>
<p><strong>Q1: 个人小程序可以选择哪些类目？</strong></p>
<ul>
<li>工具类（如计算器、记事本）</li>
<li>生活服务类（非商业）</li>
<li>体育类</li>
<li>教育类（非学历教育）</li>
</ul>
<p><strong>Q2: 个人小程序有哪些限制？</strong></p>
<ul>
<li>不能涉及支付功能</li>
<li>不能涉及社交功能</li>
<li>不能涉及直播功能</li>
<li>不能发布商业广告</li>
</ul>
<p><strong>Q3: 小程序备案失败常见原因</strong></p>
<ul>
<li>备注未说明个人使用</li>
<li>功能超出个人类目范围</li>
<li>服务器未备案或备案信息不一致</li>
<li>小程序名称不符合规范</li>
</ul>
<h2 id="小程序认证">小程序认证</h2>
<p>小程序认证分为<strong>个人认证</strong>和<strong>企业认证</strong>，两者权限有较大差异。</p>
<h3 id="个人小程序未认证">个人小程序（未认证）</h3>
<p><strong>限制：</strong></p>
<ul>
<li>无法使用微信支付</li>
<li>无法使用卡券功能</li>
<li>无法使用附近的小程序</li>
<li>部分接口受限</li>
<li>每日访问用户数有上限</li>
</ul>
<p><strong>优势：</strong></p>
<ul>
<li>免费</li>
<li>审核流程简单</li>
<li>适合个人学习和小工具开发</li>
</ul>
<h3 id="企业认证">企业认证</h3>
<p><strong>认证费用：</strong> 300 元/年（微信官方收取）</p>
<p><strong>所需材料：</strong></p>
<ul>
<li>营业执照</li>
<li>法人身份证</li>
<li>对公账户信息（或法人微信支付）</li>
<li>企业邮箱</li>
</ul>
<p><strong>认证流程：</strong></p>
<ol>
<li>登录小程序后台，点击&quot;微信认证&quot;</li>
<li>填写企业信息</li>
<li>上传营业执照等资料</li>
<li>选择认证方式：
<ul>
<li>对公账户打款验证（0.01 元，需 1-3 个工作日）</li>
<li>法人微信扫码验证（即时验证，推荐）</li>
</ul>
</li>
<li>等待审核（通常 1-3 个工作日）</li>
</ol>
<p><strong>企业认证的优势：</strong></p>
<ul>
<li>开通微信支付功能</li>
<li>提升用户信任度</li>
<li>解锁更多 API 接口</li>
<li>无访问用户数限制</li>
<li>可以使用附近的小程序功能</li>
<li>可以发布卡券</li>
</ul>
<h3 id="认证注意事项">认证注意事项</h3>
<ol>
<li><strong>企业认证必须是已注册的企业</strong>，个体工商户也可以</li>
<li><strong>年审：</strong> 企业认证每年需要重新认证，费用 300 元</li>
<li><strong>认证后不可更改主体</strong>，请谨慎选择</li>
<li><strong>认证信息要真实</strong>，虚假信息会导致认证失败甚至封号</li>
</ol>
<h2 id="开发环境配置">开发环境配置</h2>
<h3 id="下载微信开发者工具">下载微信开发者工具</h3>
<p>访问<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html">微信开发者工具下载页面</a>，根据操作系统下载对应版本。</p>
<p><strong>支持平台：</strong></p>
<ul>
<li>Windows 64 位</li>
<li>macOS</li>
<li>Linux</li>
</ul>
<h3 id="申请-appid">申请 AppID</h3>
<ol>
<li>登录<a href="https://mp.weixin.qq.com/">微信公众平台</a></li>
<li>进入小程序后台</li>
<li>在&quot;开发&quot; -&gt; &ldquo;开发管理&rdquo; -&gt; &ldquo;开发设置&quot;中查看 AppID</li>
<li>复制 AppID 备用</li>
</ol>
<h3 id="创建第一个小程序项目">创建第一个小程序项目</h3>
<ol>
<li>打开微信开发者工具</li>
<li>扫码登录</li>
<li>选择&quot;小程序项目&rdquo;</li>
<li>点击&quot;+&ldquo;创建项目</li>
<li>填写项目信息：
<ul>
<li>项目名称</li>
<li>目录（选择一个空文件夹）</li>
<li>AppID（填写之前复制的 AppID）</li>
<li>开发模式（选择&quot;小程序&rdquo;）</li>
<li>后端服务（选择&quot;不使用云服务&quot;，如需要后期可更改）</li>
</ul>
</li>
<li>点击&quot;新建&quot;</li>
</ol>
<h2 id="服务器配置">服务器配置</h2>
<h3 id="配置服务器域名">配置服务器域名</h3>
<p>小程序只能与配置过的服务器域名进行网络通信。</p>
<p><strong>配置步骤：</strong></p>
<ol>
<li>登录<a href="https://mp.weixin.qq.com/">微信公众平台</a></li>
<li>进入&quot;开发&quot; -&gt; &ldquo;开发管理&rdquo; -&gt; &ldquo;开发设置&rdquo; -&gt; &ldquo;服务器域名&rdquo;</li>
<li>点击&quot;修改&quot;</li>
<li>分别配置：
<ul>
<li>request 合法域名（用于 wx.request）</li>
<li>socket 合法域名（用于 wx.connectSocket）</li>
<li>uploadFile 合法域名（用于 wx.uploadFile）</li>
<li>downloadFile 合法域名（用于 wx.downloadFile）</li>
</ul>
</li>
</ol>
<p><strong>域名要求：</strong></p>
<ul>
<li>必须是 HTTPS 协议</li>
<li>域名必须备案</li>
<li>域名不能使用 IP 地址</li>
<li>域名不能带端口号</li>
<li>一个月内最多修改 5 次</li>
</ul>
<h3 id="ssl-证书申请">SSL 证书申请</h3>
<p><strong>免费证书来源：</strong></p>
<ul>
<li>Let&rsquo;s Encrypt（推荐，免费，90 天有效期）</li>
<li>阿里云免费证书（1 年有效期）</li>
<li>腾讯云免费证书（1 年有效期）</li>
</ul>
<p><strong>证书申请步骤（以阿里云为例）：</strong></p>
<ol>
<li>登录阿里云控制台</li>
<li>搜索&quot;SSL 证书&quot;</li>
<li>选择&quot;免费证书&quot;</li>
<li>填写域名信息</li>
<li>选择 DNS 验证或文件验证</li>
<li>完成验证后下载证书</li>
<li>在服务器上配置证书（Nginx/Apache）</li>
</ol>
<h3 id="nginx-配置示例">Nginx 配置示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="k">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">listen</span> <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">server_name</span> <span class="s">yourdomain.com</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_certificate</span> <span class="s">/path/to/cert.pem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_certificate_key</span> <span class="s">/path/to/key.pem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_session_cache</span> <span class="s">shared:SSL:1m</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_session_timeout</span> <span class="mi">5m</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_ciphers</span> <span class="s">HIGH:!aNULL:!MD5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">ssl_prefer_server_ciphers</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kn">proxy_pass</span> <span class="s">http://localhost:3000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$host</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kn">proxy_set_header</span> <span class="s">X-Real-IP</span> <span class="nv">$remote_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># HTTP自动跳转HTTPS
</span></span></span><span class="line"><span class="cl"><span class="k">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">server_name</span> <span class="s">yourdomain.com</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">return</span> <span class="mi">301</span> <span class="s">https://</span><span class="nv">$server_name$request_uri</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="小程序发布流程">小程序发布流程</h2>
<h3 id="代码开发">代码开发</h3>
<ol>
<li>在微信开发者工具中进行开发</li>
<li>使用模拟器和真机预览测试</li>
<li>确保代码符合微信小程序规范</li>
</ol>
<h3 id="代码上传">代码上传</h3>
<ol>
<li>在开发者工具中点击&quot;上传&quot;</li>
<li>填写版本号和项目备注</li>
<li>上传成功后可以在小程序后台看到</li>
</ol>
<h3 id="用户隐私保护指引设置">用户隐私保护指引设置</h3>
<p>必须要设置这一项，否则上线后小程序还是会有问题，但是这些问题在调试版本里都不会出现。</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//2025/11/24/30f931394d5afe72ab5ce867f70a580d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/24/30f931394d5afe72ab5ce867f70a580d.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>

<!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//2025/11/24/cc9fc5694f14aad01b48999c363aa55d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/24/cc9fc5694f14aad01b48999c363aa55d.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>
<p>为了能弹出对话框让用户同意隐私获取，需要在<code>app.json</code>中加入这行代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;__usePrivacyCheck__&#34;</span><span class="err">:</span> <span class="kc">true</span>
</span></span></code></pre></div><p>这样小程序会在使用到获取隐私的接口时自动弹出来。</p>
<p>如果你的小程序定制化程度高，你也可以选择在你想要的时候弹出来，那么你需要参考官方文档<a href="https://mp.weixin.qq.com/wxopen/announce?action=getannouncement&amp;announce_id=1694692148&amp;version=23&amp;lang=zh_CN">微信公众平台</a>，自行修改。</p>
<h3 id="提交审核">提交审核</h3>
<ol>
<li>登录<a href="https://mp.weixin.qq.com/">微信公众平台</a></li>
<li>进入&quot;版本管理&quot;</li>
<li>选择刚上传的版本</li>
<li>点击&quot;提交审核&quot;</li>
<li>填写审核信息：
<ul>
<li>配置功能页面（至少添加一个页面）</li>
<li>填写测试账号（如有登录功能）</li>
<li>补充说明</li>
</ul>
</li>
<li>提交等待审核</li>
</ol>
<p><strong>审核时长：</strong></p>
<ul>
<li>一般 1-7 个工作日</li>
<li>首次审核可能较慢</li>
<li>节假日会延长</li>
</ul>
<h3 id="审核不通过处理">审核不通过处理</h3>
<p><strong>常见驳回原因：</strong></p>
<ul>
<li>服务类目不符</li>
<li>涉及未开放的接口</li>
<li>功能描述与实际不符</li>
<li>页面存在违规内容</li>
<li>测试账号无法登录</li>
</ul>
<p><strong>处理方法：</strong></p>
<ul>
<li>查看驳回原因</li>
<li>根据原因修改代码或说明</li>
<li>重新提交审核</li>
</ul>
<h3 id="发布上线">发布上线</h3>
<p>审核通过后：</p>
<ol>
<li>在&quot;版本管理&quot;中找到已通过审核的版本</li>
<li>点击&quot;发布&quot;</li>
<li>确认发布</li>
<li>等待几分钟，小程序即可被用户搜索和使用</li>
</ol>
<h2 id="开发注意事项">开发注意事项</h2>
<h3 id="开发规范">开发规范</h3>
<ol>
<li>
<p><strong>代码规范</strong></p>
<ul>
<li>使用 ES6 语法</li>
<li>保持代码简洁易读</li>
<li>合理使用组件化开发</li>
<li>添加必要的注释</li>
</ul>
</li>
<li>
<p><strong>性能优化</strong></p>
<ul>
<li>图片压缩，使用 webp 格式</li>
<li>合理使用分包加载</li>
<li>避免频繁的 setData 操作</li>
<li>及时清理定时器和监听器</li>
</ul>
</li>
<li>
<p><strong>用户体验</strong></p>
<ul>
<li>添加 loading 提示</li>
<li>处理网络异常情况</li>
<li>优化首屏加载速度</li>
<li>适配不同机型</li>
</ul>
</li>
</ol>
<h3 id="常见问题">常见问题</h3>
<p><strong>Q1: 开发时如何调试 HTTPS 请求？</strong></p>
<ul>
<li>开发者工具中可以勾选&quot;不校验合法域名&quot;</li>
<li>但上线前必须配置好合法域名</li>
</ul>
<p><strong>Q2: 如何处理跨域问题？</strong></p>
<ul>
<li>小程序不存在跨域问题</li>
<li>但需要在服务器域名白名单中</li>
</ul>
<p><strong>Q3: 小程序包大小限制？</strong></p>
<ul>
<li>主包不能超过 2MB</li>
<li>所有分包总大小不能超过 20MB（使用分包优化后可达到更大）</li>
</ul>
<p><strong>Q4: 如何快速预览真机效果？</strong></p>
<ul>
<li>点击&quot;预览&quot;生成二维码</li>
<li>微信扫码即可在手机上预览</li>
</ul>
<h2 id="总结">总结</h2>
<p>微信小程序开发涉及的流程比较多，需要耐心完成每一步。主要流程总结如下：</p>
<ol>
<li>✅ <strong>购买服务器和域名</strong>（注意选择性价比高的）</li>
<li>✅ <strong>ICP 备案</strong>（10-20 个工作日，不能与小程序备案同时进行）</li>
<li>✅ <strong>小程序备案</strong>（7-20 个工作日，个人备案注意备注&quot;给自己使用&quot;）</li>
<li>✅ <strong>小程序认证</strong>（企业认证 300 元/年，个人可不认证）</li>
<li>✅ <strong>配置开发环境</strong>（下载开发者工具，申请 AppID）</li>
<li>✅ <strong>配置服务器域名</strong>（申请 SSL 证书，配置 HTTPS）</li>
<li>✅ <strong>开发与测试</strong>（遵循开发规范，注意性能优化）</li>
<li>✅ <strong>提交审核与发布</strong>（耐心等待审核，及时处理驳回）</li>
</ol>
<p><strong>时间规划建议：</strong></p>
<ul>
<li>备案阶段：预留 1 个月时间</li>
<li>开发阶段：根据项目复杂度安排</li>
<li>审核发布：预留 1 周时间</li>
</ul>
<p><strong>预算建议（个人小程序）：</strong></p>
<ul>
<li>服务器：100-500 元/年</li>
<li>域名：10-100 元/年</li>
<li>SSL 证书：免费</li>
<li>小程序注册：免费（个人）</li>
<li>小程序认证：可选（企业 300 元/年）</li>
</ul>
<p>希望这篇文章能帮助到正在开发小程序的朋友们，祝大家开发顺利！如有问题欢迎交流讨论。</p>
]]></content:encoded>
    </item>
    <item>
      <title>阿里云轻量服务器配置Swap解决内存不足</title>
      <link>https://lifeislife.cn/posts/%E9%98%BF%E9%87%8C%E4%BA%91%E8%BD%BB%E9%87%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%85%8D%E7%BD%AEswap%E8%A7%A3%E5%86%B3%E5%86%85%E5%AD%98%E4%B8%8D%E8%B6%B3/</link>
      <pubDate>Fri, 21 Nov 2025 22:00:00 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/%E9%98%BF%E9%87%8C%E4%BA%91%E8%BD%BB%E9%87%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%85%8D%E7%BD%AEswap%E8%A7%A3%E5%86%B3%E5%86%85%E5%AD%98%E4%B8%8D%E8%B6%B3/</guid>
      <description>&lt;h2 id=&#34;问题背景&#34;&gt;问题背景&lt;/h2&gt;
&lt;p&gt;在使用阿里云轻量服务器时，经常会遇到一个令人头疼的问题：&lt;strong&gt;默认配置没有 Swap 交换空间&lt;/strong&gt;。当运行内存占用较高的应用（如编译项目、运行 CI/CD 流程、部署应用等）时，服务器经常会因为内存不足而卡死或无响应。&lt;/p&gt;
&lt;p&gt;我在每次执行 Deploy 部署操作时都会遇到这个问题，机器直接卡死，无法继续工作。通过配置 Swap 交换空间，成功缓解了这一问题，现在运行稳定多了。&lt;/p&gt;
&lt;h2 id=&#34;什么是-swap&#34;&gt;什么是 Swap？&lt;/h2&gt;
&lt;p&gt;Swap（交换空间）是 Linux 系统中的一种虚拟内存技术。当物理内存（RAM）不足时，系统会将暂时不用的数据从内存转移到硬盘上的 Swap 空间，从而释放内存供其他程序使用。虽然 Swap 的读写速度比物理内存慢，但它可以有效防止系统因内存不足而崩溃。&lt;/p&gt;
&lt;h2 id=&#34;配置步骤&#34;&gt;配置步骤&lt;/h2&gt;
&lt;h3 id=&#34;1-检查当前-swap-状态&#34;&gt;1. 检查当前 Swap 状态&lt;/h3&gt;
&lt;p&gt;首先，我们需要确认系统当前是否已经配置了 Swap：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;free -h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;swapon --show
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果 &lt;code&gt;Swap&lt;/code&gt; 行显示为 0，说明系统没有配置交换空间。&lt;/p&gt;
&lt;h3 id=&#34;2-创建-swap-文件&#34;&gt;2. 创建 Swap 文件&lt;/h3&gt;
&lt;p&gt;根据服务器内存大小，建议创建与内存容量相等或稍大的 Swap 空间。以 2GB 内存的服务器为例，创建 2GB 的 Swap 文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 方法一：使用 fallocate 快速创建（推荐）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo fallocate -l 2G /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 方法二：如果 fallocate 命令不可用，使用 dd 命令&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dd &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/zero &lt;span class=&#34;nv&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/swapfile &lt;span class=&#34;nv&#34;&gt;bs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1G &lt;span class=&#34;nv&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;说明&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/swapfile&lt;/code&gt; 是 Swap 文件的路径，可以根据需要修改&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2G&lt;/code&gt; 表示创建 2GB 大小的文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fallocate&lt;/code&gt; 命令速度更快，优先使用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-设置文件权限&#34;&gt;3. 设置文件权限&lt;/h3&gt;
&lt;p&gt;为了安全，Swap 文件应该只允许 root 用户读写：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod &lt;span class=&#34;m&#34;&gt;600&lt;/span&gt; /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;4-格式化为-swap-格式&#34;&gt;4. 格式化为 Swap 格式&lt;/h3&gt;
&lt;p&gt;将创建的文件格式化为 Swap 交换空间：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mkswap /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;5-启用-swap&#34;&gt;5. 启用 Swap&lt;/h3&gt;
&lt;p&gt;激活刚刚创建的 Swap 文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo swapon /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;6-验证-swap-是否生效&#34;&gt;6. 验证 Swap 是否生效&lt;/h3&gt;
&lt;p&gt;再次查看内存和 Swap 状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;free -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              total        used        free      shared  buff/cache   available
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Mem:           2.0G        1.2G        100M         10M        700M        600M
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Swap:          2.0G          0B        2.0G
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到 Swap 一行已经显示 2.0G，说明配置成功。&lt;/p&gt;
&lt;h3 id=&#34;7-配置开机自动挂载&#34;&gt;7. 配置开机自动挂载&lt;/h3&gt;
&lt;p&gt;为了让 Swap 在系统重启后自动生效，需要将其写入 &lt;code&gt;/etc/fstab&lt;/code&gt; 文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/swapfile none swap sw 0 0&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee -a /etc/fstab
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证 &lt;code&gt;/etc/fstab&lt;/code&gt; 配置是否正确：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/fstab
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;优化建议&#34;&gt;优化建议&lt;/h2&gt;
&lt;h3 id=&#34;调整-swappiness-参数可选&#34;&gt;调整 Swappiness 参数（可选）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;swappiness&lt;/code&gt; 参数控制系统使用 Swap 的积极程度，取值范围 0-100：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0&lt;/strong&gt;：尽可能不使用 Swap，只在内存完全不足时使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;60&lt;/strong&gt;（默认值）：平衡使用物理内存和 Swap&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100&lt;/strong&gt;：积极使用 Swap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于内存较小的服务器，建议将 &lt;code&gt;swappiness&lt;/code&gt; 设置为 10-30，避免过度使用 Swap 导致性能下降：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看当前 swappiness 值&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /proc/sys/vm/swappiness
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 临时修改（重启后失效）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo sysctl vm.swappiness&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 永久修改&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;vm.swappiness=10&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee -a /etc/sysctl.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo sysctl -p
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;注意事项&#34;&gt;注意事项&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;硬盘空间占用&lt;/strong&gt;：Swap 文件会占用硬盘空间，创建前请确保有足够的磁盘空间&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能影响&lt;/strong&gt;：虽然 Swap 可以缓解内存不足问题，但过度使用会导致系统变慢，因为硬盘读写速度远低于内存&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;监控使用情况&lt;/strong&gt;：建议定期监控 Swap 的使用情况，如果经常大量使用 Swap，说明需要升级服务器配置或优化应用&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;监控-swap-使用情况&#34;&gt;监控 Swap 使用情况&lt;/h2&gt;
&lt;h3 id=&#34;实时查看内存和-swap-状态&#34;&gt;实时查看内存和 Swap 状态&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看内存使用情况&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;free -h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 持续监控（每 2 秒刷新一次）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;watch -n &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; free -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;查看哪些进程在使用-swap&#34;&gt;查看哪些进程在使用 Swap&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; file in /proc/*/status &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;/VmSwap|Name/{printf $2 &amp;#34; &amp;#34; $3}END{ print &amp;#34;&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;done&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sort -k &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; -n -r &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -10
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;删除-swap如需移除&#34;&gt;删除 Swap（如需移除）&lt;/h2&gt;
&lt;p&gt;如果以后需要删除 Swap 配置，可以执行以下步骤：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 1. 关闭 Swap&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo swapoff /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 2. 从 /etc/fstab 中删除对应行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo sed -i &lt;span class=&#34;s1&#34;&gt;&amp;#39;/\/swapfile/d&amp;#39;&lt;/span&gt; /etc/fstab
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 3. 删除 Swap 文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo rm /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;配置 Swap 是解决小内存服务器运行大型应用的有效方案。虽然不能完全替代物理内存，但可以在一定程度上缓解内存不足导致的系统卡死问题。对于阿里云轻量服务器这类默认没有 Swap 的环境，强烈建议配置 Swap 以提高系统稳定性。&lt;/p&gt;
&lt;p&gt;当然，如果应用长期大量使用 Swap，最根本的解决方案还是&lt;strong&gt;升级服务器配置或优化应用程序的内存使用&lt;/strong&gt;。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="问题背景">问题背景</h2>
<p>在使用阿里云轻量服务器时，经常会遇到一个令人头疼的问题：<strong>默认配置没有 Swap 交换空间</strong>。当运行内存占用较高的应用（如编译项目、运行 CI/CD 流程、部署应用等）时，服务器经常会因为内存不足而卡死或无响应。</p>
<p>我在每次执行 Deploy 部署操作时都会遇到这个问题，机器直接卡死，无法继续工作。通过配置 Swap 交换空间，成功缓解了这一问题，现在运行稳定多了。</p>
<h2 id="什么是-swap">什么是 Swap？</h2>
<p>Swap（交换空间）是 Linux 系统中的一种虚拟内存技术。当物理内存（RAM）不足时，系统会将暂时不用的数据从内存转移到硬盘上的 Swap 空间，从而释放内存供其他程序使用。虽然 Swap 的读写速度比物理内存慢，但它可以有效防止系统因内存不足而崩溃。</p>
<h2 id="配置步骤">配置步骤</h2>
<h3 id="1-检查当前-swap-状态">1. 检查当前 Swap 状态</h3>
<p>首先，我们需要确认系统当前是否已经配置了 Swap：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">free -h
</span></span><span class="line"><span class="cl">swapon --show
</span></span></code></pre></div><p>如果 <code>Swap</code> 行显示为 0，说明系统没有配置交换空间。</p>
<h3 id="2-创建-swap-文件">2. 创建 Swap 文件</h3>
<p>根据服务器内存大小，建议创建与内存容量相等或稍大的 Swap 空间。以 2GB 内存的服务器为例，创建 2GB 的 Swap 文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 方法一：使用 fallocate 快速创建（推荐）</span>
</span></span><span class="line"><span class="cl">sudo fallocate -l 2G /swapfile
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 方法二：如果 fallocate 命令不可用，使用 dd 命令</span>
</span></span><span class="line"><span class="cl">sudo dd <span class="k">if</span><span class="o">=</span>/dev/zero <span class="nv">of</span><span class="o">=</span>/swapfile <span class="nv">bs</span><span class="o">=</span>1G <span class="nv">count</span><span class="o">=</span><span class="m">2</span>
</span></span></code></pre></div><p><strong>说明</strong>：</p>
<ul>
<li><code>/swapfile</code> 是 Swap 文件的路径，可以根据需要修改</li>
<li><code>2G</code> 表示创建 2GB 大小的文件</li>
<li><code>fallocate</code> 命令速度更快，优先使用</li>
</ul>
<h3 id="3-设置文件权限">3. 设置文件权限</h3>
<p>为了安全，Swap 文件应该只允许 root 用户读写：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo chmod <span class="m">600</span> /swapfile
</span></span></code></pre></div><h3 id="4-格式化为-swap-格式">4. 格式化为 Swap 格式</h3>
<p>将创建的文件格式化为 Swap 交换空间：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo mkswap /swapfile
</span></span></code></pre></div><h3 id="5-启用-swap">5. 启用 Swap</h3>
<p>激活刚刚创建的 Swap 文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo swapon /swapfile
</span></span></code></pre></div><h3 id="6-验证-swap-是否生效">6. 验证 Swap 是否生效</h3>
<p>再次查看内存和 Swap 状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">free -h
</span></span></code></pre></div><p>输出示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">              total        used        free      shared  buff/cache   available
</span></span><span class="line"><span class="cl">Mem:           2.0G        1.2G        100M         10M        700M        600M
</span></span><span class="line"><span class="cl">Swap:          2.0G          0B        2.0G
</span></span></code></pre></div><p>可以看到 Swap 一行已经显示 2.0G，说明配置成功。</p>
<h3 id="7-配置开机自动挂载">7. 配置开机自动挂载</h3>
<p>为了让 Swap 在系统重启后自动生效，需要将其写入 <code>/etc/fstab</code> 文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;/swapfile none swap sw 0 0&#39;</span> <span class="p">|</span> sudo tee -a /etc/fstab
</span></span></code></pre></div><p>验证 <code>/etc/fstab</code> 配置是否正确：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cat /etc/fstab
</span></span></code></pre></div><h2 id="优化建议">优化建议</h2>
<h3 id="调整-swappiness-参数可选">调整 Swappiness 参数（可选）</h3>
<p><code>swappiness</code> 参数控制系统使用 Swap 的积极程度，取值范围 0-100：</p>
<ul>
<li><strong>0</strong>：尽可能不使用 Swap，只在内存完全不足时使用</li>
<li><strong>60</strong>（默认值）：平衡使用物理内存和 Swap</li>
<li><strong>100</strong>：积极使用 Swap</li>
</ul>
<p>对于内存较小的服务器，建议将 <code>swappiness</code> 设置为 10-30，避免过度使用 Swap 导致性能下降：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看当前 swappiness 值</span>
</span></span><span class="line"><span class="cl">cat /proc/sys/vm/swappiness
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 临时修改（重启后失效）</span>
</span></span><span class="line"><span class="cl">sudo sysctl vm.swappiness<span class="o">=</span><span class="m">10</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 永久修改</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;vm.swappiness=10&#39;</span> <span class="p">|</span> sudo tee -a /etc/sysctl.conf
</span></span><span class="line"><span class="cl">sudo sysctl -p
</span></span></code></pre></div><h2 id="注意事项">注意事项</h2>
<ol>
<li><strong>硬盘空间占用</strong>：Swap 文件会占用硬盘空间，创建前请确保有足够的磁盘空间</li>
<li><strong>性能影响</strong>：虽然 Swap 可以缓解内存不足问题，但过度使用会导致系统变慢，因为硬盘读写速度远低于内存</li>
<li><strong>监控使用情况</strong>：建议定期监控 Swap 的使用情况，如果经常大量使用 Swap，说明需要升级服务器配置或优化应用</li>
</ol>
<h2 id="监控-swap-使用情况">监控 Swap 使用情况</h2>
<h3 id="实时查看内存和-swap-状态">实时查看内存和 Swap 状态</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看内存使用情况</span>
</span></span><span class="line"><span class="cl">free -h
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 持续监控（每 2 秒刷新一次）</span>
</span></span><span class="line"><span class="cl">watch -n <span class="m">2</span> free -h
</span></span></code></pre></div><h3 id="查看哪些进程在使用-swap">查看哪些进程在使用 Swap</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">for</span> file in /proc/*/status <span class="p">;</span> <span class="k">do</span> awk <span class="s1">&#39;/VmSwap|Name/{printf $2 &#34; &#34; $3}END{ print &#34;&#34;}&#39;</span> <span class="nv">$file</span><span class="p">;</span> <span class="k">done</span> <span class="p">|</span> sort -k <span class="m">2</span> -n -r <span class="p">|</span> head -10
</span></span></code></pre></div><h2 id="删除-swap如需移除">删除 Swap（如需移除）</h2>
<p>如果以后需要删除 Swap 配置，可以执行以下步骤：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. 关闭 Swap</span>
</span></span><span class="line"><span class="cl">sudo swapoff /swapfile
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 从 /etc/fstab 中删除对应行</span>
</span></span><span class="line"><span class="cl">sudo sed -i <span class="s1">&#39;/\/swapfile/d&#39;</span> /etc/fstab
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. 删除 Swap 文件</span>
</span></span><span class="line"><span class="cl">sudo rm /swapfile
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>配置 Swap 是解决小内存服务器运行大型应用的有效方案。虽然不能完全替代物理内存，但可以在一定程度上缓解内存不足导致的系统卡死问题。对于阿里云轻量服务器这类默认没有 Swap 的环境，强烈建议配置 Swap 以提高系统稳定性。</p>
<p>当然，如果应用长期大量使用 Swap，最根本的解决方案还是<strong>升级服务器配置或优化应用程序的内存使用</strong>。</p>
]]></content:encoded>
    </item>
    <item>
      <title>OneDrive API 申请配置指南 - 实现网盘同步功能</title>
      <link>https://lifeislife.cn/posts/onedrive-api%E7%94%B3%E8%AF%B7%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</link>
      <pubDate>Fri, 21 Nov 2025 10:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/onedrive-api%E7%94%B3%E8%AF%B7%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</guid>
      <description>&lt;p&gt;在开发应用时,我们经常需要实现数据同步功能。例如我之前开发的表单填写工具(QuickFillForm),如果不保存到网盘,换个浏览器数据就丢失了;但如果接入 OneDrive 同步,用户就能在各个设备上无缝使用。本文将以这个实际案例为例,详细说明如何申请和配置 OneDrive API,帮助开发者快速实现网盘同步功能。&lt;/p&gt;
&lt;h2 id=&#34;应用场景&#34;&gt;应用场景&lt;/h2&gt;
&lt;p&gt;OneDrive API 适用于以下开发场景:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;跨设备数据同步&lt;/strong&gt;: 浏览器插件、桌面应用的配置和数据同步&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件备份与恢复&lt;/strong&gt;: 自动备份用户数据到云端,支持一键恢复&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协作与分享&lt;/strong&gt;: 实现文件共享、多人协作编辑功能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;离线访问&lt;/strong&gt;: 支持离线读取数据,在线自动同步&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;前置准备&#34;&gt;前置准备&lt;/h2&gt;
&lt;p&gt;在开始之前,你需要准备:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ Microsoft 账号(个人账号或工作/学校账号均可)&lt;/li&gt;
&lt;li&gt;✅ 开发环境(本文以浏览器插件为例)&lt;/li&gt;
&lt;li&gt;✅ 基础的 JavaScript/API 调用知识&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;提示&lt;/strong&gt;: OneDrive API 提供了免费的开发者配额,个人应用完全够用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第一步-注册-azure-应用&#34;&gt;第一步: 注册 Azure 应用&lt;/h2&gt;
&lt;h3 id=&#34;11-访问-azure-portal&#34;&gt;1.1 访问 Azure Portal&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开浏览器,访问 &lt;a href=&#34;https://portal.azure.com/&#34;&gt;Azure Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;使用你的 Microsoft 账号登录
&lt;ul&gt;
&lt;li&gt;个人账号(如 Outlook、Hotmail 邮箱)&lt;/li&gt;
&lt;li&gt;工作或学校账号(如公司/学校提供的 Office 365 账号)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果没有 Azure 账号,可以免费注册&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;注意&lt;/strong&gt;: 即使没有 Azure 订阅,也可以注册应用。免费账号足够用于开发测试。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;12-进入应用注册页面&#34;&gt;1.2 进入应用注册页面&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在 Azure Portal 首页的搜索栏中输入 &lt;strong&gt;&amp;ldquo;Azure Active Directory&amp;rdquo;&lt;/strong&gt; 或 &lt;strong&gt;&amp;ldquo;Microsoft Entra ID&amp;rdquo;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;点击搜索结果中的 &lt;strong&gt;Azure Active Directory&lt;/strong&gt; 服务&lt;/li&gt;
&lt;li&gt;在左侧菜单中找到 &lt;strong&gt;&amp;ldquo;应用注册&amp;rdquo;&lt;/strong&gt; (App registrations)&lt;/li&gt;
&lt;li&gt;点击顶部的 &lt;strong&gt;&amp;quot;+ 新注册&amp;quot;&lt;/strong&gt; (New registration) 按钮&lt;/li&gt;
&lt;/ol&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://docs.microsoft.com/zh-cn/azure/active-directory/develop/media/quickstart-register-app/portal-02-app-reg-01.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://docs.microsoft.com/zh-cn/azure/active-directory/develop/media/quickstart-register-app/portal-02-app-reg-01.png&#34; alt=&#34;Azure AD 应用注册入口&#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;13-填写应用基本信息&#34;&gt;1.3 填写应用基本信息&lt;/h3&gt;
&lt;p&gt;在应用注册页面,需要填写以下关键信息:&lt;/p&gt;
&lt;h4 id=&#34;应用名称&#34;&gt;应用名称&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;字段&lt;/strong&gt;: 名称 (Name)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;示例&lt;/strong&gt;: &lt;code&gt;QuickFillForm Extension&lt;/code&gt; 或 &lt;code&gt;我的数据同步应用&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;说明&lt;/strong&gt;: 这是应用的显示名称,用户授权时会看到此名称&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;支持的账户类型&#34;&gt;支持的账户类型&lt;/h4&gt;
&lt;p&gt;选择应用的用户范围,推荐选择:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;任何组织目录(任何 Azure AD 目录 - 多租户)中的帐户和个人 Microsoft 帐户(例如,Skype、Xbox)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;英文选项: &lt;strong&gt;Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;为什么选择这个选项?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持个人 Microsoft 账号(最广泛的用户群)&lt;/li&gt;
&lt;li&gt;支持企业和学校账号&lt;/li&gt;
&lt;li&gt;不限制组织,任何人都可以使用你的应用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;其他选项说明:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;仅此组织目录&lt;/strong&gt;: 仅限你的组织内部使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任何组织目录&lt;/strong&gt;: 仅限企业/学校账号,不支持个人账号&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;仅个人 Microsoft 账户&lt;/strong&gt;: 仅支持个人账号&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;重定向-uri-redirect-uri&#34;&gt;重定向 URI (Redirect URI)&lt;/h4&gt;
&lt;p&gt;这是 OAuth 认证完成后的回调地址,不同应用类型配置不同:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;浏览器插件 (Chrome/Edge):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平台类型: &lt;strong&gt;Web&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;URI 格式: &lt;code&gt;https://&amp;lt;YOUR_EXTENSION_ID&amp;gt;.chromiumapp.org/onedrive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;临时示例: &lt;code&gt;https://abcdefghijklmnopqrstuvwxyz123456.chromiumapp.org/onedrive&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Web 应用:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平台类型: &lt;strong&gt;Web&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;URI 格式: &lt;code&gt;https://yourdomain.com/callback&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;本地开发: &lt;code&gt;http://localhost:3000/callback&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;桌面应用:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平台类型: &lt;strong&gt;公共客户端/本机&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;URI 格式: &lt;code&gt;https://login.microsoftonline.com/common/oauth2/nativeclient&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;重要&lt;/strong&gt;: 浏览器插件的 Extension ID 需要在插件安装后才能获取,稍后我们会更新这个配置。可以先填写一个临时值,或者留空后面再添加。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;14-完成注册&#34;&gt;1.4 完成注册&lt;/h3&gt;
&lt;p&gt;确认信息无误后,点击页面底部的 &lt;strong&gt;&amp;ldquo;注册&amp;rdquo;&lt;/strong&gt; (Register) 按钮。&lt;/p&gt;
&lt;p&gt;注册成功后,会自动跳转到应用的 &lt;strong&gt;概述 (Overview)&lt;/strong&gt; 页面,这里显示了应用的基本信息和重要 ID。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第二步-配置-api-权限&#34;&gt;第二步: 配置 API 权限&lt;/h2&gt;
&lt;p&gt;注册完应用后,需要为应用添加访问 OneDrive 的权限。&lt;/p&gt;
&lt;h3 id=&#34;21-进入-api-权限页面&#34;&gt;2.1 进入 API 权限页面&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在应用详情页面的左侧菜单中,点击 &lt;strong&gt;&amp;ldquo;API 权限&amp;rdquo;&lt;/strong&gt; (API permissions)&lt;/li&gt;
&lt;li&gt;你会看到默认已经添加了 &lt;code&gt;User.Read&lt;/code&gt; 权限(这是用于读取用户基本信息的权限)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;22-添加-microsoft-graph-权限&#34;&gt;2.2 添加 Microsoft Graph 权限&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击页面中的 &lt;strong&gt;&amp;quot;+ 添加权限&amp;quot;&lt;/strong&gt; (Add a permission) 按钮&lt;/li&gt;
&lt;li&gt;在右侧弹出的面板中,点击 &lt;strong&gt;&amp;ldquo;Microsoft Graph&amp;rdquo;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;选择 &lt;strong&gt;&amp;ldquo;委托的权限&amp;rdquo;&lt;/strong&gt; (Delegated permissions)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;权限类型说明:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;委托的权限 (Delegated permissions)&lt;/strong&gt;: 应用代表已登录用户访问数据,需要用户授权&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用程序权限 (Application permissions)&lt;/strong&gt;: 应用以自己的身份访问数据,无需用户登录,需要管理员同意&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;23-选择所需权限&#34;&gt;2.3 选择所需权限&lt;/h3&gt;
&lt;p&gt;在搜索框中搜索并勾选以下权限:&lt;/p&gt;
&lt;h4 id=&#34;filesreadwriteappfolder-推荐&#34;&gt;Files.ReadWrite.AppFolder (推荐)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;权限名称&lt;/strong&gt;: &lt;code&gt;Files.ReadWrite.AppFolder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权限级别&lt;/strong&gt;: 委托的权限&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;: 允许应用读写应用专用文件夹中的文件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;: 最安全的权限,应用只能访问自己创建的 &lt;code&gt;Apps/&amp;lt;AppName&amp;gt;&lt;/code&gt; 文件夹&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;: 大多数应用数据同步场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例存储路径:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OneDrive/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── Apps/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── QuickFillForm/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ├── quickfillform-data.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        └── backup/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;offline_access-必需&#34;&gt;offline_access (必需)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;权限名称&lt;/strong&gt;: &lt;code&gt;offline_access&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权限级别&lt;/strong&gt;: 委托的权限&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;: 允许应用在用户离线时访问数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用途&lt;/strong&gt;: 获取刷新令牌(refresh token),在访问令牌过期后自动续期&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;filesreadwriteall-可选不推荐&#34;&gt;Files.ReadWrite.All (可选,不推荐)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;权限名称&lt;/strong&gt;: &lt;code&gt;Files.ReadWrite.All&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权限级别&lt;/strong&gt;: 委托的权限&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;: 允许应用读写用户的所有文件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;风险&lt;/strong&gt;: 权限范围过大,可能引起用户担忧&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;: 需要访问用户整个 OneDrive 的应用(如文件管理器)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;24-确认添加权限&#34;&gt;2.4 确认添加权限&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;勾选好权限后,点击底部的 &lt;strong&gt;&amp;ldquo;添加权限&amp;rdquo;&lt;/strong&gt; (Add permissions) 按钮&lt;/li&gt;
&lt;li&gt;返回 API 权限页面,确认列表中显示以下权限:
&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;User.Read&lt;/code&gt; (默认)&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;Files.ReadWrite.AppFolder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;offline_access&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;25-授予管理员同意-可选&#34;&gt;2.5 授予管理员同意 (可选)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果你是组织管理员,可以点击 &lt;strong&gt;&amp;ldquo;为 [组织名称] 授予管理员同意&amp;rdquo;&lt;/strong&gt; 按钮&lt;/li&gt;
&lt;li&gt;对于个人账号或公共应用,此步骤可以跳过&lt;/li&gt;
&lt;li&gt;用户首次使用时会看到授权页面,点击&amp;quot;接受&amp;quot;即可&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第三步-获取应用凭据&#34;&gt;第三步: 获取应用凭据&lt;/h2&gt;
&lt;h3 id=&#34;31-复制-client-id-应用程序-id&#34;&gt;3.1 复制 Client ID (应用程序 ID)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在应用详情页面的 &lt;strong&gt;&amp;ldquo;概述&amp;rdquo;&lt;/strong&gt; (Overview) 选项卡中&lt;/li&gt;
&lt;li&gt;找到 &lt;strong&gt;&amp;ldquo;应用程序(客户端) ID&amp;rdquo;&lt;/strong&gt; (Application (client) ID)&lt;/li&gt;
&lt;li&gt;这是一个 GUID 格式的字符串,类似:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;12345678-1234-1234-1234-123456789abc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;点击旁边的复制图标,将 Client ID 保存到安全的地方&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;🔒 &lt;strong&gt;安全提示&lt;/strong&gt;: Client ID 是公开信息,可以在前端代码中使用。但不要将它与 Client Secret 混淆。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;32-是否需要-client-secret&#34;&gt;3.2 是否需要 Client Secret?&lt;/h3&gt;
&lt;p&gt;对于不同类型的应用,Client Secret 的需求不同:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;应用类型&lt;/th&gt;
          &lt;th&gt;是否需要 Client Secret&lt;/th&gt;
          &lt;th&gt;原因&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;浏览器插件&lt;/td&gt;
          &lt;td&gt;❌ 不需要&lt;/td&gt;
          &lt;td&gt;使用隐式授权流程或 PKCE&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;单页应用 (SPA)&lt;/td&gt;
          &lt;td&gt;❌ 不需要&lt;/td&gt;
          &lt;td&gt;前端代码无法安全存储密钥&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Web 应用 (有后端)&lt;/td&gt;
          &lt;td&gt;✅ 需要&lt;/td&gt;
          &lt;td&gt;在服务器端安全存储&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;桌面/移动应用&lt;/td&gt;
          &lt;td&gt;❌ 不需要&lt;/td&gt;
          &lt;td&gt;使用 PKCE 授权流程&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;如果需要 Client Secret,创建方法如下:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在左侧菜单中点击 &lt;strong&gt;&amp;ldquo;证书和密码&amp;rdquo;&lt;/strong&gt; (Certificates &amp;amp; secrets)&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;&amp;quot;+ 新客户端密码&amp;quot;&lt;/strong&gt; (New client secret)&lt;/li&gt;
&lt;li&gt;输入描述(如 &amp;ldquo;生产环境密钥&amp;rdquo;),选择过期时间&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;&amp;ldquo;添加&amp;rdquo;&lt;/strong&gt; (Add)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;立即复制密钥值&lt;/strong&gt;,此密钥只显示一次&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;警告&lt;/strong&gt;: Client Secret 是敏感信息,必须妥善保管,不能提交到代码仓库。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;33-配置身份验证设置&#34;&gt;3.3 配置身份验证设置&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在左侧菜单点击 &lt;strong&gt;&amp;ldquo;身份验证&amp;rdquo;&lt;/strong&gt; (Authentication)&lt;/li&gt;
&lt;li&gt;确认 &lt;strong&gt;重定向 URI&lt;/strong&gt; 已正确添加&lt;/li&gt;
&lt;li&gt;向下滚动到 &lt;strong&gt;&amp;ldquo;隐式授权和混合流&amp;rdquo;&lt;/strong&gt; (Implicit grant and hybrid flows) 部分&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;对于浏览器插件或 SPA,需要启用:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ 勾选 &lt;strong&gt;&amp;ldquo;访问令牌&amp;rdquo;&lt;/strong&gt; (Access tokens)&lt;/li&gt;
&lt;li&gt;✅ 勾选 &lt;strong&gt;&amp;ldquo;ID 令牌&amp;rdquo;&lt;/strong&gt; (ID tokens)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对于使用授权码流程的应用,可以不勾选上述选项&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;点击页面底部的 &lt;strong&gt;&amp;ldquo;保存&amp;rdquo;&lt;/strong&gt; (Save) 按钮&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第四步-配置应用代码&#34;&gt;第四步: 配置应用代码&lt;/h2&gt;
&lt;p&gt;以浏览器插件为例,说明如何在代码中配置 OneDrive API。&lt;/p&gt;
&lt;h3 id=&#34;41-获取浏览器插件的-extension-id&#34;&gt;4.1 获取浏览器插件的 Extension ID&lt;/h3&gt;
&lt;p&gt;如果你正在开发浏览器插件:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开 Chrome 或 Edge 浏览器&lt;/li&gt;
&lt;li&gt;访问 &lt;code&gt;chrome://extensions/&lt;/code&gt; (Chrome) 或 &lt;code&gt;edge://extensions/&lt;/code&gt; (Edge)&lt;/li&gt;
&lt;li&gt;开启右上角的 &lt;strong&gt;&amp;ldquo;开发者模式&amp;rdquo;&lt;/strong&gt; (Developer mode)&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;&amp;ldquo;加载已解压的扩展程序&amp;rdquo;&lt;/strong&gt; (Load unpacked)&lt;/li&gt;
&lt;li&gt;选择你的插件项目文件夹&lt;/li&gt;
&lt;li&gt;安装成功后,在插件卡片上找到 &lt;strong&gt;&amp;ldquo;ID&amp;rdquo;&lt;/strong&gt; 字段,复制扩展 ID
&lt;ul&gt;
&lt;li&gt;类似: &lt;code&gt;abcdefghijklmnopqrstuvwxyz123456&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;42-更新-azure-重定向-uri&#34;&gt;4.2 更新 Azure 重定向 URI&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;返回 Azure Portal 的应用注册页面&lt;/li&gt;
&lt;li&gt;进入 &lt;strong&gt;&amp;ldquo;身份验证&amp;rdquo;&lt;/strong&gt; (Authentication)&lt;/li&gt;
&lt;li&gt;更新或添加重定向 URI:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://abcdefghijklmnopqrstuvwxyz123456.chromiumapp.org/onedrive
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;将 &lt;code&gt;abcdefghijklmnopqrstuvwxyz123456&lt;/code&gt; 替换为实际的扩展 ID&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;&amp;ldquo;保存&amp;rdquo;&lt;/strong&gt; (Save)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;43-在代码中配置-client-id&#34;&gt;4.3 在代码中配置 Client ID&lt;/h3&gt;
&lt;p&gt;以 QuickFillForm 插件为例,在 &lt;code&gt;onedrive-sync.js&lt;/code&gt; 文件中配置:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OneDriveSync&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 替换为你的 Azure 应用程序 ID (Client ID)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clientId&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;12345678-1234-1234-1234-123456789abc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 获取当前插件的 ID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redirectUri&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`https://&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;chrome&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;.chromiumapp.org/onedrive`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// Microsoft Graph API 端点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;graphEndpoint&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://graph.microsoft.com/v1.0&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 应用专用文件夹路径
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;appFolderPath&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/me/drive/special/approot&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 请求的权限范围
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scopes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;Files.ReadWrite.AppFolder&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;offline_access&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 获取访问令牌
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;reject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;chrome&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;identity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;launchWebAuthFlow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getAuthUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;interactive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redirectUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;chrome&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;nx&#34;&gt;reject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;chrome&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;c1&#34;&gt;// 从回调 URL 中提取 access_token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redirectUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;/access_token=([^&amp;amp;]*)/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;nx&#34;&gt;reject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;无法获取访问令牌&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 构建授权 URL
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;getAuthUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;client_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clientId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;response_type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;token&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;redirect_uri&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redirectUri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;scope&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scopes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;response_mode&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;fragment&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`https://login.microsoftonline.com/common/oauth2/v2.0/authorize?&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 上传文件到 OneDrive
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;uploadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;graphEndpoint&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}${&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;appFolderPath&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:/content`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`Bearer &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stringify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`上传失败: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;statusText&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 从 OneDrive 下载文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;downloadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;graphEndpoint&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}${&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;appFolderPath&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:/content`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`Bearer &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`下载失败: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;statusText&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 使用示例
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OneDriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 上传数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;uploadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;data.json&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;value&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;上传成功&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;上传失败:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 下载数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;downloadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;data.json&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;下载的数据:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;下载失败:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;44-web-应用配置示例&#34;&gt;4.4 Web 应用配置示例&lt;/h3&gt;
&lt;p&gt;对于传统 Web 应用(有后端),使用授权码流程更安全:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 前端代码
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;loginWithOneDrive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;clientId&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;YOUR_CLIENT_ID&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;redirectUri&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://yourdomain.com/callback&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scopes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Files.ReadWrite.AppFolder offline_access&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;authUrl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`https://login.microsoftonline.com/common/oauth2/v2.0/authorize?`&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;sb&#34;&gt;`client_id=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clientId&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;sb&#34;&gt;`&amp;amp;response_type=code`&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;sb&#34;&gt;`&amp;amp;redirect_uri=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;encodeURIComponent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redirectUri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;sb&#34;&gt;`&amp;amp;scope=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;encodeURIComponent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scopes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;location&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;href&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;authUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 后端代码 (Node.js Express 示例)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/callback&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 用授权码换取访问令牌
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tokenResponse&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;https://login.microsoftonline.com/common/oauth2/v2.0/token&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;application/x-www-form-urlencoded&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;client_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;CLIENT_ID&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;client_secret&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;redirect_uri&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://yourdomain.com/callback&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;grant_type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;authorization_code&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tokenData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tokenResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 保存 access_token 和 refresh_token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tokenData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;access_token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refreshToken&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tokenData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refresh_token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/dashboard&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;45-配置-manifestjson-浏览器插件&#34;&gt;4.5 配置 manifest.json (浏览器插件)&lt;/h3&gt;
&lt;p&gt;在插件的 &lt;code&gt;manifest.json&lt;/code&gt; 中添加必要的权限:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;manifest_version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;QuickFillForm&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;permissions&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;identity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;storage&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;host_permissions&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://login.microsoftonline.com/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://graph.microsoft.com/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;oauth2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;12345678-1234-1234-1234-123456789abc.chromiumapp.org&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;scopes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;Files.ReadWrite.AppFolder&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;offline_access&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;第五步-测试同步功能&#34;&gt;第五步: 测试同步功能&lt;/h2&gt;
&lt;h3 id=&#34;51-测试用户认证&#34;&gt;5.1 测试用户认证&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在应用中触发 OneDrive 登录流程&lt;/li&gt;
&lt;li&gt;浏览器会打开 Microsoft 登录页面&lt;/li&gt;
&lt;li&gt;输入 Microsoft 账号和密码登录&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;&amp;ldquo;接受&amp;rdquo;&lt;/strong&gt; (Accept) 按钮授权&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;52-测试文件上传&#34;&gt;5.2 测试文件上传&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;浏览器插件示例:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 测试上传功能
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;testUpload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;testData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;张三&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;zhangsan@example.com&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;phone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;13800138000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;uploadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;test-data.json&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;testData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;✅ 上传成功:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;❌ 上传失败:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;testUpload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;53-测试文件下载&#34;&gt;5.3 测试文件下载&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 测试下载功能
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;testDownload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;downloadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;test-data.json&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;✅ 下载成功:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;❌ 下载失败:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;testDownload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;54-验证文件存储位置&#34;&gt;5.4 验证文件存储位置&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;登录 &lt;a href=&#34;https://onedrive.live.com/&#34;&gt;OneDrive 网页版&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;在左侧菜单中找到 &lt;strong&gt;&amp;ldquo;应用&amp;rdquo;&lt;/strong&gt; (Apps) 文件夹&lt;/li&gt;
&lt;li&gt;进入你的应用文件夹,例如 &lt;code&gt;Apps/QuickFillForm/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;确认文件已成功上传&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;55-测试跨设备同步&#34;&gt;5.5 测试跨设备同步&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在设备 A 上传数据&lt;/li&gt;
&lt;li&gt;在设备 B 上登录同一 Microsoft 账号&lt;/li&gt;
&lt;li&gt;下载数据,验证是否与设备 A 一致&lt;/li&gt;
&lt;li&gt;修改数据后再次上传&lt;/li&gt;
&lt;li&gt;在设备 A 上下载,验证是否更新&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;常见问题与解决方案&#34;&gt;常见问题与解决方案&lt;/h2&gt;
&lt;h3 id=&#34;q1-无法获取访问令牌&#34;&gt;Q1: 无法获取访问令牌&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;错误信息:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AADSTS50011: The redirect URI specified in the request does not match
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因分析:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Client ID 配置错误&lt;/li&gt;
&lt;li&gt;重定向 URI 不匹配&lt;/li&gt;
&lt;li&gt;Extension ID 更改后未更新 Azure 配置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;检查代码中的 &lt;code&gt;clientId&lt;/code&gt; 是否正确复制&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 错误: 多余的空格或特殊字符
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clientId&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;12345678-1234-1234-1234-123456789abc &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 正确
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clientId&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;12345678-1234-1234-1234-123456789abc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确认 Azure 应用注册中的重定向 URI 与代码完全一致&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Azure 配置: &lt;code&gt;https://abcdefg123456.chromiumapp.org/onedrive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;代码配置: &lt;code&gt;https://abcdefg123456.chromiumapp.org/onedrive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;注意区分大小写和尾部斜杠&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保存配置后等待 2-5 分钟让配置生效&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;q2-权限被拒绝无法读写文件&#34;&gt;Q2: 权限被拒绝,无法读写文件&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;错误信息:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;403 Forbidden: Access denied
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因分析:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;权限未正确添加&lt;/li&gt;
&lt;li&gt;用户未授权&lt;/li&gt;
&lt;li&gt;使用了错误的 API 端点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 Azure Portal 检查是否已添加 &lt;code&gt;Files.ReadWrite.AppFolder&lt;/code&gt; 权限&lt;/li&gt;
&lt;li&gt;清除用户授权,重新登录并同意权限
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 清除缓存的令牌
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;chrome&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;identity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clearAllCachedAuthTokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;已清除缓存,请重新登录&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;确认使用正确的 API 路径:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 正确: 使用应用专用文件夹
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/me/drive/special/approot:/data.json:/content&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 错误: 使用根目录 (需要 Files.ReadWrite.All 权限)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;/me/drive/root:/data.json:/content&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;q3-访问令牌过期&#34;&gt;Q3: 访问令牌过期&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;错误信息:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;401 Unauthorized: The access token has expired
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因分析:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access Token 默认有效期 1 小时&lt;/li&gt;
&lt;li&gt;未实现 Refresh Token 自动刷新机制&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;实现令牌刷新逻辑:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OneDriveSync&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refreshToken&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tokenExpiry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 获取有效的访问令牌
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getValidAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 如果令牌未过期,直接返回
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tokenExpiry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 如果有刷新令牌,尝试刷新
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refreshToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refreshAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 否则重新登录
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 刷新访问令牌
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;refreshAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;https://login.microsoftonline.com/common/oauth2/v2.0/token&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;application/x-www-form-urlencoded&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;client_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;clientId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;refresh_token&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refreshToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;grant_type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;refresh_token&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;access_token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refreshToken&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refresh_token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tokenExpiry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;expires_in&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 保存到本地存储
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;chrome&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;local&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;onedrive_access_token&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;onedrive_refresh_token&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refreshToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;onedrive_token_expiry&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tokenExpiry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;q4-找不到扩展-id&#34;&gt;Q4: 找不到扩展 ID&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;解决方案:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;访问浏览器的扩展管理页面:
&lt;ul&gt;
&lt;li&gt;Chrome: &lt;code&gt;chrome://extensions/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Edge: &lt;code&gt;edge://extensions/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;确保已开启&amp;quot;开发者模式&amp;quot;&lt;/li&gt;
&lt;li&gt;Extension ID 显示在扩展卡片上,格式类似:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ID: abcdefghijklmnopqrstuvwxyz123456
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;如果是已发布的扩展,可以在 Chrome Web Store 的 URL 中找到:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://chrome.google.com/webstore/detail/extension-name/abcdefghijklmnopqrstuvwxyz123456
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;q5-文件上传成功但在-onedrive-中找不到&#34;&gt;Q5: 文件上传成功但在 OneDrive 中找不到&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;原因分析:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用了应用专用文件夹,路径在 &lt;code&gt;Apps/&amp;lt;AppName&amp;gt;/&lt;/code&gt; 下&lt;/li&gt;
&lt;li&gt;OneDrive 网页版界面可能有延迟&lt;/li&gt;
&lt;li&gt;文件名或路径错误&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 OneDrive 网页版中,导航到:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OneDrive → 应用 (Apps) → QuickFillForm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;等待 1-2 分钟,刷新页面&lt;/li&gt;
&lt;li&gt;使用 API 查询文件是否存在:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 列出应用文件夹中的所有文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;listFiles&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getValidAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://graph.microsoft.com/v1.0/me/drive/special/approot/children&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`Bearer &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;文件列表:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;q6-跨域请求被阻止-cors&#34;&gt;Q6: 跨域请求被阻止 (CORS)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;错误信息:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Access to fetch at &amp;#39;https://graph.microsoft.com/...&amp;#39; has been blocked by CORS policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因分析:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在普通网页中直接请求 Graph API&lt;/li&gt;
&lt;li&gt;未在 &lt;code&gt;manifest.json&lt;/code&gt; 中配置 &lt;code&gt;host_permissions&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方案一: 使用后端代理 (推荐)&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 前端调用后端 API
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/api/onedrive/upload&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stringify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 后端转发到 Microsoft Graph
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/api/onedrive/upload&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;https://graph.microsoft.com/v1.0/...&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`Bearer &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;accessToken&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;方案二: 浏览器插件配置&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;host_permissions&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://login.microsoftonline.com/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://graph.microsoft.com/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;q7-授权页面显示需要管理员审批&#34;&gt;Q7: 授权页面显示&amp;quot;需要管理员审批&amp;quot;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;错误信息:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AADSTS65001: The user or administrator has not consented to use the application
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因分析:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;组织策略要求管理员批准应用&lt;/li&gt;
&lt;li&gt;请求的权限需要管理员同意&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果你是管理员,在 Azure Portal 中授予管理员同意&lt;/li&gt;
&lt;li&gt;如果不是管理员,联系 IT 部门批准应用&lt;/li&gt;
&lt;li&gt;使用个人 Microsoft 账号测试(个人账号无需管理员同意)&lt;/li&gt;
&lt;li&gt;减少权限请求,只使用委托权限而不是应用权限&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;安全最佳实践&#34;&gt;安全最佳实践&lt;/h2&gt;
&lt;h3 id=&#34;1-保护-client-secret&#34;&gt;1. 保护 Client Secret&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ❌ 错误: 在前端代码中硬编码密钥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;clientSecret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;your-secret-key-123456&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ✅ 正确: 在后端使用环境变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;clientSecret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;AZURE_CLIENT_SECRET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-使用最小权限原则&#34;&gt;2. 使用最小权限原则&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ❌ 错误: 请求过多权限
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scopes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Files.ReadWrite.All&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Mail.Read&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Contacts.ReadWrite&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ✅ 正确: 只请求必需的权限
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scopes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;Files.ReadWrite.AppFolder&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;offline_access&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-安全存储令牌&#34;&gt;3. 安全存储令牌&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ✅ 浏览器插件: 使用 chrome.storage.local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;chrome&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;local&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;onedrive_token&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;encryptedToken&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 最好加密存储
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ✅ Web 应用: 使用 HttpOnly Cookie
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cookie&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;access_token&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpOnly&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;secure&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;sameSite&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;strict&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;4-定期检查应用活动&#34;&gt;4. 定期检查应用活动&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;登录 &lt;a href=&#34;https://portal.azure.com/&#34;&gt;Azure Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;进入应用注册 → 你的应用&lt;/li&gt;
&lt;li&gt;查看 &lt;strong&gt;&amp;ldquo;概述&amp;rdquo;&lt;/strong&gt; 中的使用情况统计&lt;/li&gt;
&lt;li&gt;检查异常登录和 API 调用&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;5-处理敏感数据&#34;&gt;5. 处理敏感数据&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 上传前加密敏感数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;uploadSecureData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;encrypted&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;encryptData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;uploadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;secure-data.enc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;encrypted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 下载后解密
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;downloadSecureData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;encrypted&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;downloadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;secure-data.enc&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;decryptData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;encrypted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;进阶技巧&#34;&gt;进阶技巧&lt;/h2&gt;
&lt;h3 id=&#34;1-实现文件版本管理&#34;&gt;1. 实现文件版本管理&lt;/h3&gt;
&lt;p&gt;OneDrive 自动保留文件版本历史,可以通过 API 访问:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getFileVersions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getValidAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;sb&#34;&gt;`https://graph.microsoft.com/v1.0/me/drive/special/approot:/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:/versions`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`Bearer &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 返回版本列表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-实现增量同步&#34;&gt;2. 实现增量同步&lt;/h3&gt;
&lt;p&gt;通过 Delta API 获取文件变化:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;syncChanges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;deltaLink&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getValidAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;deltaLink&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://graph.microsoft.com/v1.0/me/drive/special/approot/delta&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`Bearer &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 处理变化的文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;deleted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;文件已删除:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;文件已更新:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 保存 deltaLink 供下次使用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;@odata.deltaLink&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-实现冲突解决&#34;&gt;3. 实现冲突解决&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;smartSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 下载远程文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;remoteData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;downloadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 比较时间戳
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;remoteData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastModified&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastModified&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 远程更新,下载覆盖本地
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;download&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;remoteData&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastModified&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;remoteData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastModified&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 本地更新,上传覆盖远程
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;uploadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;upload&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 智能合并
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;merged&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mergeData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;remoteData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;uploadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;merged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;merge&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;merged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 文件不存在,直接上传
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;uploadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;create&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localData&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;4-批量操作&#34;&gt;4. 批量操作&lt;/h3&gt;
&lt;p&gt;使用批处理 API 提高性能:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;batchUpload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onedriveSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getValidAccessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;requests&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`/me/drive/special/approot:/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:/content`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;https://graph.microsoft.com/v1.0/$batch&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`Bearer &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stringify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;requests&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;相关资源&#34;&gt;相关资源&lt;/h2&gt;
&lt;h3 id=&#34;官方文档&#34;&gt;官方文档&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;📘 &lt;a href=&#34;https://docs.microsoft.com/zh-cn/graph/overview&#34;&gt;Microsoft Graph API 文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📘 &lt;a href=&#34;https://docs.microsoft.com/zh-cn/onedrive/developer/&#34;&gt;OneDrive API 参考&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📘 &lt;a href=&#34;https://docs.microsoft.com/zh-cn/azure/active-directory/develop/quickstart-register-app&#34;&gt;Azure AD 应用注册指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📘 &lt;a href=&#34;https://docs.microsoft.com/zh-cn/azure/active-directory/develop/v2-oauth2-auth-code-flow&#34;&gt;OAuth 2.0 授权流程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;开发工具&#34;&gt;开发工具&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;🛠️ &lt;a href=&#34;https://developer.microsoft.com/zh-cn/graph/graph-explorer&#34;&gt;Graph Explorer&lt;/a&gt; - 在线测试 API&lt;/li&gt;
&lt;li&gt;🛠️ &lt;a href=&#34;https://github.com/microsoftgraph&#34;&gt;Microsoft Graph SDK&lt;/a&gt; - 各语言 SDK&lt;/li&gt;
&lt;li&gt;🛠️ &lt;a href=&#34;https://www.postman.com/microsoftgraph/workspace/microsoft-graph/overview&#34;&gt;Postman Collection&lt;/a&gt; - API 调试&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;示例项目&#34;&gt;示例项目&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;💻 &lt;a href=&#34;https://github.com/OneDrive/onedrive-api-docs/blob/live/samples/javascript-picker-saver.md&#34;&gt;OneDrive JavaScript SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💻 &lt;a href=&#34;https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/api-samples/identity&#34;&gt;浏览器插件 OAuth 示例&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;社区资源&#34;&gt;社区资源&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;💬 &lt;a href=&#34;https://techcommunity.microsoft.com/t5/microsoft-graph/ct-p/MicrosoftGraph&#34;&gt;Microsoft Graph 开发者论坛&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💬 &lt;a href=&#34;https://stackoverflow.com/questions/tagged/microsoft-graph&#34;&gt;Stack Overflow - microsoft-graph&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<p>在开发应用时,我们经常需要实现数据同步功能。例如我之前开发的表单填写工具(QuickFillForm),如果不保存到网盘,换个浏览器数据就丢失了;但如果接入 OneDrive 同步,用户就能在各个设备上无缝使用。本文将以这个实际案例为例,详细说明如何申请和配置 OneDrive API,帮助开发者快速实现网盘同步功能。</p>
<h2 id="应用场景">应用场景</h2>
<p>OneDrive API 适用于以下开发场景:</p>
<ul>
<li><strong>跨设备数据同步</strong>: 浏览器插件、桌面应用的配置和数据同步</li>
<li><strong>文件备份与恢复</strong>: 自动备份用户数据到云端,支持一键恢复</li>
<li><strong>协作与分享</strong>: 实现文件共享、多人协作编辑功能</li>
<li><strong>离线访问</strong>: 支持离线读取数据,在线自动同步</li>
</ul>
<h2 id="前置准备">前置准备</h2>
<p>在开始之前,你需要准备:</p>
<ul>
<li>✅ Microsoft 账号(个人账号或工作/学校账号均可)</li>
<li>✅ 开发环境(本文以浏览器插件为例)</li>
<li>✅ 基础的 JavaScript/API 调用知识</li>
</ul>
<blockquote>
<p>💡 <strong>提示</strong>: OneDrive API 提供了免费的开发者配额,个人应用完全够用。</p>
</blockquote>
<hr>
<h2 id="第一步-注册-azure-应用">第一步: 注册 Azure 应用</h2>
<h3 id="11-访问-azure-portal">1.1 访问 Azure Portal</h3>
<ol>
<li>打开浏览器,访问 <a href="https://portal.azure.com/">Azure Portal</a></li>
<li>使用你的 Microsoft 账号登录
<ul>
<li>个人账号(如 Outlook、Hotmail 邮箱)</li>
<li>工作或学校账号(如公司/学校提供的 Office 365 账号)</li>
</ul>
</li>
<li>如果没有 Azure 账号,可以免费注册</li>
</ol>
<blockquote>
<p>📌 <strong>注意</strong>: 即使没有 Azure 订阅,也可以注册应用。免费账号足够用于开发测试。</p>
</blockquote>
<h3 id="12-进入应用注册页面">1.2 进入应用注册页面</h3>
<ol>
<li>在 Azure Portal 首页的搜索栏中输入 <strong>&ldquo;Azure Active Directory&rdquo;</strong> 或 <strong>&ldquo;Microsoft Entra ID&rdquo;</strong></li>
<li>点击搜索结果中的 <strong>Azure Active Directory</strong> 服务</li>
<li>在左侧菜单中找到 <strong>&ldquo;应用注册&rdquo;</strong> (App registrations)</li>
<li>点击顶部的 <strong>&quot;+ 新注册&quot;</strong> (New registration) 按钮</li>
</ol>
<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://docs.microsoft.com/zh-cn/azure/active-directory/develop/media/quickstart-register-app/portal-02-app-reg-01.png">
            <img class="responsive-image" src="https://docs.microsoft.com/zh-cn/azure/active-directory/develop/media/quickstart-register-app/portal-02-app-reg-01.png" alt="Azure AD 应用注册入口"  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="13-填写应用基本信息">1.3 填写应用基本信息</h3>
<p>在应用注册页面,需要填写以下关键信息:</p>
<h4 id="应用名称">应用名称</h4>
<ul>
<li><strong>字段</strong>: 名称 (Name)</li>
<li><strong>示例</strong>: <code>QuickFillForm Extension</code> 或 <code>我的数据同步应用</code></li>
<li><strong>说明</strong>: 这是应用的显示名称,用户授权时会看到此名称</li>
</ul>
<h4 id="支持的账户类型">支持的账户类型</h4>
<p>选择应用的用户范围,推荐选择:</p>
<ul>
<li>✅ <strong>任何组织目录(任何 Azure AD 目录 - 多租户)中的帐户和个人 Microsoft 帐户(例如,Skype、Xbox)</strong></li>
<li>英文选项: <strong>Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)</strong></li>
</ul>
<p><strong>为什么选择这个选项?</strong></p>
<ul>
<li>支持个人 Microsoft 账号(最广泛的用户群)</li>
<li>支持企业和学校账号</li>
<li>不限制组织,任何人都可以使用你的应用</li>
</ul>
<p><strong>其他选项说明:</strong></p>
<ul>
<li><strong>仅此组织目录</strong>: 仅限你的组织内部使用</li>
<li><strong>任何组织目录</strong>: 仅限企业/学校账号,不支持个人账号</li>
<li><strong>仅个人 Microsoft 账户</strong>: 仅支持个人账号</li>
</ul>
<h4 id="重定向-uri-redirect-uri">重定向 URI (Redirect URI)</h4>
<p>这是 OAuth 认证完成后的回调地址,不同应用类型配置不同:</p>
<p><strong>浏览器插件 (Chrome/Edge):</strong></p>
<ul>
<li>平台类型: <strong>Web</strong></li>
<li>URI 格式: <code>https://&lt;YOUR_EXTENSION_ID&gt;.chromiumapp.org/onedrive</code></li>
<li>临时示例: <code>https://abcdefghijklmnopqrstuvwxyz123456.chromiumapp.org/onedrive</code></li>
</ul>
<p><strong>Web 应用:</strong></p>
<ul>
<li>平台类型: <strong>Web</strong></li>
<li>URI 格式: <code>https://yourdomain.com/callback</code></li>
<li>本地开发: <code>http://localhost:3000/callback</code></li>
</ul>
<p><strong>桌面应用:</strong></p>
<ul>
<li>平台类型: <strong>公共客户端/本机</strong></li>
<li>URI 格式: <code>https://login.microsoftonline.com/common/oauth2/nativeclient</code></li>
</ul>
<blockquote>
<p>⚠️ <strong>重要</strong>: 浏览器插件的 Extension ID 需要在插件安装后才能获取,稍后我们会更新这个配置。可以先填写一个临时值,或者留空后面再添加。</p>
</blockquote>
<h3 id="14-完成注册">1.4 完成注册</h3>
<p>确认信息无误后,点击页面底部的 <strong>&ldquo;注册&rdquo;</strong> (Register) 按钮。</p>
<p>注册成功后,会自动跳转到应用的 <strong>概述 (Overview)</strong> 页面,这里显示了应用的基本信息和重要 ID。</p>
<hr>
<h2 id="第二步-配置-api-权限">第二步: 配置 API 权限</h2>
<p>注册完应用后,需要为应用添加访问 OneDrive 的权限。</p>
<h3 id="21-进入-api-权限页面">2.1 进入 API 权限页面</h3>
<ol>
<li>在应用详情页面的左侧菜单中,点击 <strong>&ldquo;API 权限&rdquo;</strong> (API permissions)</li>
<li>你会看到默认已经添加了 <code>User.Read</code> 权限(这是用于读取用户基本信息的权限)</li>
</ol>
<h3 id="22-添加-microsoft-graph-权限">2.2 添加 Microsoft Graph 权限</h3>
<ol>
<li>点击页面中的 <strong>&quot;+ 添加权限&quot;</strong> (Add a permission) 按钮</li>
<li>在右侧弹出的面板中,点击 <strong>&ldquo;Microsoft Graph&rdquo;</strong></li>
<li>选择 <strong>&ldquo;委托的权限&rdquo;</strong> (Delegated permissions)</li>
</ol>
<blockquote>
<p>💡 <strong>权限类型说明:</strong></p>
<ul>
<li><strong>委托的权限 (Delegated permissions)</strong>: 应用代表已登录用户访问数据,需要用户授权</li>
<li><strong>应用程序权限 (Application permissions)</strong>: 应用以自己的身份访问数据,无需用户登录,需要管理员同意</li>
</ul>
</blockquote>
<h3 id="23-选择所需权限">2.3 选择所需权限</h3>
<p>在搜索框中搜索并勾选以下权限:</p>
<h4 id="filesreadwriteappfolder-推荐">Files.ReadWrite.AppFolder (推荐)</h4>
<ul>
<li><strong>权限名称</strong>: <code>Files.ReadWrite.AppFolder</code></li>
<li><strong>权限级别</strong>: 委托的权限</li>
<li><strong>作用</strong>: 允许应用读写应用专用文件夹中的文件</li>
<li><strong>优势</strong>: 最安全的权限,应用只能访问自己创建的 <code>Apps/&lt;AppName&gt;</code> 文件夹</li>
<li><strong>适用场景</strong>: 大多数应用数据同步场景</li>
</ul>
<p><strong>示例存储路径:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">OneDrive/
</span></span><span class="line"><span class="cl">└── Apps/
</span></span><span class="line"><span class="cl">    └── QuickFillForm/
</span></span><span class="line"><span class="cl">        ├── quickfillform-data.json
</span></span><span class="line"><span class="cl">        └── backup/
</span></span></code></pre></div><h4 id="offline_access-必需">offline_access (必需)</h4>
<ul>
<li><strong>权限名称</strong>: <code>offline_access</code></li>
<li><strong>权限级别</strong>: 委托的权限</li>
<li><strong>作用</strong>: 允许应用在用户离线时访问数据</li>
<li><strong>用途</strong>: 获取刷新令牌(refresh token),在访问令牌过期后自动续期</li>
</ul>
<h4 id="filesreadwriteall-可选不推荐">Files.ReadWrite.All (可选,不推荐)</h4>
<ul>
<li><strong>权限名称</strong>: <code>Files.ReadWrite.All</code></li>
<li><strong>权限级别</strong>: 委托的权限</li>
<li><strong>作用</strong>: 允许应用读写用户的所有文件</li>
<li><strong>风险</strong>: 权限范围过大,可能引起用户担忧</li>
<li><strong>适用场景</strong>: 需要访问用户整个 OneDrive 的应用(如文件管理器)</li>
</ul>
<h3 id="24-确认添加权限">2.4 确认添加权限</h3>
<ol>
<li>勾选好权限后,点击底部的 <strong>&ldquo;添加权限&rdquo;</strong> (Add permissions) 按钮</li>
<li>返回 API 权限页面,确认列表中显示以下权限:
<ul>
<li>✅ <code>User.Read</code> (默认)</li>
<li>✅ <code>Files.ReadWrite.AppFolder</code></li>
<li>✅ <code>offline_access</code></li>
</ul>
</li>
</ol>
<h3 id="25-授予管理员同意-可选">2.5 授予管理员同意 (可选)</h3>
<ul>
<li>如果你是组织管理员,可以点击 <strong>&ldquo;为 [组织名称] 授予管理员同意&rdquo;</strong> 按钮</li>
<li>对于个人账号或公共应用,此步骤可以跳过</li>
<li>用户首次使用时会看到授权页面,点击&quot;接受&quot;即可</li>
</ul>
<hr>
<h2 id="第三步-获取应用凭据">第三步: 获取应用凭据</h2>
<h3 id="31-复制-client-id-应用程序-id">3.1 复制 Client ID (应用程序 ID)</h3>
<ol>
<li>在应用详情页面的 <strong>&ldquo;概述&rdquo;</strong> (Overview) 选项卡中</li>
<li>找到 <strong>&ldquo;应用程序(客户端) ID&rdquo;</strong> (Application (client) ID)</li>
<li>这是一个 GUID 格式的字符串,类似:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">12345678-1234-1234-1234-123456789abc
</span></span></code></pre></div></li>
<li>点击旁边的复制图标,将 Client ID 保存到安全的地方</li>
</ol>
<blockquote>
<p>🔒 <strong>安全提示</strong>: Client ID 是公开信息,可以在前端代码中使用。但不要将它与 Client Secret 混淆。</p>
</blockquote>
<h3 id="32-是否需要-client-secret">3.2 是否需要 Client Secret?</h3>
<p>对于不同类型的应用,Client Secret 的需求不同:</p>
<table>
  <thead>
      <tr>
          <th>应用类型</th>
          <th>是否需要 Client Secret</th>
          <th>原因</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>浏览器插件</td>
          <td>❌ 不需要</td>
          <td>使用隐式授权流程或 PKCE</td>
      </tr>
      <tr>
          <td>单页应用 (SPA)</td>
          <td>❌ 不需要</td>
          <td>前端代码无法安全存储密钥</td>
      </tr>
      <tr>
          <td>Web 应用 (有后端)</td>
          <td>✅ 需要</td>
          <td>在服务器端安全存储</td>
      </tr>
      <tr>
          <td>桌面/移动应用</td>
          <td>❌ 不需要</td>
          <td>使用 PKCE 授权流程</td>
      </tr>
  </tbody>
</table>
<p><strong>如果需要 Client Secret,创建方法如下:</strong></p>
<ol>
<li>在左侧菜单中点击 <strong>&ldquo;证书和密码&rdquo;</strong> (Certificates &amp; secrets)</li>
<li>点击 <strong>&quot;+ 新客户端密码&quot;</strong> (New client secret)</li>
<li>输入描述(如 &ldquo;生产环境密钥&rdquo;),选择过期时间</li>
<li>点击 <strong>&ldquo;添加&rdquo;</strong> (Add)</li>
<li><strong>立即复制密钥值</strong>,此密钥只显示一次</li>
</ol>
<blockquote>
<p>⚠️ <strong>警告</strong>: Client Secret 是敏感信息,必须妥善保管,不能提交到代码仓库。</p>
</blockquote>
<h3 id="33-配置身份验证设置">3.3 配置身份验证设置</h3>
<ol>
<li>在左侧菜单点击 <strong>&ldquo;身份验证&rdquo;</strong> (Authentication)</li>
<li>确认 <strong>重定向 URI</strong> 已正确添加</li>
<li>向下滚动到 <strong>&ldquo;隐式授权和混合流&rdquo;</strong> (Implicit grant and hybrid flows) 部分</li>
</ol>
<p><strong>对于浏览器插件或 SPA,需要启用:</strong></p>
<ul>
<li>✅ 勾选 <strong>&ldquo;访问令牌&rdquo;</strong> (Access tokens)</li>
<li>✅ 勾选 <strong>&ldquo;ID 令牌&rdquo;</strong> (ID tokens)</li>
</ul>
<p><strong>对于使用授权码流程的应用,可以不勾选上述选项</strong></p>
<ol start="4">
<li>点击页面底部的 <strong>&ldquo;保存&rdquo;</strong> (Save) 按钮</li>
</ol>
<hr>
<h2 id="第四步-配置应用代码">第四步: 配置应用代码</h2>
<p>以浏览器插件为例,说明如何在代码中配置 OneDrive API。</p>
<h3 id="41-获取浏览器插件的-extension-id">4.1 获取浏览器插件的 Extension ID</h3>
<p>如果你正在开发浏览器插件:</p>
<ol>
<li>打开 Chrome 或 Edge 浏览器</li>
<li>访问 <code>chrome://extensions/</code> (Chrome) 或 <code>edge://extensions/</code> (Edge)</li>
<li>开启右上角的 <strong>&ldquo;开发者模式&rdquo;</strong> (Developer mode)</li>
<li>点击 <strong>&ldquo;加载已解压的扩展程序&rdquo;</strong> (Load unpacked)</li>
<li>选择你的插件项目文件夹</li>
<li>安装成功后,在插件卡片上找到 <strong>&ldquo;ID&rdquo;</strong> 字段,复制扩展 ID
<ul>
<li>类似: <code>abcdefghijklmnopqrstuvwxyz123456</code></li>
</ul>
</li>
</ol>
<h3 id="42-更新-azure-重定向-uri">4.2 更新 Azure 重定向 URI</h3>
<ol>
<li>返回 Azure Portal 的应用注册页面</li>
<li>进入 <strong>&ldquo;身份验证&rdquo;</strong> (Authentication)</li>
<li>更新或添加重定向 URI:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">https://abcdefghijklmnopqrstuvwxyz123456.chromiumapp.org/onedrive
</span></span></code></pre></div><ul>
<li>将 <code>abcdefghijklmnopqrstuvwxyz123456</code> 替换为实际的扩展 ID</li>
</ul>
</li>
<li>点击 <strong>&ldquo;保存&rdquo;</strong> (Save)</li>
</ol>
<h3 id="43-在代码中配置-client-id">4.3 在代码中配置 Client ID</h3>
<p>以 QuickFillForm 插件为例,在 <code>onedrive-sync.js</code> 文件中配置:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">OneDriveSync</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 替换为你的 Azure 应用程序 ID (Client ID)
</span></span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">clientId</span> <span class="o">=</span> <span class="s1">&#39;12345678-1234-1234-1234-123456789abc&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 获取当前插件的 ID
</span></span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">redirectUri</span> <span class="o">=</span> <span class="sb">`https://</span><span class="si">${</span><span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="sb">.chromiumapp.org/onedrive`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// Microsoft Graph API 端点
</span></span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">graphEndpoint</span> <span class="o">=</span> <span class="s1">&#39;https://graph.microsoft.com/v1.0&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 应用专用文件夹路径
</span></span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">appFolderPath</span> <span class="o">=</span> <span class="s1">&#39;/me/drive/special/approot&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 请求的权限范围
</span></span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">scopes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;Files.ReadWrite.AppFolder&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;offline_access&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 获取访问令牌
</span></span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">getAccessToken</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">chrome</span><span class="p">.</span><span class="nx">identity</span><span class="p">.</span><span class="nx">launchWebAuthFlow</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">                <span class="nx">url</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">getAuthUrl</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                <span class="nx">interactive</span><span class="o">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span> <span class="p">(</span><span class="nx">redirectUrl</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">lastError</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">reject</span><span class="p">(</span><span class="nx">chrome</span><span class="p">.</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">lastError</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                
</span></span><span class="line"><span class="cl">                <span class="c1">// 从回调 URL 中提取 access_token
</span></span></span><span class="line"><span class="cl">                <span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nx">redirectUrl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="kr">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="nx">url</span><span class="p">.</span><span class="nx">hash</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/access_token=([^&amp;]*)/</span><span class="p">)</span><span class="o">?</span><span class="p">.[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">                
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">resolve</span><span class="p">(</span><span class="nx">token</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">reject</span><span class="p">(</span><span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;无法获取访问令牌&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 构建授权 URL
</span></span></span><span class="line"><span class="cl">    <span class="nx">getAuthUrl</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">params</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">client_id</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">clientId</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">response_type</span><span class="o">:</span> <span class="s1">&#39;token&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">redirect_uri</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">redirectUri</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">scope</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">scopes</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nx">response_mode</span><span class="o">:</span> <span class="s1">&#39;fragment&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sb">`https://login.microsoftonline.com/common/oauth2/v2.0/authorize?</span><span class="si">${</span><span class="nx">params</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 上传文件到 OneDrive
</span></span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">uploadFile</span><span class="p">(</span><span class="nx">fileName</span><span class="p">,</span> <span class="nx">content</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">getAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="sb">`</span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">graphEndpoint</span><span class="si">}${</span><span class="k">this</span><span class="p">.</span><span class="nx">appFolderPath</span><span class="si">}</span><span class="sb">:/</span><span class="si">${</span><span class="nx">fileName</span><span class="si">}</span><span class="sb">:/content`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;PUT&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="sb">`Bearer </span><span class="si">${</span><span class="nx">token</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="nx">body</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="sb">`上传失败: </span><span class="si">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 从 OneDrive 下载文件
</span></span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">downloadFile</span><span class="p">(</span><span class="nx">fileName</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">getAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="sb">`</span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">graphEndpoint</span><span class="si">}${</span><span class="k">this</span><span class="p">.</span><span class="nx">appFolderPath</span><span class="si">}</span><span class="sb">:/</span><span class="si">${</span><span class="nx">fileName</span><span class="si">}</span><span class="sb">:/content`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="sb">`Bearer </span><span class="si">${</span><span class="nx">token</span><span class="si">}</span><span class="sb">`</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="sb">`下载失败: </span><span class="si">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用示例
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">onedriveSync</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OneDriveSync</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 上传数据
</span></span></span><span class="line"><span class="cl"><span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">uploadFile</span><span class="p">(</span><span class="s1">&#39;data.json&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">key</span><span class="o">:</span> <span class="s1">&#39;value&#39;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;上传成功&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">err</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;上传失败:&#39;</span><span class="p">,</span> <span class="nx">err</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 下载数据
</span></span></span><span class="line"><span class="cl"><span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">downloadFile</span><span class="p">(</span><span class="s1">&#39;data.json&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">data</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;下载的数据:&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">err</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;下载失败:&#39;</span><span class="p">,</span> <span class="nx">err</span><span class="p">));</span>
</span></span></code></pre></div><h3 id="44-web-应用配置示例">4.4 Web 应用配置示例</h3>
<p>对于传统 Web 应用(有后端),使用授权码流程更安全:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 前端代码
</span></span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">loginWithOneDrive</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">clientId</span> <span class="o">=</span> <span class="s1">&#39;YOUR_CLIENT_ID&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">redirectUri</span> <span class="o">=</span> <span class="s1">&#39;https://yourdomain.com/callback&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">scopes</span> <span class="o">=</span> <span class="s1">&#39;Files.ReadWrite.AppFolder offline_access&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">authUrl</span> <span class="o">=</span> <span class="sb">`https://login.microsoftonline.com/common/oauth2/v2.0/authorize?`</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`client_id=</span><span class="si">${</span><span class="nx">clientId</span><span class="si">}</span><span class="sb">`</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`&amp;response_type=code`</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`&amp;redirect_uri=</span><span class="si">${</span><span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">redirectUri</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`&amp;scope=</span><span class="si">${</span><span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">scopes</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">authUrl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 后端代码 (Node.js Express 示例)
</span></span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;/callback&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">query</span><span class="p">.</span><span class="nx">code</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 用授权码换取访问令牌
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">tokenResponse</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">&#39;https://login.microsoftonline.com/common/oauth2/v2.0/token&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/x-www-form-urlencoded&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">body</span><span class="o">:</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">client_id</span><span class="o">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">CLIENT_ID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">client_secret</span><span class="o">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">CLIENT_SECRET</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">code</span><span class="o">:</span> <span class="nx">code</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">redirect_uri</span><span class="o">:</span> <span class="s1">&#39;https://yourdomain.com/callback&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">grant_type</span><span class="o">:</span> <span class="s1">&#39;authorization_code&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">tokenData</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">tokenResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 保存 access_token 和 refresh_token
</span></span></span><span class="line"><span class="cl">    <span class="nx">req</span><span class="p">.</span><span class="nx">session</span><span class="p">.</span><span class="nx">accessToken</span> <span class="o">=</span> <span class="nx">tokenData</span><span class="p">.</span><span class="nx">access_token</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">req</span><span class="p">.</span><span class="nx">session</span><span class="p">.</span><span class="nx">refreshToken</span> <span class="o">=</span> <span class="nx">tokenData</span><span class="p">.</span><span class="nx">refresh_token</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nx">res</span><span class="p">.</span><span class="nx">redirect</span><span class="p">(</span><span class="s1">&#39;/dashboard&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h3 id="45-配置-manifestjson-浏览器插件">4.5 配置 manifest.json (浏览器插件)</h3>
<p>在插件的 <code>manifest.json</code> 中添加必要的权限:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;manifest_version&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;QuickFillForm&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;permissions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;identity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;storage&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host_permissions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://login.microsoftonline.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://graph.microsoft.com/*&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;oauth2&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;client_id&#34;</span><span class="p">:</span> <span class="s2">&#34;12345678-1234-1234-1234-123456789abc.chromiumapp.org&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;scopes&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Files.ReadWrite.AppFolder&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;offline_access&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h2 id="第五步-测试同步功能">第五步: 测试同步功能</h2>
<h3 id="51-测试用户认证">5.1 测试用户认证</h3>
<ol>
<li>在应用中触发 OneDrive 登录流程</li>
<li>浏览器会打开 Microsoft 登录页面</li>
<li>输入 Microsoft 账号和密码登录</li>
<li>首次使用会看到权限请求页面:
<ul>
<li>显示应用名称</li>
<li>列出请求的权限</li>
<li>显示应用发布者信息</li>
</ul>
</li>
<li>点击 <strong>&ldquo;接受&rdquo;</strong> (Accept) 按钮授权</li>
</ol>
<h3 id="52-测试文件上传">5.2 测试文件上传</h3>
<p><strong>浏览器插件示例:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 测试上传功能
</span></span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">testUpload</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">testData</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;张三&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">email</span><span class="o">:</span> <span class="s1">&#39;zhangsan@example.com&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">phone</span><span class="o">:</span> <span class="s1">&#39;13800138000&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">uploadFile</span><span class="p">(</span><span class="s1">&#39;test-data.json&#39;</span><span class="p">,</span> <span class="nx">testData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;✅ 上传成功:&#39;</span><span class="p">,</span> <span class="nx">result</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;❌ 上传失败:&#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">testUpload</span><span class="p">();</span>
</span></span></code></pre></div><h3 id="53-测试文件下载">5.3 测试文件下载</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 测试下载功能
</span></span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">testDownload</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">downloadFile</span><span class="p">(</span><span class="s1">&#39;test-data.json&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;✅ 下载成功:&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;❌ 下载失败:&#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">testDownload</span><span class="p">();</span>
</span></span></code></pre></div><h3 id="54-验证文件存储位置">5.4 验证文件存储位置</h3>
<ol>
<li>登录 <a href="https://onedrive.live.com/">OneDrive 网页版</a></li>
<li>在左侧菜单中找到 <strong>&ldquo;应用&rdquo;</strong> (Apps) 文件夹</li>
<li>进入你的应用文件夹,例如 <code>Apps/QuickFillForm/</code></li>
<li>确认文件已成功上传</li>
</ol>
<h3 id="55-测试跨设备同步">5.5 测试跨设备同步</h3>
<ol>
<li>在设备 A 上传数据</li>
<li>在设备 B 上登录同一 Microsoft 账号</li>
<li>下载数据,验证是否与设备 A 一致</li>
<li>修改数据后再次上传</li>
<li>在设备 A 上下载,验证是否更新</li>
</ol>
<hr>
<h2 id="常见问题与解决方案">常见问题与解决方案</h2>
<h3 id="q1-无法获取访问令牌">Q1: 无法获取访问令牌</h3>
<p><strong>错误信息:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">AADSTS50011: The redirect URI specified in the request does not match
</span></span></code></pre></div><p><strong>原因分析:</strong></p>
<ul>
<li>Client ID 配置错误</li>
<li>重定向 URI 不匹配</li>
<li>Extension ID 更改后未更新 Azure 配置</li>
</ul>
<p><strong>解决方案:</strong></p>
<ol>
<li>
<p>检查代码中的 <code>clientId</code> 是否正确复制</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 错误: 多余的空格或特殊字符
</span></span></span><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">clientId</span> <span class="o">=</span> <span class="s1">&#39;12345678-1234-1234-1234-123456789abc &#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 正确
</span></span></span><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">clientId</span> <span class="o">=</span> <span class="s1">&#39;12345678-1234-1234-1234-123456789abc&#39;</span><span class="p">;</span>
</span></span></code></pre></div></li>
<li>
<p>确认 Azure 应用注册中的重定向 URI 与代码完全一致</p>
<ul>
<li>Azure 配置: <code>https://abcdefg123456.chromiumapp.org/onedrive</code></li>
<li>代码配置: <code>https://abcdefg123456.chromiumapp.org/onedrive</code></li>
<li>注意区分大小写和尾部斜杠</li>
</ul>
</li>
<li>
<p>保存配置后等待 2-5 分钟让配置生效</p>
</li>
</ol>
<h3 id="q2-权限被拒绝无法读写文件">Q2: 权限被拒绝,无法读写文件</h3>
<p><strong>错误信息:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">403 Forbidden: Access denied
</span></span></code></pre></div><p><strong>原因分析:</strong></p>
<ul>
<li>权限未正确添加</li>
<li>用户未授权</li>
<li>使用了错误的 API 端点</li>
</ul>
<p><strong>解决方案:</strong></p>
<ol>
<li>在 Azure Portal 检查是否已添加 <code>Files.ReadWrite.AppFolder</code> 权限</li>
<li>清除用户授权,重新登录并同意权限
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 清除缓存的令牌
</span></span></span><span class="line"><span class="cl"><span class="nx">chrome</span><span class="p">.</span><span class="nx">identity</span><span class="p">.</span><span class="nx">clearAllCachedAuthTokens</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;已清除缓存,请重新登录&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div></li>
<li>确认使用正确的 API 路径:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 正确: 使用应用专用文件夹
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="s1">&#39;/me/drive/special/approot:/data.json:/content&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 错误: 使用根目录 (需要 Files.ReadWrite.All 权限)
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="s1">&#39;/me/drive/root:/data.json:/content&#39;</span><span class="p">;</span>
</span></span></code></pre></div></li>
</ol>
<h3 id="q3-访问令牌过期">Q3: 访问令牌过期</h3>
<p><strong>错误信息:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">401 Unauthorized: The access token has expired
</span></span></code></pre></div><p><strong>原因分析:</strong></p>
<ul>
<li>Access Token 默认有效期 1 小时</li>
<li>未实现 Refresh Token 自动刷新机制</li>
</ul>
<p><strong>解决方案:</strong></p>
<p>实现令牌刷新逻辑:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">OneDriveSync</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">accessToken</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">refreshToken</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">tokenExpiry</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 获取有效的访问令牌
</span></span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">getValidAccessToken</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果令牌未过期,直接返回
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">accessToken</span> <span class="o">&amp;&amp;</span> <span class="k">this</span><span class="p">.</span><span class="nx">tokenExpiry</span> <span class="o">&gt;</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果有刷新令牌,尝试刷新
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">refreshToken</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">refreshAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 否则重新登录
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">getAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 刷新访问令牌
</span></span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">refreshAccessToken</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">&#39;https://login.microsoftonline.com/common/oauth2/v2.0/token&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/x-www-form-urlencoded&#39;</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">body</span><span class="o">:</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">                <span class="nx">client_id</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">clientId</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nx">refresh_token</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">refreshToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nx">grant_type</span><span class="o">:</span> <span class="s1">&#39;refresh_token&#39;</span>
</span></span><span class="line"><span class="cl">            <span class="p">})</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">accessToken</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">access_token</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">refreshToken</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">refresh_token</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">tokenExpiry</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">+</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">expires_in</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 保存到本地存储
</span></span></span><span class="line"><span class="cl">        <span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">set</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">onedrive_access_token</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">onedrive_refresh_token</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">refreshToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">onedrive_token_expiry</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">tokenExpiry</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="q4-找不到扩展-id">Q4: 找不到扩展 ID</h3>
<p><strong>解决方案:</strong></p>
<ol>
<li>访问浏览器的扩展管理页面:
<ul>
<li>Chrome: <code>chrome://extensions/</code></li>
<li>Edge: <code>edge://extensions/</code></li>
</ul>
</li>
<li>确保已开启&quot;开发者模式&quot;</li>
<li>Extension ID 显示在扩展卡片上,格式类似:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ID: abcdefghijklmnopqrstuvwxyz123456
</span></span></code></pre></div></li>
<li>如果是已发布的扩展,可以在 Chrome Web Store 的 URL 中找到:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">https://chrome.google.com/webstore/detail/extension-name/abcdefghijklmnopqrstuvwxyz123456
</span></span></code></pre></div></li>
</ol>
<h3 id="q5-文件上传成功但在-onedrive-中找不到">Q5: 文件上传成功但在 OneDrive 中找不到</h3>
<p><strong>原因分析:</strong></p>
<ul>
<li>使用了应用专用文件夹,路径在 <code>Apps/&lt;AppName&gt;/</code> 下</li>
<li>OneDrive 网页版界面可能有延迟</li>
<li>文件名或路径错误</li>
</ul>
<p><strong>解决方案:</strong></p>
<ol>
<li>在 OneDrive 网页版中,导航到:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">OneDrive → 应用 (Apps) → QuickFillForm
</span></span></code></pre></div></li>
<li>等待 1-2 分钟,刷新页面</li>
<li>使用 API 查询文件是否存在:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 列出应用文件夹中的所有文件
</span></span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">listFiles</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">getValidAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;https://graph.microsoft.com/v1.0/me/drive/special/approot/children&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> <span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="sb">`Bearer </span><span class="si">${</span><span class="nx">token</span><span class="si">}</span><span class="sb">`</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;文件列表:&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
<h3 id="q6-跨域请求被阻止-cors">Q6: 跨域请求被阻止 (CORS)</h3>
<p><strong>错误信息:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Access to fetch at &#39;https://graph.microsoft.com/...&#39; has been blocked by CORS policy
</span></span></code></pre></div><p><strong>原因分析:</strong></p>
<ul>
<li>在普通网页中直接请求 Graph API</li>
<li>未在 <code>manifest.json</code> 中配置 <code>host_permissions</code></li>
</ul>
<p><strong>解决方案:</strong></p>
<p><strong>方案一: 使用后端代理 (推荐)</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 前端调用后端 API
</span></span></span><span class="line"><span class="cl"><span class="nx">fetch</span><span class="p">(</span><span class="s1">&#39;/api/onedrive/upload&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">body</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 后端转发到 Microsoft Graph
</span></span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">&#39;/api/onedrive/upload&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">&#39;https://graph.microsoft.com/v1.0/...&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> <span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="sb">`Bearer </span><span class="si">${</span><span class="nx">accessToken</span><span class="si">}</span><span class="sb">`</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p><strong>方案二: 浏览器插件配置</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host_permissions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://login.microsoftonline.com/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://graph.microsoft.com/*&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="q7-授权页面显示需要管理员审批">Q7: 授权页面显示&quot;需要管理员审批&quot;</h3>
<p><strong>错误信息:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">AADSTS65001: The user or administrator has not consented to use the application
</span></span></code></pre></div><p><strong>原因分析:</strong></p>
<ul>
<li>组织策略要求管理员批准应用</li>
<li>请求的权限需要管理员同意</li>
</ul>
<p><strong>解决方案:</strong></p>
<ol>
<li>如果你是管理员,在 Azure Portal 中授予管理员同意</li>
<li>如果不是管理员,联系 IT 部门批准应用</li>
<li>使用个人 Microsoft 账号测试(个人账号无需管理员同意)</li>
<li>减少权限请求,只使用委托权限而不是应用权限</li>
</ol>
<hr>
<h2 id="安全最佳实践">安全最佳实践</h2>
<h3 id="1-保护-client-secret">1. 保护 Client Secret</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// ❌ 错误: 在前端代码中硬编码密钥
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">clientSecret</span> <span class="o">=</span> <span class="s1">&#39;your-secret-key-123456&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 正确: 在后端使用环境变量
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">clientSecret</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">AZURE_CLIENT_SECRET</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="2-使用最小权限原则">2. 使用最小权限原则</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// ❌ 错误: 请求过多权限
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">scopes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Files.ReadWrite.All&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Mail.Read&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Contacts.ReadWrite&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ 正确: 只请求必需的权限
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">scopes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Files.ReadWrite.AppFolder&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;offline_access&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span></code></pre></div><h3 id="3-安全存储令牌">3. 安全存储令牌</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// ✅ 浏览器插件: 使用 chrome.storage.local
</span></span></span><span class="line"><span class="cl"><span class="nx">chrome</span><span class="p">.</span><span class="nx">storage</span><span class="p">.</span><span class="nx">local</span><span class="p">.</span><span class="nx">set</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">onedrive_token</span><span class="o">:</span> <span class="nx">encryptedToken</span>  <span class="c1">// 最好加密存储
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ✅ Web 应用: 使用 HttpOnly Cookie
</span></span></span><span class="line"><span class="cl"><span class="nx">res</span><span class="p">.</span><span class="nx">cookie</span><span class="p">(</span><span class="s1">&#39;access_token&#39;</span><span class="p">,</span> <span class="nx">token</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">httpOnly</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">secure</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sameSite</span><span class="o">:</span> <span class="s1">&#39;strict&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h3 id="4-定期检查应用活动">4. 定期检查应用活动</h3>
<ol>
<li>登录 <a href="https://portal.azure.com/">Azure Portal</a></li>
<li>进入应用注册 → 你的应用</li>
<li>查看 <strong>&ldquo;概述&rdquo;</strong> 中的使用情况统计</li>
<li>检查异常登录和 API 调用</li>
</ol>
<h3 id="5-处理敏感数据">5. 处理敏感数据</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 上传前加密敏感数据
</span></span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">uploadSecureData</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">encrypted</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">encryptData</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">uploadFile</span><span class="p">(</span><span class="s1">&#39;secure-data.enc&#39;</span><span class="p">,</span> <span class="nx">encrypted</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 下载后解密
</span></span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">downloadSecureData</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">encrypted</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">downloadFile</span><span class="p">(</span><span class="s1">&#39;secure-data.enc&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kr">await</span> <span class="nx">decryptData</span><span class="p">(</span><span class="nx">encrypted</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h2 id="进阶技巧">进阶技巧</h2>
<h3 id="1-实现文件版本管理">1. 实现文件版本管理</h3>
<p>OneDrive 自动保留文件版本历史,可以通过 API 访问:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">getFileVersions</span><span class="p">(</span><span class="nx">fileName</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">getValidAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`https://graph.microsoft.com/v1.0/me/drive/special/approot:/</span><span class="si">${</span><span class="nx">fileName</span><span class="si">}</span><span class="sb">:/versions`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> <span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="sb">`Bearer </span><span class="si">${</span><span class="nx">token</span><span class="si">}</span><span class="sb">`</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">data</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>  <span class="c1">// 返回版本列表
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="2-实现增量同步">2. 实现增量同步</h3>
<p>通过 Delta API 获取文件变化:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">syncChanges</span><span class="p">(</span><span class="nx">deltaLink</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">getValidAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="nx">deltaLink</span> <span class="o">||</span> 
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;https://graph.microsoft.com/v1.0/me/drive/special/approot/delta&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> <span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="sb">`Bearer </span><span class="si">${</span><span class="nx">token</span><span class="si">}</span><span class="sb">`</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理变化的文件
</span></span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">item</span> <span class="k">of</span> <span class="nx">data</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">item</span><span class="p">.</span><span class="nx">deleted</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;文件已删除:&#39;</span><span class="p">,</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;文件已更新:&#39;</span><span class="p">,</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 保存 deltaLink 供下次使用
</span></span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">data</span><span class="p">[</span><span class="s1">&#39;@odata.deltaLink&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="3-实现冲突解决">3. 实现冲突解决</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">smartSync</span><span class="p">(</span><span class="nx">fileName</span><span class="p">,</span> <span class="nx">localData</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 下载远程文件
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">remoteData</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">downloadFile</span><span class="p">(</span><span class="nx">fileName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 比较时间戳
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">remoteData</span><span class="p">.</span><span class="nx">lastModified</span> <span class="o">&gt;</span> <span class="nx">localData</span><span class="p">.</span><span class="nx">lastModified</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 远程更新,下载覆盖本地
</span></span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">{</span> <span class="nx">action</span><span class="o">:</span> <span class="s1">&#39;download&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="o">:</span> <span class="nx">remoteData</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">localData</span><span class="p">.</span><span class="nx">lastModified</span> <span class="o">&gt;</span> <span class="nx">remoteData</span><span class="p">.</span><span class="nx">lastModified</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 本地更新,上传覆盖远程
</span></span></span><span class="line"><span class="cl">            <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">uploadFile</span><span class="p">(</span><span class="nx">fileName</span><span class="p">,</span> <span class="nx">localData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">{</span> <span class="nx">action</span><span class="o">:</span> <span class="s1">&#39;upload&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="o">:</span> <span class="nx">localData</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 智能合并
</span></span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">merged</span> <span class="o">=</span> <span class="nx">mergeData</span><span class="p">(</span><span class="nx">localData</span><span class="p">,</span> <span class="nx">remoteData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">uploadFile</span><span class="p">(</span><span class="nx">fileName</span><span class="p">,</span> <span class="nx">merged</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">{</span> <span class="nx">action</span><span class="o">:</span> <span class="s1">&#39;merge&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="o">:</span> <span class="nx">merged</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 文件不存在,直接上传
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">404</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">uploadFile</span><span class="p">(</span><span class="nx">fileName</span><span class="p">,</span> <span class="nx">localData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">{</span> <span class="nx">action</span><span class="o">:</span> <span class="s1">&#39;create&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="o">:</span> <span class="nx">localData</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="4-批量操作">4. 批量操作</h3>
<p>使用批处理 API 提高性能:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">batchUpload</span><span class="p">(</span><span class="nx">files</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">onedriveSync</span><span class="p">.</span><span class="nx">getValidAccessToken</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">requests</span> <span class="o">=</span> <span class="nx">files</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">file</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="nx">id</span><span class="o">:</span> <span class="nx">index</span><span class="p">.</span><span class="nx">toString</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;PUT&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">url</span><span class="o">:</span> <span class="sb">`/me/drive/special/approot:/</span><span class="si">${</span><span class="nx">file</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">:/content`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">body</span><span class="o">:</span> <span class="nx">file</span><span class="p">.</span><span class="nx">content</span>
</span></span><span class="line"><span class="cl">    <span class="p">}));</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">&#39;https://graph.microsoft.com/v1.0/$batch&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;Authorization&#39;</span><span class="o">:</span> <span class="sb">`Bearer </span><span class="si">${</span><span class="nx">token</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">body</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="nx">requests</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kr">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h2 id="相关资源">相关资源</h2>
<h3 id="官方文档">官方文档</h3>
<ul>
<li>📘 <a href="https://docs.microsoft.com/zh-cn/graph/overview">Microsoft Graph API 文档</a></li>
<li>📘 <a href="https://docs.microsoft.com/zh-cn/onedrive/developer/">OneDrive API 参考</a></li>
<li>📘 <a href="https://docs.microsoft.com/zh-cn/azure/active-directory/develop/quickstart-register-app">Azure AD 应用注册指南</a></li>
<li>📘 <a href="https://docs.microsoft.com/zh-cn/azure/active-directory/develop/v2-oauth2-auth-code-flow">OAuth 2.0 授权流程</a></li>
</ul>
<h3 id="开发工具">开发工具</h3>
<ul>
<li>🛠️ <a href="https://developer.microsoft.com/zh-cn/graph/graph-explorer">Graph Explorer</a> - 在线测试 API</li>
<li>🛠️ <a href="https://github.com/microsoftgraph">Microsoft Graph SDK</a> - 各语言 SDK</li>
<li>🛠️ <a href="https://www.postman.com/microsoftgraph/workspace/microsoft-graph/overview">Postman Collection</a> - API 调试</li>
</ul>
<h3 id="示例项目">示例项目</h3>
<ul>
<li>💻 <a href="https://github.com/OneDrive/onedrive-api-docs/blob/live/samples/javascript-picker-saver.md">OneDrive JavaScript SDK</a></li>
<li>💻 <a href="https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/api-samples/identity">浏览器插件 OAuth 示例</a></li>
</ul>
<h3 id="社区资源">社区资源</h3>
<ul>
<li>💬 <a href="https://techcommunity.microsoft.com/t5/microsoft-graph/ct-p/MicrosoftGraph">Microsoft Graph 开发者论坛</a></li>
<li>💬 <a href="https://stackoverflow.com/questions/tagged/microsoft-graph">Stack Overflow - microsoft-graph</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>VSCode SFTP插件实现本地代码与远程服务器同步</title>
      <link>https://lifeislife.cn/posts/vscode-sftp%E6%8F%92%E4%BB%B6%E5%AE%9E%E7%8E%B0%E6%9C%AC%E5%9C%B0%E4%BB%A3%E7%A0%81%E4%B8%8E%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%90%8C%E6%AD%A5/</link>
      <pubDate>Fri, 21 Nov 2025 10:03:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/vscode-sftp%E6%8F%92%E4%BB%B6%E5%AE%9E%E7%8E%B0%E6%9C%AC%E5%9C%B0%E4%BB%A3%E7%A0%81%E4%B8%8E%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%90%8C%E6%AD%A5/</guid>
      <description>&lt;p&gt;在日常开发中，我们经常需要在本地编写代码，然后将代码同步到远程服务器进行测试或部署。传统的方式是使用 FileZilla、WinSCP 等第三方工具手动上传文件，这种方式不仅繁琐，而且容易出错。VSCode 的 SFTP 插件可以帮助我们实现本地代码与远程服务器的自动同步，大大提高开发效率。&lt;/p&gt;
&lt;h2 id=&#34;应用场景&#34;&gt;应用场景&lt;/h2&gt;
&lt;p&gt;SFTP 插件适用于以下场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;本地开发，远程调试&lt;/strong&gt;：在本地编写代码，保存后自动同步到远程服务器，无需手动上传&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快速部署&lt;/strong&gt;：本地测试通过后，一键同步到生产环境服务器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多服务器管理&lt;/strong&gt;：同时管理多个远程服务器，快速切换上传目标&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;团队协作&lt;/strong&gt;：统一开发环境，确保代码在服务器端运行一致&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;安装插件&#34;&gt;安装插件&lt;/h2&gt;
&lt;p&gt;打开 VSCode，按下快捷键 &lt;code&gt;Ctrl + Shift + X&lt;/code&gt; 打开扩展商店，搜索 &lt;code&gt;sftp&lt;/code&gt;，找到名为 &lt;strong&gt;SFTP&lt;/strong&gt; 的插件（作者：Natizyskunk），这是安装量最高、最受欢迎的 SFTP 插件。点击安装即可。&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/20220104114810.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220104114810.png&#34; alt=&#34;SFTP插件&#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;安装完成后，VSCode 会在侧边栏显示 SFTP 图标，表示插件已成功安装。&lt;/p&gt;
&lt;h2 id=&#34;配置插件&#34;&gt;配置插件&lt;/h2&gt;
&lt;h3 id=&#34;生成配置文件&#34;&gt;生成配置文件&lt;/h3&gt;
&lt;p&gt;插件安装完成后，我们需要为当前项目创建配置文件。有两种方式：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方式一：使用命令面板&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;按下快捷键 &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; 打开命令面板&lt;/li&gt;
&lt;li&gt;输入 &lt;code&gt;SFTP: Config&lt;/code&gt; 并回车&lt;/li&gt;
&lt;li&gt;VSCode 会在当前工作区的 &lt;code&gt;.vscode&lt;/code&gt; 文件夹下自动生成 &lt;code&gt;sftp.json&lt;/code&gt; 配置文件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;方式二：右键菜单&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 VSCode 资源管理器中，右键点击项目文件夹&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;SFTP: Config&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;同样会生成 &lt;code&gt;sftp.json&lt;/code&gt; 配置文件&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;基础配置示例&#34;&gt;基础配置示例&lt;/h3&gt;
&lt;p&gt;打开生成的 &lt;code&gt;sftp.json&lt;/code&gt; 文件，你会看到一个默认的配置模板。下面是一个基础配置示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;我的服务器&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;192.168.1.100&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;protocol&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sftp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;root&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your_password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;remotePath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/var/www/project&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;useTempFile&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;openSsh&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;完整配置示例&#34;&gt;完整配置示例&lt;/h3&gt;
&lt;p&gt;下面是一个包含更多选项的完整配置示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;生产环境服务器&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;192.168.1.100&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;protocol&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sftp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deployer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your_password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;remotePath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/deployer/project&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;./&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;useTempFile&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;openSsh&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;downloadOnOpen&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;.vscode&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;.git&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;.DS_Store&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_modules&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;*.log&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;*.tmp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;watcher&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;files&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;autoUpload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;autoDelete&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;syncOption&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;delete&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;skipCreate&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignoreExisting&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;sshConfigPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;concurrency&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;配置参数详解&#34;&gt;配置参数详解&lt;/h2&gt;
&lt;h3 id=&#34;连接配置参数&#34;&gt;连接配置参数&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;参数&lt;/th&gt;
          &lt;th&gt;类型&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
          &lt;th&gt;示例&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;配置名称，用于区分多个服务器配置&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;生产环境&amp;quot;&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;host&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;服务器 IP 地址或域名（必填）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;192.168.1.100&amp;quot;&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;port&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Number&lt;/td&gt;
          &lt;td&gt;端口号，SFTP 默认 22，FTP 默认 21&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;22&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;protocol&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;传输协议，可选 &lt;code&gt;sftp&lt;/code&gt; 或 &lt;code&gt;ftp&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;sftp&amp;quot;&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;username&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;登录用户名（必填）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;root&amp;quot;&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;password&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;登录密码&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;your_password&amp;quot;&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;认证方式参数&#34;&gt;认证方式参数&lt;/h3&gt;
&lt;p&gt;SFTP 插件支持三种认证方式：&lt;/p&gt;
&lt;h4 id=&#34;1-密码认证最简单&#34;&gt;1. 密码认证（最简单）&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;root&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your_password&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;2-密钥认证推荐&#34;&gt;2. 密钥认证（推荐）&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;root&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;privateKeyPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;C:\\Users\\YourName\\.ssh\\id_rsa&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;passphrase&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;密钥密码（如果有的话）&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;密钥文件路径说明：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;C:\\Users\\YourName\\.ssh\\id_rsa&lt;/code&gt; 或 &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Linux/Mac: &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;支持 OpenSSH 格式和 PPK 格式的密钥&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;3-ssh-config-配置高级&#34;&gt;3. SSH Config 配置（高级）&lt;/h4&gt;
&lt;p&gt;如果你已经配置了 SSH config 文件，可以直接引用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;sshConfigPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~/.ssh/config&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;myserver&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对应的 SSH config 文件内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Host myserver
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    HostName 192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    User deployer
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Port 22
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    IdentityFile ~/.ssh/id_rsa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;路径配置参数&#34;&gt;路径配置参数&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;参数&lt;/th&gt;
          &lt;th&gt;类型&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
          &lt;th&gt;示例&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;remotePath&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;远程服务器的目标路径（必填）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;/var/www/project&amp;quot;&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;context&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;本地项目的相对路径，默认为 &lt;code&gt;./&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;./src&amp;quot;&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;路径配置说明：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;假设本地项目结构如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;project/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── src/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── index.js
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── utils.js
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── tests/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── .vscode/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── sftp.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;如果 &lt;code&gt;context&lt;/code&gt; 为 &lt;code&gt;&amp;quot;./&amp;quot;&lt;/code&gt; 且 &lt;code&gt;remotePath&lt;/code&gt; 为 &lt;code&gt;&amp;quot;/var/www/project&amp;quot;&lt;/code&gt;，则 &lt;code&gt;src/index.js&lt;/code&gt; 会上传到 &lt;code&gt;/var/www/project/src/index.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;context&lt;/code&gt; 为 &lt;code&gt;&amp;quot;./src&amp;quot;&lt;/code&gt; 且 &lt;code&gt;remotePath&lt;/code&gt; 为 &lt;code&gt;&amp;quot;/var/www/project&amp;quot;&lt;/code&gt;，则 &lt;code&gt;src/index.js&lt;/code&gt; 会上传到 &lt;code&gt;/var/www/project/index.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;自动同步参数&#34;&gt;自动同步参数&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;参数&lt;/th&gt;
          &lt;th&gt;类型&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
          &lt;th&gt;默认值&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;uploadOnSave&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;保存文件时自动上传&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;downloadOnOpen&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;打开文件时自动下载最新版本&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;useTempFile&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;上传时使用临时文件（避免覆盖）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;最佳实践建议：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开发环境：设置 &lt;code&gt;uploadOnSave: true&lt;/code&gt;，方便快速同步&lt;/li&gt;
&lt;li&gt;生产环境：设置 &lt;code&gt;uploadOnSave: false&lt;/code&gt;，避免误操作&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;忽略文件配置&#34;&gt;忽略文件配置&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ignore&lt;/code&gt; 参数用于指定哪些文件或文件夹不需要同步，支持 glob 模式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/.vscode/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/.git/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/.DS_Store&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/node_modules/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/*.log&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/*.tmp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/dist/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/coverage/**&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;常用忽略模式：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;**/.git/**&lt;/code&gt;：忽略 Git 版本控制文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;**/node_modules/**&lt;/code&gt;：忽略 Node.js 依赖包&lt;/li&gt;
&lt;li&gt;&lt;code&gt;**/*.log&lt;/code&gt;：忽略所有日志文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;**/dist/**&lt;/code&gt;：忽略构建输出目录&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;监听器配置watcher&#34;&gt;监听器配置（Watcher）&lt;/h3&gt;
&lt;p&gt;监听器可以监控文件变化并自动执行同步操作：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;watcher&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;files&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;autoUpload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;autoDelete&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;参数&lt;/th&gt;
          &lt;th&gt;类型&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
          &lt;th&gt;默认值&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;files&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;String&lt;/td&gt;
          &lt;td&gt;监听的文件模式（glob）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;autoUpload&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;文件变化时自动上传&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;autoDelete&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;本地删除文件时自动删除远程文件&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;autoDelete: true&lt;/code&gt; 可能导致误删除，请谨慎使用&lt;/li&gt;
&lt;li&gt;监听器会持续运行，可能影响性能，建议按需启用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;同步选项sync-option&#34;&gt;同步选项（Sync Option）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;syncOption&lt;/code&gt; 用于配置手动同步时的行为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;syncOption&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;delete&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;skipCreate&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignoreExisting&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;参数&lt;/th&gt;
          &lt;th&gt;类型&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;delete&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;删除远程服务器上多余的文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;skipCreate&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;跳过创建新文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ignoreExisting&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;忽略已存在的文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;update&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Boolean&lt;/td&gt;
          &lt;td&gt;更新已修改的文件&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;性能优化参数&#34;&gt;性能优化参数&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;参数&lt;/th&gt;
          &lt;th&gt;类型&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
          &lt;th&gt;默认值&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;concurrency&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Number&lt;/td&gt;
          &lt;td&gt;并发上传的文件数量&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;connectTimeout&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Number&lt;/td&gt;
          &lt;td&gt;连接超时时间（毫秒）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;10000&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;keepalive&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Number&lt;/td&gt;
          &lt;td&gt;保持连接的间隔时间（毫秒）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;10000&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;concurrency&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;connectTimeout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;keepalive&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;性能优化建议：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;网络环境好时，可以增加 &lt;code&gt;concurrency&lt;/code&gt; 提高上传速度&lt;/li&gt;
&lt;li&gt;网络不稳定时，增加 &lt;code&gt;connectTimeout&lt;/code&gt; 避免连接超时&lt;/li&gt;
&lt;li&gt;频繁操作时，减小 &lt;code&gt;keepalive&lt;/code&gt; 保持连接活跃&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;插件使用&#34;&gt;插件使用&lt;/h2&gt;
&lt;h3 id=&#34;常用操作&#34;&gt;常用操作&lt;/h3&gt;
&lt;p&gt;安装并配置好插件后，可以通过以下方式使用：&lt;/p&gt;
&lt;h4 id=&#34;1-上传文件或文件夹&#34;&gt;1. 上传文件或文件夹&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;方式一：右键菜单&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在资源管理器中右键点击文件或文件夹&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;Upload&lt;/code&gt; 上传到服务器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;方式二：命令面板&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;按 &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; 打开命令面板&lt;/li&gt;
&lt;li&gt;输入 &lt;code&gt;SFTP: Upload&lt;/code&gt; 并选择相应的命令&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;2-下载文件或文件夹&#34;&gt;2. 下载文件或文件夹&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;右键点击文件或文件夹&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;Download&lt;/code&gt; 从服务器下载&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;3-同步操作&#34;&gt;3. 同步操作&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;同步本地到远程：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;命令面板输入 &lt;code&gt;SFTP: Sync Local -&amp;gt; Remote&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将本地所有文件同步到服务器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;同步远程到本地：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;命令面板输入 &lt;code&gt;SFTP: Sync Remote -&amp;gt; Local&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将服务器文件同步到本地&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;双向同步：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;命令面板输入 &lt;code&gt;SFTP: Sync Both Directions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;根据文件修改时间自动判断同步方向&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;4-对比文件差异&#34;&gt;4. 对比文件差异&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;右键点击文件&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;Diff with Remote&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;VSCode 会显示本地文件和远程文件的差异&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;5-列出远程文件&#34;&gt;5. 列出远程文件&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;命令面板输入 &lt;code&gt;SFTP: List&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;可以浏览远程服务器的文件目录&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;完整命令列表&#34;&gt;完整命令列表&lt;/h3&gt;
&lt;p&gt;按 &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; 打开命令面板，输入 &lt;code&gt;SFTP&lt;/code&gt; 可以看到所有可用命令：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;命令&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Config&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;创建或编辑配置文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Upload&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;上传当前文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Upload Folder&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;上传整个文件夹&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Download&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;下载当前文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Download Folder&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;下载整个文件夹&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Sync Local -&amp;gt; Remote&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;本地同步到远程&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Sync Remote -&amp;gt; Local&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;远程同步到本地&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Sync Both Directions&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;双向同步&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Diff with Remote&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;对比本地和远程文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: List&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;列出远程目录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: List All&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;列出所有配置的服务器&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;SFTP: Delete&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;删除远程文件&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;多服务器配置&#34;&gt;多服务器配置&lt;/h2&gt;
&lt;p&gt;在实际开发中，我们可能需要同时管理开发环境、测试环境和生产环境多个服务器。SFTP 插件支持在一个配置文件中配置多个服务器。&lt;/p&gt;
&lt;h3 id=&#34;配置多个服务器&#34;&gt;配置多个服务器&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;sftp.json&lt;/code&gt; 中使用数组格式配置多个服务器：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;开发环境&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;192.168.1.101&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;developer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;dev_password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;protocol&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sftp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;remotePath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/developer/project&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;.vscode/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;.git/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_modules/**&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;测试环境&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;192.168.1.102&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tester&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;test_password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;protocol&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sftp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;remotePath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/var/www/test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;.vscode/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;.git/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_modules/**&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;生产环境&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;192.168.1.103&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deployer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;privateKeyPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~/.ssh/production_key&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;protocol&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sftp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;remotePath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/var/www/production&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;.vscode/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;.git/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_modules/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s2&#34;&gt;&amp;#34;*.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;切换服务器&#34;&gt;切换服务器&lt;/h3&gt;
&lt;p&gt;配置多个服务器后，每次执行上传、下载等操作时，VSCode 会弹出选择框，让你选择目标服务器。&lt;/p&gt;
&lt;p&gt;也可以通过命令面板 &lt;code&gt;SFTP: List All&lt;/code&gt; 查看所有配置的服务器。&lt;/p&gt;
&lt;h2 id=&#34;最佳实践&#34;&gt;最佳实践&lt;/h2&gt;
&lt;h3 id=&#34;1-安全配置建议&#34;&gt;1. 安全配置建议&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;不要将密码明文写入配置文件：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;方案一：使用环境变量&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${env.SFTP_USER}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${env.SFTP_PASSWORD}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;方案二：使用密钥认证（推荐）&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deployer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;privateKeyPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~/.ssh/id_rsa&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;将配置文件加入 .gitignore：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.vscode/sftp.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果需要团队共享配置，可以创建一个 &lt;code&gt;sftp.json.example&lt;/code&gt; 模板文件，将敏感信息替换为占位符：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;示例配置&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your_server_ip&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your_username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your_password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;remotePath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/path/to/project&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-工作流建议&#34;&gt;2. 工作流建议&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;开发阶段：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;downloadOnOpen&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;watcher&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;autoUpload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;部署阶段：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;syncOption&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;delete&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-团队协作建议&#34;&gt;3. 团队协作建议&lt;/h3&gt;
&lt;p&gt;为团队创建统一的配置模板：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;团队开发服务器&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${env.DEV_SERVER}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${env.DEV_USER}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${env.DEV_PASSWORD}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;remotePath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/${env.DEV_USER}/project&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;uploadOnSave&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;.vscode/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;.git/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;.env&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_modules/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;*.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;团队成员在本地创建 &lt;code&gt;.env&lt;/code&gt; 文件配置自己的环境变量即可。&lt;/p&gt;
&lt;h3 id=&#34;4-性能优化建议&#34;&gt;4. 性能优化建议&lt;/h3&gt;
&lt;p&gt;对于大型项目，建议优化以下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;concurrency&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignore&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_modules/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;dist/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;build/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;.git/**&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;**/*.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;syncOption&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;常见问题&#34;&gt;常见问题&lt;/h2&gt;
&lt;h3 id=&#34;1-连接超时&#34;&gt;1. 连接超时&lt;/h3&gt;
&lt;p&gt;**问题：**上传文件时提示连接超时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查服务器 IP 和端口是否正确&lt;/li&gt;
&lt;li&gt;检查防火墙是否开放对应端口&lt;/li&gt;
&lt;li&gt;增加 &lt;code&gt;connectTimeout&lt;/code&gt; 参数：
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;connectTimeout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;30000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-权限不足&#34;&gt;2. 权限不足&lt;/h3&gt;
&lt;p&gt;**问题：**上传文件提示 &lt;code&gt;Permission denied&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确认登录用户对目标目录有写权限&lt;/li&gt;
&lt;li&gt;在服务器上执行：&lt;code&gt;chmod 755 /target/path&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;或切换到有权限的用户&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-密钥认证失败&#34;&gt;3. 密钥认证失败&lt;/h3&gt;
&lt;p&gt;**问题：**使用密钥登录时提示认证失败。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确认密钥文件路径正确&lt;/li&gt;
&lt;li&gt;确认密钥文件权限：&lt;code&gt;chmod 600 ~/.ssh/id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;如果密钥有密码，需要配置 &lt;code&gt;passphrase&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Windows 用户注意路径格式：&lt;code&gt;C:\\Users\\Name\\.ssh\\id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4-文件同步冲突&#34;&gt;4. 文件同步冲突&lt;/h3&gt;
&lt;p&gt;**问题：**本地和远程文件都被修改，不知道以哪个为准。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;Diff with Remote&lt;/code&gt; 对比差异&lt;/li&gt;
&lt;li&gt;手动合并代码后再上传&lt;/li&gt;
&lt;li&gt;使用版本控制工具如 Git 管理代码&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;5-上传速度慢&#34;&gt;5. 上传速度慢&lt;/h3&gt;
&lt;p&gt;**问题：**上传大量文件时速度很慢。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;增加并发数：&lt;code&gt;&amp;quot;concurrency&amp;quot;: 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;完善 &lt;code&gt;ignore&lt;/code&gt; 配置，避免上传不必要的文件&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;Sync Local -&amp;gt; Remote&lt;/code&gt; 而不是 &lt;code&gt;Upload Folder&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/liximomo/vscode-sftp&#34;&gt;SFTP 插件官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.ssh.com/academy/ssh/keygen&#34;&gt;SSH 密钥认证配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02&#34;&gt;SFTP 协议介绍&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<p>在日常开发中，我们经常需要在本地编写代码，然后将代码同步到远程服务器进行测试或部署。传统的方式是使用 FileZilla、WinSCP 等第三方工具手动上传文件，这种方式不仅繁琐，而且容易出错。VSCode 的 SFTP 插件可以帮助我们实现本地代码与远程服务器的自动同步，大大提高开发效率。</p>
<h2 id="应用场景">应用场景</h2>
<p>SFTP 插件适用于以下场景：</p>
<ul>
<li><strong>本地开发，远程调试</strong>：在本地编写代码，保存后自动同步到远程服务器，无需手动上传</li>
<li><strong>快速部署</strong>：本地测试通过后，一键同步到生产环境服务器</li>
<li><strong>多服务器管理</strong>：同时管理多个远程服务器，快速切换上传目标</li>
<li><strong>团队协作</strong>：统一开发环境，确保代码在服务器端运行一致</li>
</ul>
<h2 id="安装插件">安装插件</h2>
<p>打开 VSCode，按下快捷键 <code>Ctrl + Shift + X</code> 打开扩展商店，搜索 <code>sftp</code>，找到名为 <strong>SFTP</strong> 的插件（作者：Natizyskunk），这是安装量最高、最受欢迎的 SFTP 插件。点击安装即可。</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/20220104114810.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220104114810.png" alt="SFTP插件"  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>安装完成后，VSCode 会在侧边栏显示 SFTP 图标，表示插件已成功安装。</p>
<h2 id="配置插件">配置插件</h2>
<h3 id="生成配置文件">生成配置文件</h3>
<p>插件安装完成后，我们需要为当前项目创建配置文件。有两种方式：</p>
<p><strong>方式一：使用命令面板</strong></p>
<ol>
<li>按下快捷键 <code>Ctrl + Shift + P</code> 打开命令面板</li>
<li>输入 <code>SFTP: Config</code> 并回车</li>
<li>VSCode 会在当前工作区的 <code>.vscode</code> 文件夹下自动生成 <code>sftp.json</code> 配置文件</li>
</ol>
<p><strong>方式二：右键菜单</strong></p>
<ol>
<li>在 VSCode 资源管理器中，右键点击项目文件夹</li>
<li>选择 <code>SFTP: Config</code></li>
<li>同样会生成 <code>sftp.json</code> 配置文件</li>
</ol>
<h3 id="基础配置示例">基础配置示例</h3>
<p>打开生成的 <code>sftp.json</code> 文件，你会看到一个默认的配置模板。下面是一个基础配置示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;我的服务器&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;192.168.1.100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;protocol&#34;</span><span class="p">:</span> <span class="s2">&#34;sftp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;port&#34;</span><span class="p">:</span> <span class="mi">22</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;root&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;your_password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;remotePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/var/www/project&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;useTempFile&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;openSsh&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="完整配置示例">完整配置示例</h3>
<p>下面是一个包含更多选项的完整配置示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;生产环境服务器&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;192.168.1.100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;protocol&#34;</span><span class="p">:</span> <span class="s2">&#34;sftp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;port&#34;</span><span class="p">:</span> <span class="mi">22</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;deployer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;your_password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;remotePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/home/deployer/project&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;context&#34;</span><span class="p">:</span> <span class="s2">&#34;./&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;useTempFile&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;openSsh&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;downloadOnOpen&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ignore&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;.vscode&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;.git&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;.DS_Store&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;node_modules&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;*.log&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;*.tmp&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;watcher&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="s2">&#34;**/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;autoUpload&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;autoDelete&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;syncOption&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;delete&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;skipCreate&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ignoreExisting&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;update&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sshConfigPath&#34;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;concurrency&#34;</span><span class="p">:</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="配置参数详解">配置参数详解</h2>
<h3 id="连接配置参数">连接配置参数</h3>
<table>
  <thead>
      <tr>
          <th>参数</th>
          <th>类型</th>
          <th>说明</th>
          <th>示例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>name</code></td>
          <td>String</td>
          <td>配置名称，用于区分多个服务器配置</td>
          <td><code>&quot;生产环境&quot;</code></td>
      </tr>
      <tr>
          <td><code>host</code></td>
          <td>String</td>
          <td>服务器 IP 地址或域名（必填）</td>
          <td><code>&quot;192.168.1.100&quot;</code></td>
      </tr>
      <tr>
          <td><code>port</code></td>
          <td>Number</td>
          <td>端口号，SFTP 默认 22，FTP 默认 21</td>
          <td><code>22</code></td>
      </tr>
      <tr>
          <td><code>protocol</code></td>
          <td>String</td>
          <td>传输协议，可选 <code>sftp</code> 或 <code>ftp</code></td>
          <td><code>&quot;sftp&quot;</code></td>
      </tr>
      <tr>
          <td><code>username</code></td>
          <td>String</td>
          <td>登录用户名（必填）</td>
          <td><code>&quot;root&quot;</code></td>
      </tr>
      <tr>
          <td><code>password</code></td>
          <td>String</td>
          <td>登录密码</td>
          <td><code>&quot;your_password&quot;</code></td>
      </tr>
  </tbody>
</table>
<h3 id="认证方式参数">认证方式参数</h3>
<p>SFTP 插件支持三种认证方式：</p>
<h4 id="1-密码认证最简单">1. 密码认证（最简单）</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;root&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;your_password&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="2-密钥认证推荐">2. 密钥认证（推荐）</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;root&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;privateKeyPath&#34;</span><span class="p">:</span> <span class="s2">&#34;C:\\Users\\YourName\\.ssh\\id_rsa&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;passphrase&#34;</span><span class="p">:</span> <span class="s2">&#34;密钥密码（如果有的话）&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>密钥文件路径说明：</strong></p>
<ul>
<li>Windows: <code>C:\\Users\\YourName\\.ssh\\id_rsa</code> 或 <code>~/.ssh/id_rsa</code></li>
<li>Linux/Mac: <code>~/.ssh/id_rsa</code></li>
<li>支持 OpenSSH 格式和 PPK 格式的密钥</li>
</ul>
<h4 id="3-ssh-config-配置高级">3. SSH Config 配置（高级）</h4>
<p>如果你已经配置了 SSH config 文件，可以直接引用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sshConfigPath&#34;</span><span class="p">:</span> <span class="s2">&#34;~/.ssh/config&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;myserver&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>对应的 SSH config 文件内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Host myserver
</span></span><span class="line"><span class="cl">    HostName 192.168.1.100
</span></span><span class="line"><span class="cl">    User deployer
</span></span><span class="line"><span class="cl">    Port 22
</span></span><span class="line"><span class="cl">    IdentityFile ~/.ssh/id_rsa
</span></span></code></pre></div><h3 id="路径配置参数">路径配置参数</h3>
<table>
  <thead>
      <tr>
          <th>参数</th>
          <th>类型</th>
          <th>说明</th>
          <th>示例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>remotePath</code></td>
          <td>String</td>
          <td>远程服务器的目标路径（必填）</td>
          <td><code>&quot;/var/www/project&quot;</code></td>
      </tr>
      <tr>
          <td><code>context</code></td>
          <td>String</td>
          <td>本地项目的相对路径，默认为 <code>./</code></td>
          <td><code>&quot;./src&quot;</code></td>
      </tr>
  </tbody>
</table>
<p><strong>路径配置说明：</strong></p>
<p>假设本地项目结构如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">project/
</span></span><span class="line"><span class="cl">├── src/
</span></span><span class="line"><span class="cl">│   ├── index.js
</span></span><span class="line"><span class="cl">│   └── utils.js
</span></span><span class="line"><span class="cl">├── tests/
</span></span><span class="line"><span class="cl">└── .vscode/
</span></span><span class="line"><span class="cl">    └── sftp.json
</span></span></code></pre></div><ul>
<li>如果 <code>context</code> 为 <code>&quot;./&quot;</code> 且 <code>remotePath</code> 为 <code>&quot;/var/www/project&quot;</code>，则 <code>src/index.js</code> 会上传到 <code>/var/www/project/src/index.js</code></li>
<li>如果 <code>context</code> 为 <code>&quot;./src&quot;</code> 且 <code>remotePath</code> 为 <code>&quot;/var/www/project&quot;</code>，则 <code>src/index.js</code> 会上传到 <code>/var/www/project/index.js</code></li>
</ul>
<h3 id="自动同步参数">自动同步参数</h3>
<table>
  <thead>
      <tr>
          <th>参数</th>
          <th>类型</th>
          <th>说明</th>
          <th>默认值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>uploadOnSave</code></td>
          <td>Boolean</td>
          <td>保存文件时自动上传</td>
          <td><code>false</code></td>
      </tr>
      <tr>
          <td><code>downloadOnOpen</code></td>
          <td>Boolean</td>
          <td>打开文件时自动下载最新版本</td>
          <td><code>false</code></td>
      </tr>
      <tr>
          <td><code>useTempFile</code></td>
          <td>Boolean</td>
          <td>上传时使用临时文件（避免覆盖）</td>
          <td><code>false</code></td>
      </tr>
  </tbody>
</table>
<p><strong>最佳实践建议：</strong></p>
<ul>
<li>开发环境：设置 <code>uploadOnSave: true</code>，方便快速同步</li>
<li>生产环境：设置 <code>uploadOnSave: false</code>，避免误操作</li>
</ul>
<h3 id="忽略文件配置">忽略文件配置</h3>
<p><code>ignore</code> 参数用于指定哪些文件或文件夹不需要同步，支持 glob 模式：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ignore&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/.vscode/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/.git/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/.DS_Store&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/node_modules/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/*.log&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/*.tmp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/dist/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/coverage/**&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>常用忽略模式：</strong></p>
<ul>
<li><code>**/.git/**</code>：忽略 Git 版本控制文件</li>
<li><code>**/node_modules/**</code>：忽略 Node.js 依赖包</li>
<li><code>**/*.log</code>：忽略所有日志文件</li>
<li><code>**/dist/**</code>：忽略构建输出目录</li>
</ul>
<h3 id="监听器配置watcher">监听器配置（Watcher）</h3>
<p>监听器可以监控文件变化并自动执行同步操作：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;watcher&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;files&#34;</span><span class="p">:</span> <span class="s2">&#34;**/*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;autoUpload&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;autoDelete&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>参数</th>
          <th>类型</th>
          <th>说明</th>
          <th>默认值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>files</code></td>
          <td>String</td>
          <td>监听的文件模式（glob）</td>
          <td><code>false</code></td>
      </tr>
      <tr>
          <td><code>autoUpload</code></td>
          <td>Boolean</td>
          <td>文件变化时自动上传</td>
          <td><code>false</code></td>
      </tr>
      <tr>
          <td><code>autoDelete</code></td>
          <td>Boolean</td>
          <td>本地删除文件时自动删除远程文件</td>
          <td><code>false</code></td>
      </tr>
  </tbody>
</table>
<p><strong>注意事项：</strong></p>
<ul>
<li><code>autoDelete: true</code> 可能导致误删除，请谨慎使用</li>
<li>监听器会持续运行，可能影响性能，建议按需启用</li>
</ul>
<h3 id="同步选项sync-option">同步选项（Sync Option）</h3>
<p><code>syncOption</code> 用于配置手动同步时的行为：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;syncOption&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;delete&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;skipCreate&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ignoreExisting&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;update&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>参数</th>
          <th>类型</th>
          <th>说明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>delete</code></td>
          <td>Boolean</td>
          <td>删除远程服务器上多余的文件</td>
      </tr>
      <tr>
          <td><code>skipCreate</code></td>
          <td>Boolean</td>
          <td>跳过创建新文件</td>
      </tr>
      <tr>
          <td><code>ignoreExisting</code></td>
          <td>Boolean</td>
          <td>忽略已存在的文件</td>
      </tr>
      <tr>
          <td><code>update</code></td>
          <td>Boolean</td>
          <td>更新已修改的文件</td>
      </tr>
  </tbody>
</table>
<h3 id="性能优化参数">性能优化参数</h3>
<table>
  <thead>
      <tr>
          <th>参数</th>
          <th>类型</th>
          <th>说明</th>
          <th>默认值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>concurrency</code></td>
          <td>Number</td>
          <td>并发上传的文件数量</td>
          <td><code>4</code></td>
      </tr>
      <tr>
          <td><code>connectTimeout</code></td>
          <td>Number</td>
          <td>连接超时时间（毫秒）</td>
          <td><code>10000</code></td>
      </tr>
      <tr>
          <td><code>keepalive</code></td>
          <td>Number</td>
          <td>保持连接的间隔时间（毫秒）</td>
          <td><code>10000</code></td>
      </tr>
  </tbody>
</table>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;concurrency&#34;</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;connectTimeout&#34;</span><span class="p">:</span> <span class="mi">15000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;keepalive&#34;</span><span class="p">:</span> <span class="mi">5000</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>性能优化建议：</strong></p>
<ul>
<li>网络环境好时，可以增加 <code>concurrency</code> 提高上传速度</li>
<li>网络不稳定时，增加 <code>connectTimeout</code> 避免连接超时</li>
<li>频繁操作时，减小 <code>keepalive</code> 保持连接活跃</li>
</ul>
<h2 id="插件使用">插件使用</h2>
<h3 id="常用操作">常用操作</h3>
<p>安装并配置好插件后，可以通过以下方式使用：</p>
<h4 id="1-上传文件或文件夹">1. 上传文件或文件夹</h4>
<p><strong>方式一：右键菜单</strong></p>
<ul>
<li>在资源管理器中右键点击文件或文件夹</li>
<li>选择 <code>Upload</code> 上传到服务器</li>
</ul>
<p><strong>方式二：命令面板</strong></p>
<ul>
<li>按 <code>Ctrl + Shift + P</code> 打开命令面板</li>
<li>输入 <code>SFTP: Upload</code> 并选择相应的命令</li>
</ul>
<h4 id="2-下载文件或文件夹">2. 下载文件或文件夹</h4>
<ul>
<li>右键点击文件或文件夹</li>
<li>选择 <code>Download</code> 从服务器下载</li>
</ul>
<h4 id="3-同步操作">3. 同步操作</h4>
<p><strong>同步本地到远程：</strong></p>
<ul>
<li>命令面板输入 <code>SFTP: Sync Local -&gt; Remote</code></li>
<li>将本地所有文件同步到服务器</li>
</ul>
<p><strong>同步远程到本地：</strong></p>
<ul>
<li>命令面板输入 <code>SFTP: Sync Remote -&gt; Local</code></li>
<li>将服务器文件同步到本地</li>
</ul>
<p><strong>双向同步：</strong></p>
<ul>
<li>命令面板输入 <code>SFTP: Sync Both Directions</code></li>
<li>根据文件修改时间自动判断同步方向</li>
</ul>
<h4 id="4-对比文件差异">4. 对比文件差异</h4>
<ul>
<li>右键点击文件</li>
<li>选择 <code>Diff with Remote</code></li>
<li>VSCode 会显示本地文件和远程文件的差异</li>
</ul>
<h4 id="5-列出远程文件">5. 列出远程文件</h4>
<ul>
<li>命令面板输入 <code>SFTP: List</code></li>
<li>可以浏览远程服务器的文件目录</li>
</ul>
<h3 id="完整命令列表">完整命令列表</h3>
<p>按 <code>Ctrl + Shift + P</code> 打开命令面板，输入 <code>SFTP</code> 可以看到所有可用命令：</p>
<table>
  <thead>
      <tr>
          <th>命令</th>
          <th>说明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>SFTP: Config</code></td>
          <td>创建或编辑配置文件</td>
      </tr>
      <tr>
          <td><code>SFTP: Upload</code></td>
          <td>上传当前文件</td>
      </tr>
      <tr>
          <td><code>SFTP: Upload Folder</code></td>
          <td>上传整个文件夹</td>
      </tr>
      <tr>
          <td><code>SFTP: Download</code></td>
          <td>下载当前文件</td>
      </tr>
      <tr>
          <td><code>SFTP: Download Folder</code></td>
          <td>下载整个文件夹</td>
      </tr>
      <tr>
          <td><code>SFTP: Sync Local -&gt; Remote</code></td>
          <td>本地同步到远程</td>
      </tr>
      <tr>
          <td><code>SFTP: Sync Remote -&gt; Local</code></td>
          <td>远程同步到本地</td>
      </tr>
      <tr>
          <td><code>SFTP: Sync Both Directions</code></td>
          <td>双向同步</td>
      </tr>
      <tr>
          <td><code>SFTP: Diff with Remote</code></td>
          <td>对比本地和远程文件</td>
      </tr>
      <tr>
          <td><code>SFTP: List</code></td>
          <td>列出远程目录</td>
      </tr>
      <tr>
          <td><code>SFTP: List All</code></td>
          <td>列出所有配置的服务器</td>
      </tr>
      <tr>
          <td><code>SFTP: Delete</code></td>
          <td>删除远程文件</td>
      </tr>
  </tbody>
</table>
<h2 id="多服务器配置">多服务器配置</h2>
<p>在实际开发中，我们可能需要同时管理开发环境、测试环境和生产环境多个服务器。SFTP 插件支持在一个配置文件中配置多个服务器。</p>
<h3 id="配置多个服务器">配置多个服务器</h3>
<p>在 <code>sftp.json</code> 中使用数组格式配置多个服务器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;开发环境&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;192.168.1.101&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;port&#34;</span><span class="p">:</span> <span class="mi">22</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;developer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;dev_password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;protocol&#34;</span><span class="p">:</span> <span class="s2">&#34;sftp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;remotePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/home/developer/project&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ignore&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;.vscode/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;.git/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;node_modules/**&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;测试环境&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;192.168.1.102&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;port&#34;</span><span class="p">:</span> <span class="mi">22</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;tester&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;test_password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;protocol&#34;</span><span class="p">:</span> <span class="s2">&#34;sftp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;remotePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/var/www/test&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ignore&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;.vscode/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;.git/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;node_modules/**&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;生产环境&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;192.168.1.103&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;port&#34;</span><span class="p">:</span> <span class="mi">22</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;deployer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;privateKeyPath&#34;</span><span class="p">:</span> <span class="s2">&#34;~/.ssh/production_key&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;protocol&#34;</span><span class="p">:</span> <span class="s2">&#34;sftp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;remotePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/var/www/production&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;ignore&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;.vscode/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;.git/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;node_modules/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;*.log&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></div><h3 id="切换服务器">切换服务器</h3>
<p>配置多个服务器后，每次执行上传、下载等操作时，VSCode 会弹出选择框，让你选择目标服务器。</p>
<p>也可以通过命令面板 <code>SFTP: List All</code> 查看所有配置的服务器。</p>
<h2 id="最佳实践">最佳实践</h2>
<h3 id="1-安全配置建议">1. 安全配置建议</h3>
<p><strong>不要将密码明文写入配置文件：</strong></p>
<p>方案一：使用环境变量</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;${env.SFTP_USER}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;${env.SFTP_PASSWORD}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>方案二：使用密钥认证（推荐）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;deployer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;privateKeyPath&#34;</span><span class="p">:</span> <span class="s2">&#34;~/.ssh/id_rsa&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>将配置文件加入 .gitignore：</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">.vscode/sftp.json
</span></span></code></pre></div><p>如果需要团队共享配置，可以创建一个 <code>sftp.json.example</code> 模板文件，将敏感信息替换为占位符：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;示例配置&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;your_server_ip&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;your_username&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;your_password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;remotePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/path/to/project&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="2-工作流建议">2. 工作流建议</h3>
<p><strong>开发阶段：</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;downloadOnOpen&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;watcher&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;autoUpload&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><strong>部署阶段：</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;syncOption&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;update&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;delete&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="3-团队协作建议">3. 团队协作建议</h3>
<p>为团队创建统一的配置模板：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;团队开发服务器&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;${env.DEV_SERVER}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;username&#34;</span><span class="p">:</span> <span class="s2">&#34;${env.DEV_USER}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;${env.DEV_PASSWORD}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;remotePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/home/${env.DEV_USER}/project&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;uploadOnSave&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ignore&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;.vscode/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;.git/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;.env&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;node_modules/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;*.log&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>团队成员在本地创建 <code>.env</code> 文件配置自己的环境变量即可。</p>
<h3 id="4-性能优化建议">4. 性能优化建议</h3>
<p>对于大型项目，建议优化以下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;concurrency&#34;</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ignore&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;node_modules/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;dist/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;build/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;.git/**&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;**/*.log&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;syncOption&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;update&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="常见问题">常见问题</h2>
<h3 id="1-连接超时">1. 连接超时</h3>
<p>**问题：**上传文件时提示连接超时。</p>
<p><strong>解决方案：</strong></p>
<ul>
<li>检查服务器 IP 和端口是否正确</li>
<li>检查防火墙是否开放对应端口</li>
<li>增加 <code>connectTimeout</code> 参数：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;connectTimeout&#34;</span><span class="p">:</span> <span class="mi">30000</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="2-权限不足">2. 权限不足</h3>
<p>**问题：**上传文件提示 <code>Permission denied</code>。</p>
<p><strong>解决方案：</strong></p>
<ul>
<li>确认登录用户对目标目录有写权限</li>
<li>在服务器上执行：<code>chmod 755 /target/path</code></li>
<li>或切换到有权限的用户</li>
</ul>
<h3 id="3-密钥认证失败">3. 密钥认证失败</h3>
<p>**问题：**使用密钥登录时提示认证失败。</p>
<p><strong>解决方案：</strong></p>
<ul>
<li>确认密钥文件路径正确</li>
<li>确认密钥文件权限：<code>chmod 600 ~/.ssh/id_rsa</code></li>
<li>如果密钥有密码，需要配置 <code>passphrase</code></li>
<li>Windows 用户注意路径格式：<code>C:\\Users\\Name\\.ssh\\id_rsa</code></li>
</ul>
<h3 id="4-文件同步冲突">4. 文件同步冲突</h3>
<p>**问题：**本地和远程文件都被修改，不知道以哪个为准。</p>
<p><strong>解决方案：</strong></p>
<ul>
<li>使用 <code>Diff with Remote</code> 对比差异</li>
<li>手动合并代码后再上传</li>
<li>使用版本控制工具如 Git 管理代码</li>
</ul>
<h3 id="5-上传速度慢">5. 上传速度慢</h3>
<p>**问题：**上传大量文件时速度很慢。</p>
<p><strong>解决方案：</strong></p>
<ul>
<li>增加并发数：<code>&quot;concurrency&quot;: 10</code></li>
<li>完善 <code>ignore</code> 配置，避免上传不必要的文件</li>
<li>使用 <code>Sync Local -&gt; Remote</code> 而不是 <code>Upload Folder</code></li>
</ul>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="https://github.com/liximomo/vscode-sftp">SFTP 插件官方文档</a></li>
<li><a href="https://www.ssh.com/academy/ssh/keygen">SSH 密钥认证配置</a></li>
<li><a href="https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02">SFTP 协议介绍</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Quest3 激活教程 - 使用 Netch</title>
      <link>https://lifeislife.cn/posts/quest3%E6%BF%80%E6%B4%BB%E6%95%99%E7%A8%8B-%E4%BD%BF%E7%94%A8netch/</link>
      <pubDate>Tue, 11 Nov 2025 10:00:00 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/quest3%E6%BF%80%E6%B4%BB%E6%95%99%E7%A8%8B-%E4%BD%BF%E7%94%A8netch/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;p&gt;Meta Quest 3 是一款优秀的 VR 一体机，但在国内激活存在网络限制问题。本文将介绍如何使用 Netch 工具，通过 Windows 电脑配置网络代理，实现 Quest 3 在国内的顺利激活。&lt;/p&gt;
&lt;h2 id=&#34;准备工作&#34;&gt;准备工作&lt;/h2&gt;
&lt;p&gt;在开始之前，你需要准备以下内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 电脑一台&lt;/li&gt;
&lt;li&gt;一个路由器&lt;/li&gt;
&lt;li&gt;VPN 代理&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;获取代理服务&#34;&gt;获取代理服务&lt;/h3&gt;
&lt;p&gt;首先需要获取一个 VPN 代理服务。可以访问以下任意链接注册，必须支持 UDP 转发，连接 WiFi 时需要对设备进行时间校准，这个校准步骤就同时验证这个 WiFi 是否可以连网，但这个校准是基于 UDP 的，只开启梯子的系统代理只能走 TCP 又不支持 UDP，所以只能上网不能校准时间。绝大部分机场不支持 UDP，你可以咨询机场客服是否支持 UDP 转发，也可以参考这篇文档&lt;a href=&#34;https://ocguide.eyw015.com/quest-guide/basic-net&#34;&gt;Quest 网络受限原因 | Meta Quest 中文指南&lt;/a&gt;，测试是否支持 UDP。&lt;/p&gt;
&lt;p&gt;如果你没有代理，或者代理不支持 UDP，可以考虑使用以下代理，我已经测试过：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://inv05.lmaff01.cc/register?aff=mBYlbuzN&#34;&gt;龙猫云&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://laomao.biz?path=register&amp;amp;code=mXsVHPDT&#34;&gt;老猫云&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://web3jsq.com/f/pO3Xzwo2&#34;&gt;Web3加速器&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选最便宜的买就行，买一个月，后续可能会用其他不支持 UDP 便宜的机场，所以不要买贵的。我之前用的机场就不支持 UDP，但是平时使用完全够用，只有在 Quest3 激活是用到过这一次 UDP。&lt;/p&gt;
&lt;p&gt;日常使用可以考虑便宜大碗的&lt;a href=&#34;https://bo5yk-c42pu-oc17a-l1gys.glados.space&#34;&gt;Glados&lt;/a&gt;，目前至少稳定运行了10年了。&lt;/p&gt;
&lt;p&gt;机场注册之后注意看一下是否支持导出订阅地址，有一些机场只能使用自己的客户端，那就无法使用 Netch。请一定要注意！！！因为文章可能维护不及时，我也不保证上面的机场是否还支持导出订阅地址。之前使用过万达云的机场，用了一天就因为无法导出订阅地址无法使用了。他家改成只能使用自己的客户端了。&lt;/p&gt;
&lt;h2 id=&#34;netch-配置步骤&#34;&gt;Netch 配置步骤&lt;/h2&gt;
&lt;h3 id=&#34;下载-netch&#34;&gt;下载 Netch&lt;/h3&gt;
&lt;p&gt;从&lt;a href=&#34;https://github.com/netchx/Netch/releases/latest&#34;&gt;Netch 开源主页&lt;/a&gt;下载最新版本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重要提示&lt;/strong&gt;：使用 Netch 时不能同时开启其他代理软件！如果之前有开启其他代理，请全部关闭后重启电脑。&lt;/p&gt;
&lt;h3 id=&#34;安装-netch&#34;&gt;安装 Netch&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;解压下载的压缩包到任意磁盘根目录&lt;/li&gt;
&lt;li&gt;运行 &lt;code&gt;Netch.exe&lt;/code&gt; 文件&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;安装-npcap&#34;&gt;安装 Npcap&lt;/h2&gt;
&lt;p&gt;前往官网下载 Npcap 并安装&lt;a href=&#34;https://npcap.com/#download&#34;&gt;Npcap: Windows Packet Capture Library &amp;amp; Driver&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;安装时选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support raw 802.11 traffic&lt;/li&gt;
&lt;li&gt;Install Npcap in WinPcap API-Compatible Mode&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;添加订阅&#34;&gt;添加订阅&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击上方的&amp;quot;订阅&amp;quot; → &amp;ldquo;管理订阅&amp;rdquo;
&lt;ol&gt;
&lt;li&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//2025/11/22/907ee0115ca6687201ec8ef22ce65f6f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/907ee0115ca6687201ec8ef22ce65f6f.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;添加订阅链接
&lt;ol&gt;
&lt;li&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//2025/11/22/0ed0fef7e6bb5fddc26af07472a203d6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/0ed0fef7e6bb5fddc26af07472a203d6.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;更新服务器&#34;&gt;更新服务器&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击上方的&amp;quot;订阅&amp;quot; → &amp;ldquo;更新服务器&amp;rdquo;
&lt;ol&gt;
&lt;li&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//2025/11/22/ebaaaf4d943d5e11a691b8ce24810aa0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/ebaaaf4d943d5e11a691b8ce24810aa0.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;如果更新失败，检查订阅链接是否正确，确保关闭所有其他代理软件&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;选择节点&#34;&gt;选择节点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击&amp;quot;服务器&amp;quot;，选择一个节点，优先选择美国节点&lt;/li&gt;
&lt;li&gt;如果是自己的代理服务，可以多尝试不同节点&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;选择模式&#34;&gt;选择模式&lt;/h3&gt;
&lt;p&gt;点击&amp;quot;模式&amp;quot;，选择 &lt;code&gt;[pcap2socks]Tencent(10.6.0.1)Preset&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//2025/11/22/7423dabd4ac294679b1b63c6501e5aaf.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/7423dabd4ac294679b1b63c6501e5aaf.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;配置-dns&#34;&gt;配置 DNS&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击&amp;quot;设置&amp;quot;
&lt;ol&gt;
&lt;li&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//2025/11/22/ba9f42e7572521f32c87fab7264ebe3f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/ba9f42e7572521f32c87fab7264ebe3f.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;切换到&amp;quot;WinTUN&amp;quot;选项卡&lt;/li&gt;
&lt;li&gt;勾选&amp;quot;使用自定义 DNS&amp;quot;&lt;/li&gt;
&lt;li&gt;勾选&amp;quot;代理 DNS&amp;quot;&lt;/li&gt;
&lt;li&gt;将 DNS 改为 &lt;code&gt;1.1.1.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;点击右下角&amp;quot;保存&amp;quot;&lt;/li&gt;
&lt;/ol&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//2025/11/22/2e5823713f26843a52c50e8c37de9665.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/2e5823713f26843a52c50e8c37de9665.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;启动-netch&#34;&gt;启动 Netch&lt;/h3&gt;
&lt;p&gt;点击右下角的&amp;quot;启动&amp;quot;按钮，等待启动完成。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;判断启动成功&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;启动&amp;quot;按钮变成&amp;quot;停止&amp;rdquo;&lt;/li&gt;
&lt;li&gt;下方出现绿色圆形标志&lt;/li&gt;
&lt;/ul&gt;
&lt;p&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//2025/11/22/8495e7eaf32baee027ac3be82535170c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/8495e7eaf32baee027ac3be82535170c.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;h2 id=&#34;quest-3-激活流程&#34;&gt;Quest 3 激活流程&lt;/h2&gt;
&lt;h3 id=&#34;1-连接-wifi&#34;&gt;1. 连接 WiFi&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;启动 Quest 3&lt;/li&gt;
&lt;li&gt;在语言选择界面选择&amp;quot;简体中文&amp;quot;&lt;/li&gt;
&lt;li&gt;选择&amp;quot;在头戴设备中输入 WIFI 信息&amp;quot;&lt;/li&gt;
&lt;li&gt;选择你的路由器 WiFi，不要点击连接，需要后面配置&lt;strong&gt;高级选项&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-配置静态-ip&#34;&gt;2. 配置静态 IP&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;输入 WiFi 密码后，点击&amp;quot;高级选项&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将 IP 设置从&amp;quot;DHCP&amp;quot;改为&amp;quot;静态&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置静态 IP 地址&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;IP 地址：10.6.0.1&lt;/li&gt;
&lt;li&gt;网关：10.6.0.2&lt;/li&gt;
&lt;li&gt;网络前缀长度：24&lt;/li&gt;
&lt;li&gt;DNS1:8.8.8.8&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;滚动到页面最下方，点击&amp;quot;连接&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;正常情况下点击连接后，Netch 软件旁边的终端窗口会有响应，有一行输出提示某一个 IP 地址设备加入了链接。&lt;/p&gt;
&lt;h3 id=&#34;3-系统更新&#34;&gt;3. 系统更新&lt;/h3&gt;
&lt;p&gt;连接成功后，Quest 3 会自动进入更新流程。按照我的经验，激活过程中经常断连，所以需要多尝试几次。不要开始更新就把机器扔一边，经常看看。&lt;/p&gt;
&lt;h2 id=&#34;常见问题处理&#34;&gt;常见问题处理&lt;/h2&gt;
&lt;h3 id=&#34;提示wrong-stun-server&#34;&gt;提示&amp;quot;Wrong STUN Server&amp;quot;&lt;/h3&gt;
&lt;p&gt;进入设置，将 STUN 服务器改为以下任意一个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stun.voipstunt.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stun.xten.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stun.ekiga.net&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;没有绿点或显示-noudpunsupportedserver&#34;&gt;没有绿点或显示 NOUDP/UnsupportedServer&lt;/h3&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//2025/11/22/c31c0ad35587e4dfea4f1d9f35cbf2e2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/c31c0ad35587e4dfea4f1d9f35cbf2e2.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;&lt;strong&gt;刷新状态&lt;/strong&gt;：点击 NOUDP 刷新几次，如果变成黄点或红点也可能能用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;兼容性问题&lt;/strong&gt;：进入设置，勾选&amp;quot;TLS AllowInsecure&amp;quot;，保存后重试
&lt;ol&gt;
&lt;li&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//2025/11/22/99b7962edd0873414c96a8c0935cbe31.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/99b7962edd0873414c96a8c0935cbe31.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;修改 STUN 服务器&lt;/strong&gt;：同上面的解决方案&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检查其他软件&lt;/strong&gt;：确保关闭所有其他代理软件后重启电脑&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检查节点&lt;/strong&gt;：选择其他节点重试&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重启设备&lt;/strong&gt;：重启路由器和 Netch&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跳过绿点检查&lt;/strong&gt;：如果后续 Quest3 能正常更新（进度条会走），可以不用管绿点&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;提示找不到-vcruntime140dll&#34;&gt;提示&amp;quot;找不到 VCRUNTIME140.dll&amp;quot;&lt;/h3&gt;
&lt;p&gt;搜索下载&amp;quot;Windows 常用运行库&amp;quot;并安装。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>Meta Quest 3 是一款优秀的 VR 一体机，但在国内激活存在网络限制问题。本文将介绍如何使用 Netch 工具，通过 Windows 电脑配置网络代理，实现 Quest 3 在国内的顺利激活。</p>
<h2 id="准备工作">准备工作</h2>
<p>在开始之前，你需要准备以下内容：</p>
<ul>
<li>Windows 电脑一台</li>
<li>一个路由器</li>
<li>VPN 代理</li>
</ul>
<h3 id="获取代理服务">获取代理服务</h3>
<p>首先需要获取一个 VPN 代理服务。可以访问以下任意链接注册，必须支持 UDP 转发，连接 WiFi 时需要对设备进行时间校准，这个校准步骤就同时验证这个 WiFi 是否可以连网，但这个校准是基于 UDP 的，只开启梯子的系统代理只能走 TCP 又不支持 UDP，所以只能上网不能校准时间。绝大部分机场不支持 UDP，你可以咨询机场客服是否支持 UDP 转发，也可以参考这篇文档<a href="https://ocguide.eyw015.com/quest-guide/basic-net">Quest 网络受限原因 | Meta Quest 中文指南</a>，测试是否支持 UDP。</p>
<p>如果你没有代理，或者代理不支持 UDP，可以考虑使用以下代理，我已经测试过：</p>
<ul>
<li><a href="https://inv05.lmaff01.cc/register?aff=mBYlbuzN">龙猫云</a></li>
<li><a href="https://laomao.biz?path=register&amp;code=mXsVHPDT">老猫云</a></li>
<li><a href="https://web3jsq.com/f/pO3Xzwo2">Web3加速器</a></li>
</ul>
<p>选最便宜的买就行，买一个月，后续可能会用其他不支持 UDP 便宜的机场，所以不要买贵的。我之前用的机场就不支持 UDP，但是平时使用完全够用，只有在 Quest3 激活是用到过这一次 UDP。</p>
<p>日常使用可以考虑便宜大碗的<a href="https://bo5yk-c42pu-oc17a-l1gys.glados.space">Glados</a>，目前至少稳定运行了10年了。</p>
<p>机场注册之后注意看一下是否支持导出订阅地址，有一些机场只能使用自己的客户端，那就无法使用 Netch。请一定要注意！！！因为文章可能维护不及时，我也不保证上面的机场是否还支持导出订阅地址。之前使用过万达云的机场，用了一天就因为无法导出订阅地址无法使用了。他家改成只能使用自己的客户端了。</p>
<h2 id="netch-配置步骤">Netch 配置步骤</h2>
<h3 id="下载-netch">下载 Netch</h3>
<p>从<a href="https://github.com/netchx/Netch/releases/latest">Netch 开源主页</a>下载最新版本。</p>
<p><strong>重要提示</strong>：使用 Netch 时不能同时开启其他代理软件！如果之前有开启其他代理，请全部关闭后重启电脑。</p>
<h3 id="安装-netch">安装 Netch</h3>
<ol>
<li>解压下载的压缩包到任意磁盘根目录</li>
<li>运行 <code>Netch.exe</code> 文件</li>
</ol>
<h2 id="安装-npcap">安装 Npcap</h2>
<p>前往官网下载 Npcap 并安装<a href="https://npcap.com/#download">Npcap: Windows Packet Capture Library &amp; Driver</a>。</p>
<p>安装时选择：</p>
<ul>
<li>Support raw 802.11 traffic</li>
<li>Install Npcap in WinPcap API-Compatible Mode</li>
</ul>
<h3 id="添加订阅">添加订阅</h3>
<ol>
<li>点击上方的&quot;订阅&quot; → &ldquo;管理订阅&rdquo;
<ol>
<li>

<!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//2025/11/22/907ee0115ca6687201ec8ef22ce65f6f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/907ee0115ca6687201ec8ef22ce65f6f.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></li>
</ol>
</li>
<li>添加订阅链接
<ol>
<li>

<!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//2025/11/22/0ed0fef7e6bb5fddc26af07472a203d6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/0ed0fef7e6bb5fddc26af07472a203d6.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></li>
</ol>
</li>
</ol>
<h3 id="更新服务器">更新服务器</h3>
<ol>
<li>点击上方的&quot;订阅&quot; → &ldquo;更新服务器&rdquo;
<ol>
<li>

<!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//2025/11/22/ebaaaf4d943d5e11a691b8ce24810aa0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/ebaaaf4d943d5e11a691b8ce24810aa0.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></li>
</ol>
</li>
<li>如果更新失败，检查订阅链接是否正确，确保关闭所有其他代理软件</li>
</ol>
<h3 id="选择节点">选择节点</h3>
<ol>
<li>点击&quot;服务器&quot;，选择一个节点，优先选择美国节点</li>
<li>如果是自己的代理服务，可以多尝试不同节点</li>
</ol>
<h3 id="选择模式">选择模式</h3>
<p>点击&quot;模式&quot;，选择 <code>[pcap2socks]Tencent(10.6.0.1)Preset</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//2025/11/22/7423dabd4ac294679b1b63c6501e5aaf.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/7423dabd4ac294679b1b63c6501e5aaf.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="配置-dns">配置 DNS</h3>
<ol>
<li>点击&quot;设置&quot;
<ol>
<li>

<!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//2025/11/22/ba9f42e7572521f32c87fab7264ebe3f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/ba9f42e7572521f32c87fab7264ebe3f.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></li>
</ol>
</li>
<li>切换到&quot;WinTUN&quot;选项卡</li>
<li>勾选&quot;使用自定义 DNS&quot;</li>
<li>勾选&quot;代理 DNS&quot;</li>
<li>将 DNS 改为 <code>1.1.1.1</code></li>
<li>点击右下角&quot;保存&quot;</li>
</ol>
<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//2025/11/22/2e5823713f26843a52c50e8c37de9665.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/2e5823713f26843a52c50e8c37de9665.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="启动-netch">启动 Netch</h3>
<p>点击右下角的&quot;启动&quot;按钮，等待启动完成。</p>
<p><strong>判断启动成功</strong>：</p>
<ul>
<li>&ldquo;启动&quot;按钮变成&quot;停止&rdquo;</li>
<li>下方出现绿色圆形标志</li>
</ul>
<p>软件右侧出现以下终端窗口：</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//2025/11/22/8495e7eaf32baee027ac3be82535170c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/8495e7eaf32baee027ac3be82535170c.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>
<h2 id="quest-3-激活流程">Quest 3 激活流程</h2>
<h3 id="1-连接-wifi">1. 连接 WiFi</h3>
<ol>
<li>启动 Quest 3</li>
<li>在语言选择界面选择&quot;简体中文&quot;</li>
<li>选择&quot;在头戴设备中输入 WIFI 信息&quot;</li>
<li>选择你的路由器 WiFi，不要点击连接，需要后面配置<strong>高级选项</strong></li>
</ol>
<h3 id="2-配置静态-ip">2. 配置静态 IP</h3>
<ol>
<li>
<p>输入 WiFi 密码后，点击&quot;高级选项&quot;</p>
</li>
<li>
<p>将 IP 设置从&quot;DHCP&quot;改为&quot;静态&quot;</p>
</li>
<li>
<p>配置静态 IP 地址</p>
<ol>
<li>IP 地址：10.6.0.1</li>
<li>网关：10.6.0.2</li>
<li>网络前缀长度：24</li>
<li>DNS1:8.8.8.8</li>
</ol>
</li>
<li>
<p>滚动到页面最下方，点击&quot;连接&quot;</p>
</li>
</ol>
<p>正常情况下点击连接后，Netch 软件旁边的终端窗口会有响应，有一行输出提示某一个 IP 地址设备加入了链接。</p>
<h3 id="3-系统更新">3. 系统更新</h3>
<p>连接成功后，Quest 3 会自动进入更新流程。按照我的经验，激活过程中经常断连，所以需要多尝试几次。不要开始更新就把机器扔一边，经常看看。</p>
<h2 id="常见问题处理">常见问题处理</h2>
<h3 id="提示wrong-stun-server">提示&quot;Wrong STUN Server&quot;</h3>
<p>进入设置，将 STUN 服务器改为以下任意一个：</p>
<ul>
<li><code>stun.voipstunt.com</code></li>
<li><code>stun.xten.com</code></li>
<li><code>stun.ekiga.net</code></li>
</ul>
<h3 id="没有绿点或显示-noudpunsupportedserver">没有绿点或显示 NOUDP/UnsupportedServer</h3>
<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//2025/11/22/c31c0ad35587e4dfea4f1d9f35cbf2e2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/c31c0ad35587e4dfea4f1d9f35cbf2e2.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><strong>刷新状态</strong>：点击 NOUDP 刷新几次，如果变成黄点或红点也可能能用</li>
<li><strong>兼容性问题</strong>：进入设置，勾选&quot;TLS AllowInsecure&quot;，保存后重试
<ol>
<li>

<!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//2025/11/22/99b7962edd0873414c96a8c0935cbe31.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/11/22/99b7962edd0873414c96a8c0935cbe31.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></li>
</ol>
</li>
<li><strong>修改 STUN 服务器</strong>：同上面的解决方案</li>
<li><strong>检查其他软件</strong>：确保关闭所有其他代理软件后重启电脑</li>
<li><strong>检查节点</strong>：选择其他节点重试</li>
<li><strong>重启设备</strong>：重启路由器和 Netch</li>
<li><strong>跳过绿点检查</strong>：如果后续 Quest3 能正常更新（进度条会走），可以不用管绿点</li>
</ol>
<h3 id="提示找不到-vcruntime140dll">提示&quot;找不到 VCRUNTIME140.dll&quot;</h3>
<p>搜索下载&quot;Windows 常用运行库&quot;并安装。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Git Bash 终端卡顿排查实录：一次 z 插件数据库过大的坑与高效优化清单</title>
      <link>https://lifeislife.cn/posts/git-bash%E7%BB%88%E7%AB%AF%E5%8D%A1%E9%A1%BF%E6%8E%92%E6%9F%A5%E5%AE%9E%E5%BD%95/</link>
      <pubDate>Tue, 21 Oct 2025 22:00:00 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/git-bash%E7%BB%88%E7%AB%AF%E5%8D%A1%E9%A1%BF%E6%8E%92%E6%9F%A5%E5%AE%9E%E5%BD%95/</guid>
      <description>&lt;p&gt;当终端出现输入卡顿、命令执行后迟迟“回不来”的情况，往往不是单一因素导致。本文记录了我在 Git Bash 上的排查过程：最终发现是 oh-my-zsh 的 &lt;code&gt;z&lt;/code&gt; 插件在家目录累积了大量 &lt;code&gt;.z&lt;/code&gt; 数据文件，引发了明显的慢响应。同时也整理了一份更系统的优化与排错清单，帮助你快速定位并消除常见的性能瓶颈。&lt;/p&gt;
&lt;h2 id=&#34;一现象与环境&#34;&gt;一、现象与环境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Git Bash 终端卡顿：输入有明显延迟，命令执行后迟迟不返回提示符。&lt;/li&gt;
&lt;li&gt;使用的是 &lt;code&gt;zsh&lt;/code&gt;（配合 oh-my-zsh），启用了包括 &lt;code&gt;z&lt;/code&gt; 在内的多个插件。&lt;/li&gt;
&lt;li&gt;家目录出现了很多 &lt;code&gt;.z&lt;/code&gt; 相关文件（&lt;code&gt;z&lt;/code&gt; 插件的跳转数据库），怀疑数据库过大导致慢响应。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;二我的排查与解决过程&#34;&gt;二、我的排查与解决过程&lt;/h2&gt;
&lt;h3 id=&#34;1-先隔离配置用干净的-zsh--f-启动&#34;&gt;1) 先隔离配置：用干净的 &lt;code&gt;zsh -f&lt;/code&gt; 启动&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;zsh -f&lt;/code&gt; 会在不加载 &lt;code&gt;~/.zshrc&lt;/code&gt; 的前提下启动一个“空配置”的 zsh，能快速判断是否是配置或插件导致问题。&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;zsh -f&lt;/code&gt; 下终端恢复顺畅，基本可以确定问题在 &lt;code&gt;~/.zshrc&lt;/code&gt; 或其引入的插件与脚本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-定位嫌疑插件移除-zshrc-中的-z-插件&#34;&gt;2) 定位“嫌疑插件”：移除 &lt;code&gt;~/.zshrc&lt;/code&gt; 中的 &lt;code&gt;z&lt;/code&gt; 插件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;打开 &lt;code&gt;~/.zshrc&lt;/code&gt;，从 &lt;code&gt;plugins=(...)&lt;/code&gt; 中移除 &lt;code&gt;z&lt;/code&gt;，保存退出，重新打开终端。&lt;/li&gt;
&lt;li&gt;如果卡顿消失，进一步确认 &lt;code&gt;z&lt;/code&gt; 插件就是主要原因。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-清理-z-插件数据库删除家目录下的-z-数据文件&#34;&gt;3) 清理 &lt;code&gt;z&lt;/code&gt; 插件数据库：删除家目录下的 &lt;code&gt;.z&lt;/code&gt; 数据文件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;z&lt;/code&gt; 插件默认会在家目录存储路径权重数据库，文件名通常为 &lt;code&gt;~/.z&lt;/code&gt;（可能还有备份，比如 &lt;code&gt;~/.z.1&lt;/code&gt; 等）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -f ~/.z ~/.z.*
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：只删除 &lt;code&gt;.z&lt;/code&gt; 及其备份，勿用宽泛的通配符误删其他以 &lt;code&gt;.z&lt;/code&gt; 开头的配置文件（例如 &lt;code&gt;~/.zshrc&lt;/code&gt;、&lt;code&gt;~/.zsh_history&lt;/code&gt; 等）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4-验证&#34;&gt;4) 验证&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;再次启动 Git Bash，观察输入响应与命令执行返回。若明显恢复正常，则问题确认并解决。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;三更多可能导致终端卡顿的原因与优化方法&#34;&gt;三、更多可能导致终端卡顿的原因与优化方法&lt;/h2&gt;
&lt;p&gt;下面按层次列出常见瓶颈与处理策略。建议从“快速隔离”到“逐步优化”依次进行。&lt;/p&gt;
&lt;h3 id=&#34;a-zsh-配置与插件层面&#34;&gt;A. zsh 配置与插件层面&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;用 &lt;code&gt;zsh -f&lt;/code&gt; 快速隔离：先确认卡顿是否源自 &lt;code&gt;~/.zshrc&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用内置性能分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在交互会话里执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zmodload zsh/zprof
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; ~/.zshrc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zprof
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看哪个函数或插件耗时最多，针对性优化或禁用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;精简/优化高开销插件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;zsh-autosuggestions&lt;/code&gt;：在超长历史或远程磁盘上可能放大延迟，必要时暂时禁用。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zsh-syntax-highlighting&lt;/code&gt;：对超长命令行或复杂高亮也可能造成卡顿，可移到 &lt;code&gt;~/.zshrc&lt;/code&gt; 最末加载或暂时禁用。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git&lt;/code&gt; 相关主题/段：提示符中显示仓库状态若每次刷新都做代价高的检测，易导致卡顿（详见下一节）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;清理与限制缓存/历史：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;删除过期的 &lt;code&gt;~/.zcompdump*&lt;/code&gt; 文件，让补全缓存重新生成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -f ~/.zcompdump*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;compinit -C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;限制历史大小，避免历史过大：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;HISTSIZE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SAVEHIST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;必要时压缩或清理 &lt;code&gt;~/.zsh_history&lt;/code&gt;（先备份，再用专门工具或谨慎操作）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优化补全与缓存（选做）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 开启补全缓存（需确保缓存目录存在）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zstyle &lt;span class=&#34;s1&#34;&gt;&amp;#39;:completion:*&amp;#39;&lt;/span&gt; use-cache on
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zstyle &lt;span class=&#34;s1&#34;&gt;&amp;#39;:completion:*&amp;#39;&lt;/span&gt; cache-path &lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/.zsh/cache
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;控制 &lt;code&gt;PATH&lt;/code&gt; 长度与重复项：超长 &lt;code&gt;PATH&lt;/code&gt; 或大量网络路径会增加命令解析成本，去重并移除不必要条目。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;跳转插件替代：如果依赖频繁路径跳转，可以考虑更高性能的 &lt;code&gt;zoxide&lt;/code&gt;（Rust 实现，跨平台），或使用 &lt;code&gt;fzf&lt;/code&gt; + &lt;code&gt;cd&lt;/code&gt; 组合实现快速导航。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;b-git-状态提示符层面对大型仓库尤其重要&#34;&gt;B. Git 状态/提示符层面（对大型仓库尤其重要）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;减少提示符的 Git 状态开销：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果使用 oh-my-zsh 的 &lt;code&gt;git&lt;/code&gt; 主题段，可以只显示分支名，不显示“脏状态”（新增/删除/未跟踪），显著减少每次刷新调用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;DISABLE_UNTRACKED_FILES_DIRTY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;避免在 &lt;code&gt;PROMPT&lt;/code&gt; 或 &lt;code&gt;RPROMPT&lt;/code&gt; 中触发完整的 &lt;code&gt;git status&lt;/code&gt;（尤其是有大量未跟踪文件时）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;提升 Git 的基础性能（全局设置，温和安全）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global core.preloadIndex &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global core.fscache &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global gc.writeCommitGraph &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git maintenance start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;core.preloadIndex&lt;/code&gt; 与 &lt;code&gt;core.fscache&lt;/code&gt; 常见于 Git for Windows 的加速选项（新版本多为默认开启，显式配置更稳妥）。&lt;/li&gt;
&lt;li&gt;写入 commit-graph 与开启维护任务，可以加速多种 Git 操作（包括 &lt;code&gt;status&lt;/code&gt;、&lt;code&gt;log&lt;/code&gt; 等），对大仓库尤为明显。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;仅在确定版本支持的情况下考虑文件监控（fsmonitor）：新版本 Git 在 Windows 上逐步完善内置 FS 监控特性，能减少反复扫描文件系统的成本。建议升级到较新的 Git for Windows 后再查阅对应版本说明开启；旧版或不支持时不要启用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;c-终端系统层面windows&#34;&gt;C. 终端/系统层面（Windows）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;升级工具与组件：
&lt;ul&gt;
&lt;li&gt;升级到最新的 Git for Windows。&lt;/li&gt;
&lt;li&gt;如果使用外部终端（如 Windows Terminal），留意 ConPTY 后端的兼容参数；不同设置在部分环境下会影响交互流畅度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;杀毒/安全软件与索引扫描：
&lt;ul&gt;
&lt;li&gt;为 &lt;code&gt;C:\Program Files\Git\&lt;/code&gt; 安装目录与主要仓库路径添加实时扫描排除规则，避免每次命令触发大量 I/O 扫描。&lt;/li&gt;
&lt;li&gt;避免在网络盘、同步盘（如 OneDrive）或被系统索引服务重度扫描的目录中工作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;字体与渲染：
&lt;ul&gt;
&lt;li&gt;某些 Nerd Font 或高开销渲染配置在低配环境下也会造成刷新卡顿，可换用常规字体测试。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;四快速排查与操作清单可直接拿来用&#34;&gt;四、快速排查与操作清单（可直接拿来用）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;干净启动，判断是否为配置问题：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zsh -f
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;性能画像（找出最慢的函数/插件）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zmodload zsh/zprof
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; ~/.zshrc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zprof
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;移除 &lt;code&gt;z&lt;/code&gt; 插件，并清理数据库：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;编辑 &lt;code&gt;~/.zshrc&lt;/code&gt;，从 &lt;code&gt;plugins=(...)&lt;/code&gt; 中删除 &lt;code&gt;z&lt;/code&gt;；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;删除数据库文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -f ~/.z ~/.z.*
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;限制 Git 状态显示开销（oh-my-zsh）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;DISABLE_UNTRACKED_FILES_DIRTY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;提升 Git 基础性能（全局）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global core.preloadIndex &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global core.fscache &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global gc.writeCommitGraph &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git maintenance start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;清理补全缓存并快速初始化：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -f ~/.zcompdump*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;compinit -C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;五结语&#34;&gt;五、结语&lt;/h2&gt;
&lt;p&gt;这次卡顿的根因是 &lt;code&gt;z&lt;/code&gt; 插件的路径数据库过大，移除插件并清理 &lt;code&gt;.z&lt;/code&gt; 数据后问题即刻消失。更通用的经验是：先用 &lt;code&gt;zsh -f&lt;/code&gt; 快速隔离，再用 &lt;code&gt;zprof&lt;/code&gt; 定位耗时点&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>当终端出现输入卡顿、命令执行后迟迟“回不来”的情况，往往不是单一因素导致。本文记录了我在 Git Bash 上的排查过程：最终发现是 oh-my-zsh 的 <code>z</code> 插件在家目录累积了大量 <code>.z</code> 数据文件，引发了明显的慢响应。同时也整理了一份更系统的优化与排错清单，帮助你快速定位并消除常见的性能瓶颈。</p>
<h2 id="一现象与环境">一、现象与环境</h2>
<ul>
<li>Git Bash 终端卡顿：输入有明显延迟，命令执行后迟迟不返回提示符。</li>
<li>使用的是 <code>zsh</code>（配合 oh-my-zsh），启用了包括 <code>z</code> 在内的多个插件。</li>
<li>家目录出现了很多 <code>.z</code> 相关文件（<code>z</code> 插件的跳转数据库），怀疑数据库过大导致慢响应。</li>
</ul>
<h2 id="二我的排查与解决过程">二、我的排查与解决过程</h2>
<h3 id="1-先隔离配置用干净的-zsh--f-启动">1) 先隔离配置：用干净的 <code>zsh -f</code> 启动</h3>
<ul>
<li><code>zsh -f</code> 会在不加载 <code>~/.zshrc</code> 的前提下启动一个“空配置”的 zsh，能快速判断是否是配置或插件导致问题。</li>
<li>如果 <code>zsh -f</code> 下终端恢复顺畅，基本可以确定问题在 <code>~/.zshrc</code> 或其引入的插件与脚本。</li>
</ul>
<h3 id="2-定位嫌疑插件移除-zshrc-中的-z-插件">2) 定位“嫌疑插件”：移除 <code>~/.zshrc</code> 中的 <code>z</code> 插件</h3>
<ul>
<li>打开 <code>~/.zshrc</code>，从 <code>plugins=(...)</code> 中移除 <code>z</code>，保存退出，重新打开终端。</li>
<li>如果卡顿消失，进一步确认 <code>z</code> 插件就是主要原因。</li>
</ul>
<h3 id="3-清理-z-插件数据库删除家目录下的-z-数据文件">3) 清理 <code>z</code> 插件数据库：删除家目录下的 <code>.z</code> 数据文件</h3>
<ul>
<li>
<p><code>z</code> 插件默认会在家目录存储路径权重数据库，文件名通常为 <code>~/.z</code>（可能还有备份，比如 <code>~/.z.1</code> 等）。</p>
</li>
<li>
<p>执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rm -f ~/.z ~/.z.*
</span></span></code></pre></div><p>注意：只删除 <code>.z</code> 及其备份，勿用宽泛的通配符误删其他以 <code>.z</code> 开头的配置文件（例如 <code>~/.zshrc</code>、<code>~/.zsh_history</code> 等）。</p>
</li>
</ul>
<h3 id="4-验证">4) 验证</h3>
<ul>
<li>再次启动 Git Bash，观察输入响应与命令执行返回。若明显恢复正常，则问题确认并解决。</li>
</ul>
<h2 id="三更多可能导致终端卡顿的原因与优化方法">三、更多可能导致终端卡顿的原因与优化方法</h2>
<p>下面按层次列出常见瓶颈与处理策略。建议从“快速隔离”到“逐步优化”依次进行。</p>
<h3 id="a-zsh-配置与插件层面">A. zsh 配置与插件层面</h3>
<ul>
<li>
<p>用 <code>zsh -f</code> 快速隔离：先确认卡顿是否源自 <code>~/.zshrc</code>。</p>
</li>
<li>
<p>用内置性能分析：</p>
<ol>
<li>
<p>在交互会话里执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">zmodload zsh/zprof
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.zshrc
</span></span><span class="line"><span class="cl">zprof
</span></span></code></pre></div></li>
<li>
<p>查看哪个函数或插件耗时最多，针对性优化或禁用。</p>
</li>
</ol>
</li>
<li>
<p>精简/优化高开销插件：</p>
<ul>
<li><code>zsh-autosuggestions</code>：在超长历史或远程磁盘上可能放大延迟，必要时暂时禁用。</li>
<li><code>zsh-syntax-highlighting</code>：对超长命令行或复杂高亮也可能造成卡顿，可移到 <code>~/.zshrc</code> 最末加载或暂时禁用。</li>
<li><code>git</code> 相关主题/段：提示符中显示仓库状态若每次刷新都做代价高的检测，易导致卡顿（详见下一节）。</li>
</ul>
</li>
<li>
<p>清理与限制缓存/历史：</p>
<ul>
<li>
<p>删除过期的 <code>~/.zcompdump*</code> 文件，让补全缓存重新生成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rm -f ~/.zcompdump*
</span></span><span class="line"><span class="cl">compinit -C
</span></span></code></pre></div></li>
<li>
<p>限制历史大小，避免历史过大：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">HISTSIZE</span><span class="o">=</span><span class="m">5000</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">SAVEHIST</span><span class="o">=</span><span class="m">5000</span>
</span></span></code></pre></div></li>
<li>
<p>必要时压缩或清理 <code>~/.zsh_history</code>（先备份，再用专门工具或谨慎操作）。</p>
</li>
</ul>
</li>
<li>
<p>优化补全与缓存（选做）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 开启补全缓存（需确保缓存目录存在）</span>
</span></span><span class="line"><span class="cl">zstyle <span class="s1">&#39;:completion:*&#39;</span> use-cache on
</span></span><span class="line"><span class="cl">zstyle <span class="s1">&#39;:completion:*&#39;</span> cache-path <span class="nv">$HOME</span>/.zsh/cache
</span></span></code></pre></div></li>
<li>
<p>控制 <code>PATH</code> 长度与重复项：超长 <code>PATH</code> 或大量网络路径会增加命令解析成本，去重并移除不必要条目。</p>
</li>
<li>
<p>跳转插件替代：如果依赖频繁路径跳转，可以考虑更高性能的 <code>zoxide</code>（Rust 实现，跨平台），或使用 <code>fzf</code> + <code>cd</code> 组合实现快速导航。</p>
</li>
</ul>
<h3 id="b-git-状态提示符层面对大型仓库尤其重要">B. Git 状态/提示符层面（对大型仓库尤其重要）</h3>
<ul>
<li>
<p>减少提示符的 Git 状态开销：</p>
<ul>
<li>
<p>如果使用 oh-my-zsh 的 <code>git</code> 主题段，可以只显示分支名，不显示“脏状态”（新增/删除/未跟踪），显著减少每次刷新调用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">DISABLE_UNTRACKED_FILES_DIRTY</span><span class="o">=</span><span class="s2">&#34;true&#34;</span>
</span></span></code></pre></div></li>
<li>
<p>避免在 <code>PROMPT</code> 或 <code>RPROMPT</code> 中触发完整的 <code>git status</code>（尤其是有大量未跟踪文件时）。</p>
</li>
</ul>
</li>
<li>
<p>提升 Git 的基础性能（全局设置，温和安全）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global core.preloadIndex <span class="nb">true</span>
</span></span><span class="line"><span class="cl">git config --global core.fscache <span class="nb">true</span>
</span></span><span class="line"><span class="cl">git config --global gc.writeCommitGraph <span class="nb">true</span>
</span></span><span class="line"><span class="cl">git maintenance start
</span></span></code></pre></div><p>说明：</p>
<ul>
<li><code>core.preloadIndex</code> 与 <code>core.fscache</code> 常见于 Git for Windows 的加速选项（新版本多为默认开启，显式配置更稳妥）。</li>
<li>写入 commit-graph 与开启维护任务，可以加速多种 Git 操作（包括 <code>status</code>、<code>log</code> 等），对大仓库尤为明显。</li>
</ul>
</li>
<li>
<p>仅在确定版本支持的情况下考虑文件监控（fsmonitor）：新版本 Git 在 Windows 上逐步完善内置 FS 监控特性，能减少反复扫描文件系统的成本。建议升级到较新的 Git for Windows 后再查阅对应版本说明开启；旧版或不支持时不要启用。</p>
</li>
</ul>
<h3 id="c-终端系统层面windows">C. 终端/系统层面（Windows）</h3>
<ul>
<li>升级工具与组件：
<ul>
<li>升级到最新的 Git for Windows。</li>
<li>如果使用外部终端（如 Windows Terminal），留意 ConPTY 后端的兼容参数；不同设置在部分环境下会影响交互流畅度。</li>
</ul>
</li>
<li>杀毒/安全软件与索引扫描：
<ul>
<li>为 <code>C:\Program Files\Git\</code> 安装目录与主要仓库路径添加实时扫描排除规则，避免每次命令触发大量 I/O 扫描。</li>
<li>避免在网络盘、同步盘（如 OneDrive）或被系统索引服务重度扫描的目录中工作。</li>
</ul>
</li>
<li>字体与渲染：
<ul>
<li>某些 Nerd Font 或高开销渲染配置在低配环境下也会造成刷新卡顿，可换用常规字体测试。</li>
</ul>
</li>
</ul>
<h2 id="四快速排查与操作清单可直接拿来用">四、快速排查与操作清单（可直接拿来用）</h2>
<ul>
<li>
<p>干净启动，判断是否为配置问题：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">zsh -f
</span></span></code></pre></div></li>
<li>
<p>性能画像（找出最慢的函数/插件）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">zmodload zsh/zprof
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.zshrc
</span></span><span class="line"><span class="cl">zprof
</span></span></code></pre></div></li>
<li>
<p>移除 <code>z</code> 插件，并清理数据库：</p>
<ol>
<li>
<p>编辑 <code>~/.zshrc</code>，从 <code>plugins=(...)</code> 中删除 <code>z</code>；</p>
</li>
<li>
<p>删除数据库文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rm -f ~/.z ~/.z.*
</span></span></code></pre></div></li>
</ol>
</li>
<li>
<p>限制 Git 状态显示开销（oh-my-zsh）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">DISABLE_UNTRACKED_FILES_DIRTY</span><span class="o">=</span><span class="s2">&#34;true&#34;</span>
</span></span></code></pre></div></li>
<li>
<p>提升 Git 基础性能（全局）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global core.preloadIndex <span class="nb">true</span>
</span></span><span class="line"><span class="cl">git config --global core.fscache <span class="nb">true</span>
</span></span><span class="line"><span class="cl">git config --global gc.writeCommitGraph <span class="nb">true</span>
</span></span><span class="line"><span class="cl">git maintenance start
</span></span></code></pre></div></li>
<li>
<p>清理补全缓存并快速初始化：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rm -f ~/.zcompdump*
</span></span><span class="line"><span class="cl">compinit -C
</span></span></code></pre></div></li>
</ul>
<h2 id="五结语">五、结语</h2>
<p>这次卡顿的根因是 <code>z</code> 插件的路径数据库过大，移除插件并清理 <code>.z</code> 数据后问题即刻消失。更通用的经验是：先用 <code>zsh -f</code> 快速隔离，再用 <code>zprof</code> 定位耗时点</p>
]]></content:encoded>
    </item>
    <item>
      <title>Zwift单机版多人联机教程</title>
      <link>https://lifeislife.cn/posts/zwift%E5%8D%95%E6%9C%BA%E7%89%88%E5%A4%9A%E4%BA%BA%E8%81%94%E6%9C%BA%E6%95%99%E7%A8%8B/</link>
      <pubDate>Mon, 18 Aug 2025 10:03:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/zwift%E5%8D%95%E6%9C%BA%E7%89%88%E5%A4%9A%E4%BA%BA%E8%81%94%E6%9C%BA%E6%95%99%E7%A8%8B/</guid>
      <description>&lt;p&gt;创建普通用户，用户名为user&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;useradd -m user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;修改默认shell为bash&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo usermod -s /bin/bash user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;添加用户到sudoers&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo usermod -aG sudo user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;立即生效配置&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo newgrp sudo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;修改用户密码&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo passwd user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一键安装Docker&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -fsSL https://cdn.jsdelivr.net/gh/muzihuaner/docker-install@master/install.sh -o install-docker.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sh install-docker.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置镜像仓库&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;registry-mirrors&amp;#34;: [&amp;#34;https://docker.1ms.run&amp;#34;]}&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee /etc/docker/daemon.json &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        systemctl daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        systemctl restart docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;添加当前用户到docker组，避免每次使用docker命令时都需要sudo&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo usermod -aG docker &lt;span class=&#34;nv&#34;&gt;$USER&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;生效配置&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;newgrp docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;常见问题&#34;&gt;常见问题&lt;/h1&gt;
&lt;h2 id=&#34;安卓客户端登录密码错误-password-invalid&#34;&gt;安卓客户端登录密码错误 password invalid&lt;/h2&gt;
&lt;p&gt;确保hosts中的域名都能够被正常解析。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1 us-or-rly101.zwift.com 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1 secure.zwift.com 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1 cdn.zwift.com 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安卓设备可以下载应用&lt;code&gt;Termux&lt;/code&gt;，可以使用&lt;code&gt;ping&lt;/code&gt;命令检查域名是否能够被正常解析。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ping us-or-rly101.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ping secure.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ping cdn.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一定到注意每个域名都测试一遍，之前有遇到过只有部分域名能够被正常解析的情况。如果发现部分域名能够被正常解析，而部分域名不能被正常解析，可以尝试使用txt编辑工具，比如&lt;code&gt;txtpad&lt;/code&gt;，不要使用WPS，Microsoft Word等工具来编辑，这些工具可能会自动将域名转换为其他格式，导致无法被正常解析。用&lt;code&gt;txtpad&lt;/code&gt;编辑后，保存为&lt;code&gt;hosts.txt&lt;/code&gt;文件到能找到的位置，然后重新使用&lt;code&gt;ping&lt;/code&gt;命令检查域名是否能够被正常解析。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>创建普通用户，用户名为user</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">useradd -m user
</span></span></code></pre></div><p>修改默认shell为bash</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo usermod -s /bin/bash user
</span></span></code></pre></div><p>添加用户到sudoers</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo usermod -aG sudo user
</span></span></code></pre></div><p>立即生效配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo newgrp sudo
</span></span></code></pre></div><p>修改用户密码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo passwd user
</span></span></code></pre></div><p>一键安装Docker</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -fsSL https://cdn.jsdelivr.net/gh/muzihuaner/docker-install@master/install.sh -o install-docker.sh
</span></span><span class="line"><span class="cl">sh install-docker.sh
</span></span></code></pre></div><p>配置镜像仓库</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;{&#34;registry-mirrors&#34;: [&#34;https://docker.1ms.run&#34;]}&#39;</span> <span class="p">|</span> sudo tee /etc/docker/daemon.json &gt; /dev/null
</span></span><span class="line"><span class="cl">        systemctl daemon-reload
</span></span><span class="line"><span class="cl">        systemctl restart docker
</span></span></code></pre></div><p>添加当前用户到docker组，避免每次使用docker命令时都需要sudo</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo usermod -aG docker <span class="nv">$USER</span>
</span></span></code></pre></div><p>生效配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">newgrp docker
</span></span></code></pre></div><h1 id="常见问题">常见问题</h1>
<h2 id="安卓客户端登录密码错误-password-invalid">安卓客户端登录密码错误 password invalid</h2>
<p>确保hosts中的域名都能够被正常解析。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">127.0.0.1 us-or-rly101.zwift.com 
</span></span><span class="line"><span class="cl">127.0.0.1 secure.zwift.com 
</span></span><span class="line"><span class="cl">127.0.0.1 cdn.zwift.com 
</span></span></code></pre></div><p>安卓设备可以下载应用<code>Termux</code>，可以使用<code>ping</code>命令检查域名是否能够被正常解析。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ping us-or-rly101.zwift.com
</span></span><span class="line"><span class="cl">ping secure.zwift.com
</span></span><span class="line"><span class="cl">ping cdn.zwift.com
</span></span></code></pre></div><p>一定到注意每个域名都测试一遍，之前有遇到过只有部分域名能够被正常解析的情况。如果发现部分域名能够被正常解析，而部分域名不能被正常解析，可以尝试使用txt编辑工具，比如<code>txtpad</code>，不要使用WPS，Microsoft Word等工具来编辑，这些工具可能会自动将域名转换为其他格式，导致无法被正常解析。用<code>txtpad</code>编辑后，保存为<code>hosts.txt</code>文件到能找到的位置，然后重新使用<code>ping</code>命令检查域名是否能够被正常解析。</p>
]]></content:encoded>
    </item>
    <item>
      <title>探险家V900 GPS轨迹记录器折腾笔记</title>
      <link>https://lifeislife.cn/posts/%E6%8E%A2%E9%99%A9%E5%AE%B6v900-gps%E8%BD%A8%E8%BF%B9%E8%AE%B0%E5%BD%95%E5%99%A8%E6%8A%98%E8%85%BE%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Tue, 01 Jul 2025 14:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E6%8E%A2%E9%99%A9%E5%AE%B6v900-gps%E8%BD%A8%E8%BF%B9%E8%AE%B0%E5%BD%95%E5%99%A8%E6%8A%98%E8%85%BE%E7%AC%94%E8%AE%B0/</guid>
      <description>&lt;h1 id=&#34;一切要从一个软件开始&#34;&gt;一切要从一个软件开始&lt;/h1&gt;
&lt;p&gt;某天看到Strava上自己的骑行轨迹，仓鼠的天性开始显露，既然能记录骑行的轨迹，为啥不把开车轨迹，步行轨迹都记录了？虽然早就听说了世界迷雾（Fog of World），但是因为走过的地方并不多，再加上安装过程比较复杂，所以一直没有尝试。现在看到骑行的地方越来越多，世界迷雾（Fog of World）的诱惑越来越大，于是开始折腾。&lt;/p&gt;
&lt;p&gt;使用这个软件就不多说了，反正倒腾Google Pay就捣鼓了好久，又花了20刀乐买了这个软件。&lt;/p&gt;
&lt;p&gt;虽然这个软件本身就是自带轨迹记录功能，但是使用手机导航的都知道，任何需要GPS功能的软件都是耗电大户，让这个软件常驻后台显然不能接受，一次偶然的机会，看到了这个设备，原来可以通过外置GPS记录轨迹，于是开始折腾。&lt;/p&gt;
&lt;h1 id=&#34;探险家v900-gps轨迹记录器&#34;&gt;探险家V900 GPS轨迹记录器&lt;/h1&gt;
&lt;p&gt;一个快20年前的产品，早已经停产，目前只能找到它的升级版V990以及性能更好的产品，但是价格也贵了不少。即使现在能找到在售的V990，价格也要500块，还是没有舍得。只能在闲鱼上淘一下，140块淘了一个品相还不错的V900。&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//2025/07/03/7dc5957f8418b66ee4c0ff0fe2826123.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/7dc5957f8418b66ee4c0ff0fe2826123.jpg&#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//2025/07/03/c0f7e0fadddd6d6899c4ac5f062905fa.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/c0f7e0fadddd6d6899c4ac5f062905fa.jpg&#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//2025/07/03/e487c85699bae5fec64f5a42c3156f39.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/e487c85699bae5fec64f5a42c3156f39.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//2025/07/03/1d9e0ba43c6e52466da0d5b0fcd470e9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/1d9e0ba43c6e52466da0d5b0fcd470e9.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;因为对GPS硬件一窍不通，也不知道这个规格里的性能如何，只是想当然觉得专用的GPS记录器应该至少比手机强吧，但是东西到手只能感叹一句，技术进步太快了。&lt;/p&gt;
&lt;p&gt;这玩意只能获取GPS信号，但是现在最廉价的手机都能支持多种定位方式，包括GPS，北斗，GLONASS等等信号了，这意味它获取定位的精度和速度都远远不如手机。&lt;/p&gt;
&lt;p&gt;在室内定位了很久都没有定位成功，又专门到楼下空地还是无法定位，最终只能放弃，退回卖家，但是卖家也可能觉得是老古董了，估计很难卖出去，最终答应20块钱卖给我。我心想20块钱买个曾经500块的玩具，也挺划算的，于是就收下了。既然能开机，说明硬件应该没问题，应该还是能用的。&lt;/p&gt;
&lt;h1 id=&#34;折腾过程&#34;&gt;折腾过程&lt;/h1&gt;
&lt;h2 id=&#34;无法定位&#34;&gt;无法定位&lt;/h2&gt;
&lt;p&gt;因为设备很久没有开机定位了，所以相当于冷启动，通常获取GPS信号时间就会很久，之前买新的码表时就会遇到这种情况，第一次定位花了很久，以为设备坏了，后来定位成功一次后，后续定位就很快了。于是我又找了一块广场空地开始定位，这次果然定位成功了。&lt;/p&gt;
&lt;p&gt;所以想要解决定位问题，首先不能在室内，室内因为混凝土遮挡信号，几乎无法定位成功，第一次定位时最好找个没有遮挡的空地，3-5分钟基本上能定位成功。&lt;/p&gt;
&lt;h2 id=&#34;无法记录轨迹&#34;&gt;无法记录轨迹&lt;/h2&gt;
&lt;p&gt;这个问题包括插入内存卡后，开机后自动关机，定位成功后设备发出急促的哔哔哔声音，这些都是因为内存卡分区格式问题导致的。&lt;/p&gt;
&lt;p&gt;因为这是快20年前的产品，那时候的内存卡大多还是4G以下的内存，文件系统还是FAT格式，现在想要买一个4G以下的内存卡都很难了，所以能见到的大部分内存卡都是无法直接在这个设备上使用，必须格式化成FAT格式。所以最好找一个小容量的卡，如果是4G以下的卡，通常可以在电脑上直接格式化为FAT或者FAT16，如果大于4G的卡，那就需要专门的软件来格式化了，并且格式化之后的容量最大就只有4G，所以最好买一个小容量卡。&lt;/p&gt;
&lt;p&gt;格式化的工具和步骤可以参考这个链接：&lt;a href=&#34;https://www.cbgps.com/download/format/index_en.html&#34;&gt;How to record tracks in V-990/V-900 with a MicroSD card (Capacity above 4G)&lt;/a&gt;。虽然是老古董产品，但是这个常见的售后支持做的还是很到位的，这么多年都还保留了所有产品的文档和相关工具。&lt;/p&gt;
&lt;p&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//2025/07/03/27314625d6dc6026c5401508ebdf119f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/27314625d6dc6026c5401508ebdf119f.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//2025/07/03/705b229d7c2da473502de4d412969437.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/705b229d7c2da473502de4d412969437.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;a href=&#34;https://www.cbgps.com/download.htm&#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//2025/07/03/17e88c53a9bf685312a4fc4b26beafa5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/17e88c53a9bf685312a4fc4b26beafa5.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;h2 id=&#34;使用蓝牙连接设备&#34;&gt;使用蓝牙连接设备&lt;/h2&gt;
&lt;p&gt;这个功能当初开发出来应该是为了给一些软件提供第三方的导航功能的，但是现在导航工具已经触手可得，这个功能就没什么用了。如果你想尝试一下这个功能，可以下载一个Bluetooth GPS软件，&lt;a href=&#34;https://bluetooth-gps.cn.uptodown.com/android&#34;&gt;Bluetooth GPS针对于Android - 从Uptodown上下载APK&lt;/a&gt;来连接设备。&lt;/p&gt;
&lt;p&gt;链接过程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;长按开机键开机，自动进入蓝牙模式，蓝牙灯闪烁&lt;/li&gt;
&lt;li&gt;打开手机蓝牙，搜索设备，找到设备后点击连接，设备名称为Columbus GPS&lt;/li&gt;
&lt;li&gt;输入匹配码0000连接设备&lt;/li&gt;
&lt;li&gt;打开软件选择Columbus GPS，点击连接&lt;/li&gt;
&lt;li&gt;连接成功后，软件会显示设备信息，包括经纬度，速度，海拔等&lt;/li&gt;
&lt;/ol&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//2025/07/03/35fe63a86d0c09df17996669003f41bb.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/35fe63a86d0c09df17996669003f41bb.jpg&#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;h1 id=&#34;轨迹实测&#34;&gt;轨迹实测&lt;/h1&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//2025/07/03/72d0441f8c4f8a5ebc776699af1ec85e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/72d0441f8c4f8a5ebc776699af1ec85e.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;蓝色是佳明Forerunner 255的运动轨迹，红色是V900的轨迹，可以看到V900的性能在今天来说完全不够看了，漂移严重，并且海拔信息几乎不可用，这么点距离海拔爬升200多，在海宁这种大平原上，走一天都爬不了这么高。&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//2025/07/03/8d16c4a47497dd12b62a63842b7c764d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/8d16c4a47497dd12b62a63842b7c764d.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;
</description>
      <content:encoded><![CDATA[<h1 id="一切要从一个软件开始">一切要从一个软件开始</h1>
<p>某天看到Strava上自己的骑行轨迹，仓鼠的天性开始显露，既然能记录骑行的轨迹，为啥不把开车轨迹，步行轨迹都记录了？虽然早就听说了世界迷雾（Fog of World），但是因为走过的地方并不多，再加上安装过程比较复杂，所以一直没有尝试。现在看到骑行的地方越来越多，世界迷雾（Fog of World）的诱惑越来越大，于是开始折腾。</p>
<p>使用这个软件就不多说了，反正倒腾Google Pay就捣鼓了好久，又花了20刀乐买了这个软件。</p>
<p>虽然这个软件本身就是自带轨迹记录功能，但是使用手机导航的都知道，任何需要GPS功能的软件都是耗电大户，让这个软件常驻后台显然不能接受，一次偶然的机会，看到了这个设备，原来可以通过外置GPS记录轨迹，于是开始折腾。</p>
<h1 id="探险家v900-gps轨迹记录器">探险家V900 GPS轨迹记录器</h1>
<p>一个快20年前的产品，早已经停产，目前只能找到它的升级版V990以及性能更好的产品，但是价格也贵了不少。即使现在能找到在售的V990，价格也要500块，还是没有舍得。只能在闲鱼上淘一下，140块淘了一个品相还不错的V900。</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//2025/07/03/7dc5957f8418b66ee4c0ff0fe2826123.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/7dc5957f8418b66ee4c0ff0fe2826123.jpg" 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//2025/07/03/c0f7e0fadddd6d6899c4ac5f062905fa.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/c0f7e0fadddd6d6899c4ac5f062905fa.jpg" 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//2025/07/03/e487c85699bae5fec64f5a42c3156f39.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/e487c85699bae5fec64f5a42c3156f39.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//2025/07/03/1d9e0ba43c6e52466da0d5b0fcd470e9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/1d9e0ba43c6e52466da0d5b0fcd470e9.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>因为对GPS硬件一窍不通，也不知道这个规格里的性能如何，只是想当然觉得专用的GPS记录器应该至少比手机强吧，但是东西到手只能感叹一句，技术进步太快了。</p>
<p>这玩意只能获取GPS信号，但是现在最廉价的手机都能支持多种定位方式，包括GPS，北斗，GLONASS等等信号了，这意味它获取定位的精度和速度都远远不如手机。</p>
<p>在室内定位了很久都没有定位成功，又专门到楼下空地还是无法定位，最终只能放弃，退回卖家，但是卖家也可能觉得是老古董了，估计很难卖出去，最终答应20块钱卖给我。我心想20块钱买个曾经500块的玩具，也挺划算的，于是就收下了。既然能开机，说明硬件应该没问题，应该还是能用的。</p>
<h1 id="折腾过程">折腾过程</h1>
<h2 id="无法定位">无法定位</h2>
<p>因为设备很久没有开机定位了，所以相当于冷启动，通常获取GPS信号时间就会很久，之前买新的码表时就会遇到这种情况，第一次定位花了很久，以为设备坏了，后来定位成功一次后，后续定位就很快了。于是我又找了一块广场空地开始定位，这次果然定位成功了。</p>
<p>所以想要解决定位问题，首先不能在室内，室内因为混凝土遮挡信号，几乎无法定位成功，第一次定位时最好找个没有遮挡的空地，3-5分钟基本上能定位成功。</p>
<h2 id="无法记录轨迹">无法记录轨迹</h2>
<p>这个问题包括插入内存卡后，开机后自动关机，定位成功后设备发出急促的哔哔哔声音，这些都是因为内存卡分区格式问题导致的。</p>
<p>因为这是快20年前的产品，那时候的内存卡大多还是4G以下的内存，文件系统还是FAT格式，现在想要买一个4G以下的内存卡都很难了，所以能见到的大部分内存卡都是无法直接在这个设备上使用，必须格式化成FAT格式。所以最好找一个小容量的卡，如果是4G以下的卡，通常可以在电脑上直接格式化为FAT或者FAT16，如果大于4G的卡，那就需要专门的软件来格式化了，并且格式化之后的容量最大就只有4G，所以最好买一个小容量卡。</p>
<p>格式化的工具和步骤可以参考这个链接：<a href="https://www.cbgps.com/download/format/index_en.html">How to record tracks in V-990/V-900 with a MicroSD card (Capacity above 4G)</a>。虽然是老古董产品，但是这个常见的售后支持做的还是很到位的，这么多年都还保留了所有产品的文档和相关工具。</p>
<p>最终卡信息如下：</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//2025/07/03/27314625d6dc6026c5401508ebdf119f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/27314625d6dc6026c5401508ebdf119f.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//2025/07/03/705b229d7c2da473502de4d412969437.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/705b229d7c2da473502de4d412969437.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>产品文档链接：<a href="https://www.cbgps.com/download.htm">支持和下载</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//2025/07/03/17e88c53a9bf685312a4fc4b26beafa5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/17e88c53a9bf685312a4fc4b26beafa5.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>
<h2 id="使用蓝牙连接设备">使用蓝牙连接设备</h2>
<p>这个功能当初开发出来应该是为了给一些软件提供第三方的导航功能的，但是现在导航工具已经触手可得，这个功能就没什么用了。如果你想尝试一下这个功能，可以下载一个Bluetooth GPS软件，<a href="https://bluetooth-gps.cn.uptodown.com/android">Bluetooth GPS针对于Android - 从Uptodown上下载APK</a>来连接设备。</p>
<p>链接过程如下：</p>
<ol>
<li>长按开机键开机，自动进入蓝牙模式，蓝牙灯闪烁</li>
<li>打开手机蓝牙，搜索设备，找到设备后点击连接，设备名称为Columbus GPS</li>
<li>输入匹配码0000连接设备</li>
<li>打开软件选择Columbus GPS，点击连接</li>
<li>连接成功后，软件会显示设备信息，包括经纬度，速度，海拔等</li>
</ol>
<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//2025/07/03/35fe63a86d0c09df17996669003f41bb.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/35fe63a86d0c09df17996669003f41bb.jpg" 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>
<h1 id="轨迹实测">轨迹实测</h1>
<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//2025/07/03/72d0441f8c4f8a5ebc776699af1ec85e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/72d0441f8c4f8a5ebc776699af1ec85e.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>蓝色是佳明Forerunner 255的运动轨迹，红色是V900的轨迹，可以看到V900的性能在今天来说完全不够看了，漂移严重，并且海拔信息几乎不可用，这么点距离海拔爬升200多，在海宁这种大平原上，走一天都爬不了这么高。</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//2025/07/03/8d16c4a47497dd12b62a63842b7c764d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/07/03/8d16c4a47497dd12b62a63842b7c764d.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>
]]></content:encoded>
    </item>
    <item>
      <title>Docker Compose 部署 Strava Statistics</title>
      <link>https://lifeislife.cn/posts/docker-compose%E9%83%A8%E7%BD%B2strava-statistics/</link>
      <pubDate>Sat, 08 Mar 2025 10:34:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/docker-compose%E9%83%A8%E7%BD%B2strava-statistics/</guid>
      <description>&lt;h1 id=&#34;strava-statistics-部署文档&#34;&gt;Strava Statistics 部署文档&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/robiningelbrecht/strava-statistics&#34;&gt;Strava Statistics&lt;/a&gt; 是一个自托管工具，利用 Strava 数据生成个人运动统计，支持 Docker 部署，提供丰富的可视化分析。下面介绍如何使用 Docker Compose 部署。&lt;/p&gt;
&lt;h2 id=&#34;申请-strava-的-api-权限&#34;&gt;申请 Strava 的 API 权限&lt;/h2&gt;
&lt;p&gt;在&lt;a href=&#34;https://lifeislife.cn/awesome-cycling/docs/training-fitness/zwiftoffline/&#34;&gt;ZwiftOffline&lt;/a&gt;中也需要用到 API 权限，因为不同应用程序也不冲突，懒得再重新申请，所以以下填的信息使用的 ZwiftOffline 的 API 权限。&lt;/p&gt;
&lt;p&gt;登录&lt;a href=&#34;https://www.strava.com/settings/api&#34;&gt;Strava&lt;/a&gt;，申请 API，获取&lt;code&gt;Client ID&lt;/code&gt;和&lt;code&gt;Client Secret&lt;/code&gt;。&lt;/p&gt;
&lt;p&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//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.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;其中授权回调域填写：launcher.zwift.com&lt;/p&gt;
&lt;p&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//2025/02/09/a5024f9e2e150810251222315be11ac9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/a5024f9e2e150810251222315be11ac9.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;code&gt;client_id&lt;/code&gt;值换成上图中的客户 ID，然后浏览器中访问这个链接：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;http://www.strava.com/oauth/authorize?client_id=xxxxxxx&amp;amp;response_type=code&amp;amp;redirect_uri=http://localhost/exchange_token&amp;amp;approval_prompt=force&amp;amp;scope=activity:read_all
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&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//2025/03/08/89a9c1d81d40e6b4687531ab92d689a2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/03/08/89a9c1d81d40e6b4687531ab92d689a2.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;code&gt;code&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//2025/03/08/c3a74e83d363bb0f453ad37712dd28ac.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/03/08/c3a74e83d363bb0f453ad37712dd28ac.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;code&gt;code&lt;/code&gt;发起一个 cURL 请求得到&lt;code&gt;refresh_token&lt;/code&gt;，具体做法是在命令行终端中执行下面的命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	curl -X POST https://www.strava.com/oauth/token &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-F &lt;span class=&#34;nv&#34;&gt;client_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;YOURCLIENTID &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-F &lt;span class=&#34;nv&#34;&gt;client_secret&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;YOURCLIENTSECRET &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-F &lt;span class=&#34;nv&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;AUTHORIZATIONCODE &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-F &lt;span class=&#34;nv&#34;&gt;grant_type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;authorization_code
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;client_id&lt;/code&gt;值替换为申请得到的&lt;code&gt;客户ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;client_secret&lt;/code&gt;值替换为申请得到的&lt;code&gt;客户端密钥&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code&lt;/code&gt;替换为上一步浏览器中得到的&lt;code&gt;code&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;执行成功后可以得到下面的返回信息，需要将返回值中的&lt;code&gt;refresh_token&lt;/code&gt;和&lt;code&gt;access_token&lt;/code&gt;记录下来，后面需要用到：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;token_type&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;Bearer&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;expires_at&amp;#34;&lt;/span&gt;:1740213400,&lt;span class=&#34;s2&#34;&gt;&amp;#34;expires_in&amp;#34;&lt;/span&gt;:21600,&lt;span class=&#34;s2&#34;&gt;&amp;#34;refresh_token&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;123456789123456789&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;access_token&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;123456789123456789&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;athlete&amp;#34;&lt;/span&gt;:&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:117756825,&lt;span class=&#34;s2&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;dunky_zhang&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;resource_state&amp;#34;&lt;/span&gt;:2,&lt;span class=&#34;s2&#34;&gt;&amp;#34;firstname&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;Dominic&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;lastname&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;Zhang&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;bio&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;骑行小白&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;city&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;state&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;country&amp;#34;&lt;/span&gt;:null,&lt;span class=&#34;s2&#34;&gt;&amp;#34;sex&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;M&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;premium&amp;#34;&lt;/span&gt;:true,&lt;span class=&#34;s2&#34;&gt;&amp;#34;summit&amp;#34;&lt;/span&gt;:true,&lt;span class=&#34;s2&#34;&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;2023-05-10T13:54:32Z&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;updated_at&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;2025-02-06T05:29:52Z&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;badge_type_id&amp;#34;&lt;/span&gt;:1,&lt;span class=&#34;s2&#34;&gt;&amp;#34;weight&amp;#34;&lt;/span&gt;:66.0,&lt;span class=&#34;s2&#34;&gt;&amp;#34;profile_medium&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://dgalywyr863hv.cloudfront.net/pictures/athletes/117756825/32138099/2/medium.jpg&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;profile&amp;#34;&lt;/span&gt;:&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://dgalywyr863hv.cloudfront.net/pictures/athletes/117756825/32138099/2/large.jpg&amp;#34;&lt;/span&gt;,&lt;span class=&#34;s2&#34;&gt;&amp;#34;friend&amp;#34;&lt;/span&gt;:null,&lt;span class=&#34;s2&#34;&gt;&amp;#34;follower&amp;#34;&lt;/span&gt;:null&lt;span class=&#34;o&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;docker-compose-部署-strava-statistics&#34;&gt;Docker Compose 部署 Strava Statistics&lt;/h2&gt;
&lt;p&gt;创建目录用于存放 Strava Statistics 的配置文件&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir statistics-for-strava
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; statistics-for-strava
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建 &lt;code&gt;docker-compose.yml&lt;/code&gt; 文件&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;touch docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编辑 &lt;code&gt;docker-compose.yml&lt;/code&gt; 文件，填入以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;robiningelbrecht/strava-statistics:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./build:/var/www/build&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./storage/database:/var/www/storage/database&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./storage/files:/var/www/storage/files&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;env_file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;./.env&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;PUID=1000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;PGID=1000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;TZ=Asia/Shanghai&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 因为 Strava 的 API 需要代理访问，所以下面设置了我的代理地址，请你根据自己的代理地址进行设置&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;http_proxy=http://192.168.1.9:7890&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;https_proxy=http://192.168.1.9:7890&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;all_proxy=socks5://192.168.1.9:7891&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建 &lt;code&gt;.env&lt;/code&gt; 文件&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;touch .env
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编辑 &lt;code&gt;.env&lt;/code&gt; 文件，填入以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 应用程序将托管的URL。此URL将用于清单文件中。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 这将允许您将Web应用程序作为原生应用程序安装到您的设备上。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MANIFEST_APP_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;http://localhost:8080/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 您的Strava应用程序的客户端ID。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;STRAVA_CLIENT_ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;xxxxxxx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 您的Strava应用程序的客户端密钥。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;STRAVA_CLIENT_SECRET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;xxxxxxxxxxxxxx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 您的Strava应用程序的刷新令牌。在上一步获取到的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;STRAVA_REFRESH_TOKEN&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;xxxxxxxxxxxxxx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Strava API有限速限制（https://github.com/robiningelbrecht/strava-statistics/wiki），&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 为了确保我们不会达到限速，我们希望限制每次导入处理的新活动数量。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 考虑到每天有1000次请求限制，并且导入一个新活动最多可能需要3次API调用，250应该是一个安全的数字。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NUMBER_OF_NEW_ACTIVITIES_TO_PROCESS_PER_IMPORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;250&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 定期运行导入和HTML构建的计划。留空以禁用定期导入。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 默认计划为每天凌晨04:05运行一次。如果您不知道什么是cron表达式，请勿更改此项。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 请确保不要过于频繁地运行导入，以避免达到Strava API的限速。每天一次应该足够。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;IMPORT_AND_BUILD_SCHEDULE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;5 4 * * *&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置用于计划的时区&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 有效的时区可以在此处找到（TZ标识符列）：https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;TZ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;Asia/Shanghai
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 允许的选项：en_US、fr_FR 或 nl_BE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LOCALE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;en_US
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 允许的选项：metric（公制）或 imperial（英制）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UNIT_SYSTEM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;metric
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 渲染应用程序时使用的时间格式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 允许的格式：24 或 12（包括AM和PM）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;TIME_FORMAT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 渲染应用程序时使用的日期格式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 允许的格式：DAY-MONTH-YEAR（日-月-年）或 MONTH-DAY-YEAR（月-日-年）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DATE_FORMAT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;DAY-MONTH-YEAR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 要导入的运动类型。留空以导入所有运动类型。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 通过此列表，您还可以决定运动类型的渲染顺序。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 完整的允许选项列表可在以下地址找到：https://github.com/robiningelbrecht/strava-statistics/wiki/Supported-sport-types/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SPORT_TYPES_TO_IMPORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 您的生日。需要用于计算心率区间。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ATHLETE_BIRTHDAY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1999-01-01
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 体重历史（单位为千克或磅，取决于UNIT_SYSTEM）。需要用于计算相对功率重量比（w/kg）。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看更多信息：https://github.com/robiningelbrecht/strava-statistics/wiki&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ATHLETE_WEIGHTS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;    &amp;#34;1970-01-01&amp;#34;: 68
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# FTP（功能阈值功率）历史。需要用于计算活动压力水平。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看更多信息：https://github.com/robiningelbrecht/strava-statistics/wiki&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;FTP_VALUES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;    &amp;#34;2024-12-12&amp;#34;: 235
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 包含ntfy主题的完整URL。此主题将用于在新的HTML构建运行时通知您。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 留空以禁用通知。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NTFY_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在导入期间要跳过的活动ID数组。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 这允许您在导入时跳过特定活动。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ACTIVITIES_TO_SKIP_DURING_IMPORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建/拥有由strava-statistics管理的文件的UID和GID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 可能仅在Linux主机上需要，详见Wiki中的文件权限部分&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#PUID=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#PGID=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;开始导入数据：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;启动容器&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;导入数据&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; App bin/console app:strava:import-data
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; App bin/console app:strava:build-files
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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//2025/02/23/af3c468483a1c46544c02b5c2d8124ac.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/23/af3c468483a1c46544c02b5c2d8124ac.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;/li&gt;
&lt;li&gt;
&lt;p&gt;访问 &lt;code&gt;localhost:8080&lt;/code&gt; 查看数据&lt;/p&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//2025/03/08/07a1a21d1c53ab4560de20f5cbcb60db.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/03/08/07a1a21d1c53ab4560de20f5cbcb60db.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;h2 id=&#34;常见问题&#34;&gt;常见问题&lt;/h2&gt;
&lt;h3 id=&#34;401-unauthorized&#34;&gt;401 Unauthorized&lt;/h3&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//2025/02/22/a541f82e213a98de7f9e69a614e0e7bc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/22/a541f82e213a98de7f9e69a614e0e7bc.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;code&gt;.env&lt;/code&gt;中的&lt;code&gt;STRAVA_REFRESH_TOKEN&lt;/code&gt;可能过期，需要重新获取。又或者压根就忘记了获取&lt;code&gt;refresh_token&lt;/code&gt;，需要重新获取。&lt;/p&gt;
&lt;h3 id=&#34;failed-to-connect-to-nominatimopenstreetmaporg&#34;&gt;Failed to connect to nominatim.openstreetmap.org&lt;/h3&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//2025/02/22/86b6394d0eca978d164927d9129641ea.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/22/86b6394d0eca978d164927d9129641ea.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;因为需要生成一些地图热点信息，所以需要访问 nominatim.openstreetmap.org，所以需要配置代理。&lt;/p&gt;
&lt;h3 id=&#34;tring-to-calculate-the-relative-power-for-activity&#34;&gt;Tring to calculate the relative power for activity&lt;/h3&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//2025/02/23/e39d6ef29c3288646bfd500908e58e0c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/23/e39d6ef29c3288646bfd500908e58e0c.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;因为需要计算相对功率重量比（w/kg），所以需要配置体重历史。如果发现某个活动的时间点没有配置体重信息就会报这个错。看看&lt;code&gt;.env&lt;/code&gt;中的&lt;code&gt;ATHLETE_WEIGHTS&lt;/code&gt;是否配置正确。我建议直接修改成我提供的数据，也就是从 1970 年开始都是当前的体重。因为我尝试配置了自己的体重历史，但是一直报错，所以直接改成了这个。&lt;/p&gt;
&lt;h3 id=&#34;looks-like-you-still-need-to-import-your-strava-data&#34;&gt;Looks like you still need to import your strava data&lt;/h3&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//2025/02/23/e31949d1bf03a1df838b21a74908bfbc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/23/e31949d1bf03a1df838b21a74908bfbc.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;code&gt;docker-compose up -d&lt;/code&gt;，而没有执行导入数据的命令，那么就会出现这个错误。需要执行导入数据的命令。如果你已经导入成功了，如果还报这个错，就需要重启一下容器。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="strava-statistics-部署文档">Strava Statistics 部署文档</h1>
<p><a href="https://github.com/robiningelbrecht/strava-statistics">Strava Statistics</a> 是一个自托管工具，利用 Strava 数据生成个人运动统计，支持 Docker 部署，提供丰富的可视化分析。下面介绍如何使用 Docker Compose 部署。</p>
<h2 id="申请-strava-的-api-权限">申请 Strava 的 API 权限</h2>
<p>在<a href="https://lifeislife.cn/awesome-cycling/docs/training-fitness/zwiftoffline/">ZwiftOffline</a>中也需要用到 API 权限，因为不同应用程序也不冲突，懒得再重新申请，所以以下填的信息使用的 ZwiftOffline 的 API 权限。</p>
<p>登录<a href="https://www.strava.com/settings/api">Strava</a>，申请 API，获取<code>Client ID</code>和<code>Client Secret</code>。</p>
<p>填写内容可以参考下图：</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//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.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>其中授权回调域填写：launcher.zwift.com</p>
<p>申请完成后会得到下面的信息：</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//2025/02/09/a5024f9e2e150810251222315be11ac9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/a5024f9e2e150810251222315be11ac9.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>将下面链接中的<code>client_id</code>值换成上图中的客户 ID，然后浏览器中访问这个链接：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">http://www.strava.com/oauth/authorize?client_id=xxxxxxx&amp;response_type=code&amp;redirect_uri=http://localhost/exchange_token&amp;approval_prompt=force&amp;scope=activity:read_all
</span></span></code></pre></div><p>点击授权：</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//2025/03/08/89a9c1d81d40e6b4687531ab92d689a2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/03/08/89a9c1d81d40e6b4687531ab92d689a2.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>此时跳转到一个无法访问的网页，将地址栏中的<code>code</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//2025/03/08/c3a74e83d363bb0f453ad37712dd28ac.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/03/08/c3a74e83d363bb0f453ad37712dd28ac.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>需要用这个<code>code</code>发起一个 cURL 请求得到<code>refresh_token</code>，具体做法是在命令行终端中执行下面的命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">	curl -X POST https://www.strava.com/oauth/token <span class="se">\
</span></span></span><span class="line"><span class="cl">	-F <span class="nv">client_id</span><span class="o">=</span>YOURCLIENTID <span class="se">\
</span></span></span><span class="line"><span class="cl">	-F <span class="nv">client_secret</span><span class="o">=</span>YOURCLIENTSECRET <span class="se">\
</span></span></span><span class="line"><span class="cl">	-F <span class="nv">code</span><span class="o">=</span>AUTHORIZATIONCODE <span class="se">\
</span></span></span><span class="line"><span class="cl">	-F <span class="nv">grant_type</span><span class="o">=</span>authorization_code
</span></span></code></pre></div><ul>
<li><code>client_id</code>值替换为申请得到的<code>客户ID</code></li>
<li><code>client_secret</code>值替换为申请得到的<code>客户端密钥</code></li>
<li><code>code</code>替换为上一步浏览器中得到的<code>code</code></li>
</ul>
<p>执行成功后可以得到下面的返回信息，需要将返回值中的<code>refresh_token</code>和<code>access_token</code>记录下来，后面需要用到：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">{</span><span class="s2">&#34;token_type&#34;</span>:<span class="s2">&#34;Bearer&#34;</span>,<span class="s2">&#34;expires_at&#34;</span>:1740213400,<span class="s2">&#34;expires_in&#34;</span>:21600,<span class="s2">&#34;refresh_token&#34;</span>:<span class="s2">&#34;123456789123456789&#34;</span>,<span class="s2">&#34;access_token&#34;</span>:<span class="s2">&#34;123456789123456789&#34;</span>,<span class="s2">&#34;athlete&#34;</span>:<span class="o">{</span><span class="s2">&#34;id&#34;</span>:117756825,<span class="s2">&#34;username&#34;</span>:<span class="s2">&#34;dunky_zhang&#34;</span>,<span class="s2">&#34;resource_state&#34;</span>:2,<span class="s2">&#34;firstname&#34;</span>:<span class="s2">&#34;Dominic&#34;</span>,<span class="s2">&#34;lastname&#34;</span>:<span class="s2">&#34;Zhang&#34;</span>,<span class="s2">&#34;bio&#34;</span>:<span class="s2">&#34;骑行小白&#34;</span>,<span class="s2">&#34;city&#34;</span>:<span class="s2">&#34;&#34;</span>,<span class="s2">&#34;state&#34;</span>:<span class="s2">&#34;&#34;</span>,<span class="s2">&#34;country&#34;</span>:null,<span class="s2">&#34;sex&#34;</span>:<span class="s2">&#34;M&#34;</span>,<span class="s2">&#34;premium&#34;</span>:true,<span class="s2">&#34;summit&#34;</span>:true,<span class="s2">&#34;created_at&#34;</span>:<span class="s2">&#34;2023-05-10T13:54:32Z&#34;</span>,<span class="s2">&#34;updated_at&#34;</span>:<span class="s2">&#34;2025-02-06T05:29:52Z&#34;</span>,<span class="s2">&#34;badge_type_id&#34;</span>:1,<span class="s2">&#34;weight&#34;</span>:66.0,<span class="s2">&#34;profile_medium&#34;</span>:<span class="s2">&#34;https://dgalywyr863hv.cloudfront.net/pictures/athletes/117756825/32138099/2/medium.jpg&#34;</span>,<span class="s2">&#34;profile&#34;</span>:<span class="s2">&#34;https://dgalywyr863hv.cloudfront.net/pictures/athletes/117756825/32138099/2/large.jpg&#34;</span>,<span class="s2">&#34;friend&#34;</span>:null,<span class="s2">&#34;follower&#34;</span>:null<span class="o">}}</span><span class="c1">#</span>
</span></span></code></pre></div><h2 id="docker-compose-部署-strava-statistics">Docker Compose 部署 Strava Statistics</h2>
<p>创建目录用于存放 Strava Statistics 的配置文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir statistics-for-strava
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> statistics-for-strava
</span></span></code></pre></div><p>创建 <code>docker-compose.yml</code> 文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">touch docker-compose.yml
</span></span></code></pre></div><p>编辑 <code>docker-compose.yml</code> 文件，填入以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">app</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">robiningelbrecht/strava-statistics:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./build:/var/www/build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./storage/database:/var/www/storage/database</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./storage/files:/var/www/storage/files</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">env_file</span><span class="p">:</span><span class="w"> </span><span class="l">./.env</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">8080</span><span class="p">:</span><span class="m">8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">PUID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">PGID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TZ=Asia/Shanghai</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 因为 Strava 的 API 需要代理访问，所以下面设置了我的代理地址，请你根据自己的代理地址进行设置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">http_proxy=http://192.168.1.9:7890</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">https_proxy=http://192.168.1.9:7890</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">all_proxy=socks5://192.168.1.9:7891</span><span class="w">
</span></span></span></code></pre></div><p>创建 <code>.env</code> 文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">touch .env
</span></span></code></pre></div><p>编辑 <code>.env</code> 文件，填入以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 应用程序将托管的URL。此URL将用于清单文件中。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 这将允许您将Web应用程序作为原生应用程序安装到您的设备上。</span>
</span></span><span class="line"><span class="cl"><span class="nv">MANIFEST_APP_URL</span><span class="o">=</span>http://localhost:8080/
</span></span><span class="line"><span class="cl"><span class="c1"># 您的Strava应用程序的客户端ID。</span>
</span></span><span class="line"><span class="cl"><span class="nv">STRAVA_CLIENT_ID</span><span class="o">=</span>xxxxxxx
</span></span><span class="line"><span class="cl"><span class="c1"># 您的Strava应用程序的客户端密钥。</span>
</span></span><span class="line"><span class="cl"><span class="nv">STRAVA_CLIENT_SECRET</span><span class="o">=</span>xxxxxxxxxxxxxx
</span></span><span class="line"><span class="cl"><span class="c1"># 您的Strava应用程序的刷新令牌。在上一步获取到的</span>
</span></span><span class="line"><span class="cl"><span class="nv">STRAVA_REFRESH_TOKEN</span><span class="o">=</span>xxxxxxxxxxxxxx
</span></span><span class="line"><span class="cl"><span class="c1"># Strava API有限速限制（https://github.com/robiningelbrecht/strava-statistics/wiki），</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 为了确保我们不会达到限速，我们希望限制每次导入处理的新活动数量。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 考虑到每天有1000次请求限制，并且导入一个新活动最多可能需要3次API调用，250应该是一个安全的数字。</span>
</span></span><span class="line"><span class="cl"><span class="nv">NUMBER_OF_NEW_ACTIVITIES_TO_PROCESS_PER_IMPORT</span><span class="o">=</span><span class="m">250</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 定期运行导入和HTML构建的计划。留空以禁用定期导入。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 默认计划为每天凌晨04:05运行一次。如果您不知道什么是cron表达式，请勿更改此项。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 请确保不要过于频繁地运行导入，以避免达到Strava API的限速。每天一次应该足够。</span>
</span></span><span class="line"><span class="cl"><span class="nv">IMPORT_AND_BUILD_SCHEDULE</span><span class="o">=</span><span class="s2">&#34;5 4 * * *&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 设置用于计划的时区</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 有效的时区可以在此处找到（TZ标识符列）：https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List</span>
</span></span><span class="line"><span class="cl"><span class="nv">TZ</span><span class="o">=</span>Asia/Shanghai
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 允许的选项：en_US、fr_FR 或 nl_BE</span>
</span></span><span class="line"><span class="cl"><span class="nv">LOCALE</span><span class="o">=</span>en_US
</span></span><span class="line"><span class="cl"><span class="c1"># 允许的选项：metric（公制）或 imperial（英制）</span>
</span></span><span class="line"><span class="cl"><span class="nv">UNIT_SYSTEM</span><span class="o">=</span>metric
</span></span><span class="line"><span class="cl"><span class="c1"># 渲染应用程序时使用的时间格式</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 允许的格式：24 或 12（包括AM和PM）</span>
</span></span><span class="line"><span class="cl"><span class="nv">TIME_FORMAT</span><span class="o">=</span><span class="m">24</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 渲染应用程序时使用的日期格式</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 允许的格式：DAY-MONTH-YEAR（日-月-年）或 MONTH-DAY-YEAR（月-日-年）</span>
</span></span><span class="line"><span class="cl"><span class="nv">DATE_FORMAT</span><span class="o">=</span>DAY-MONTH-YEAR
</span></span><span class="line"><span class="cl"><span class="c1"># 要导入的运动类型。留空以导入所有运动类型。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 通过此列表，您还可以决定运动类型的渲染顺序。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 完整的允许选项列表可在以下地址找到：https://github.com/robiningelbrecht/strava-statistics/wiki/Supported-sport-types/</span>
</span></span><span class="line"><span class="cl"><span class="nv">SPORT_TYPES_TO_IMPORT</span><span class="o">=</span><span class="s1">&#39;[]&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 您的生日。需要用于计算心率区间。</span>
</span></span><span class="line"><span class="cl"><span class="nv">ATHLETE_BIRTHDAY</span><span class="o">=</span>1999-01-01
</span></span><span class="line"><span class="cl"><span class="c1"># 体重历史（单位为千克或磅，取决于UNIT_SYSTEM）。需要用于计算相对功率重量比（w/kg）。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 查看更多信息：https://github.com/robiningelbrecht/strava-statistics/wiki</span>
</span></span><span class="line"><span class="cl"><span class="nv">ATHLETE_WEIGHTS</span><span class="o">=</span><span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;1970-01-01&#34;: 68
</span></span></span><span class="line"><span class="cl"><span class="s1">}&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># FTP（功能阈值功率）历史。需要用于计算活动压力水平。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 查看更多信息：https://github.com/robiningelbrecht/strava-statistics/wiki</span>
</span></span><span class="line"><span class="cl"><span class="nv">FTP_VALUES</span><span class="o">=</span><span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;2024-12-12&#34;: 235
</span></span></span><span class="line"><span class="cl"><span class="s1">}&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 包含ntfy主题的完整URL。此主题将用于在新的HTML构建运行时通知您。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 留空以禁用通知。</span>
</span></span><span class="line"><span class="cl"><span class="nv">NTFY_URL</span><span class="o">=</span><span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 在导入期间要跳过的活动ID数组。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 这允许您在导入时跳过特定活动。</span>
</span></span><span class="line"><span class="cl"><span class="nv">ACTIVITIES_TO_SKIP_DURING_IMPORT</span><span class="o">=</span><span class="s1">&#39;[]&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建/拥有由strava-statistics管理的文件的UID和GID</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 可能仅在Linux主机上需要，详见Wiki中的文件权限部分</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PUID=</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PGID=</span>
</span></span></code></pre></div><p>开始导入数据：</p>
<ul>
<li>
<p>启动容器</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker-compose up -d
</span></span></code></pre></div></li>
<li>
<p>导入数据</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose <span class="nb">exec</span> App bin/console app:strava:import-data
</span></span><span class="line"><span class="cl">docker compose <span class="nb">exec</span> App bin/console app:strava:build-files
</span></span></code></pre></div><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//2025/02/23/af3c468483a1c46544c02b5c2d8124ac.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/23/af3c468483a1c46544c02b5c2d8124ac.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>
</li>
<li>
<p>访问 <code>localhost:8080</code> 查看数据</p>
</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//2025/03/08/07a1a21d1c53ab4560de20f5cbcb60db.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/03/08/07a1a21d1c53ab4560de20f5cbcb60db.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>
<h2 id="常见问题">常见问题</h2>
<h3 id="401-unauthorized">401 Unauthorized</h3>
<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//2025/02/22/a541f82e213a98de7f9e69a614e0e7bc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/22/a541f82e213a98de7f9e69a614e0e7bc.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><code>.env</code>中的<code>STRAVA_REFRESH_TOKEN</code>可能过期，需要重新获取。又或者压根就忘记了获取<code>refresh_token</code>，需要重新获取。</p>
<h3 id="failed-to-connect-to-nominatimopenstreetmaporg">Failed to connect to nominatim.openstreetmap.org</h3>
<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//2025/02/22/86b6394d0eca978d164927d9129641ea.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/22/86b6394d0eca978d164927d9129641ea.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>因为需要生成一些地图热点信息，所以需要访问 nominatim.openstreetmap.org，所以需要配置代理。</p>
<h3 id="tring-to-calculate-the-relative-power-for-activity">Tring to calculate the relative power for activity</h3>
<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//2025/02/23/e39d6ef29c3288646bfd500908e58e0c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/23/e39d6ef29c3288646bfd500908e58e0c.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>因为需要计算相对功率重量比（w/kg），所以需要配置体重历史。如果发现某个活动的时间点没有配置体重信息就会报这个错。看看<code>.env</code>中的<code>ATHLETE_WEIGHTS</code>是否配置正确。我建议直接修改成我提供的数据，也就是从 1970 年开始都是当前的体重。因为我尝试配置了自己的体重历史，但是一直报错，所以直接改成了这个。</p>
<h3 id="looks-like-you-still-need-to-import-your-strava-data">Looks like you still need to import your strava data</h3>
<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//2025/02/23/e31949d1bf03a1df838b21a74908bfbc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/23/e31949d1bf03a1df838b21a74908bfbc.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>如果你只执行了<code>docker-compose up -d</code>，而没有执行导入数据的命令，那么就会出现这个错误。需要执行导入数据的命令。如果你已经导入成功了，如果还报这个错，就需要重启一下容器。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Zwift 单机版安装教程</title>
      <link>https://lifeislife.cn/posts/zwift%E5%8D%95%E6%9C%BA%E7%89%88%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/</link>
      <pubDate>Sun, 09 Feb 2025 10:34:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/zwift%E5%8D%95%E6%9C%BA%E7%89%88%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/</guid>
      <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/zoffline/zwift-offline?tab=readme-ov-file&#34;&gt;ZwiftOffline&lt;/a&gt;是一个开源社区自制的 Zwift 的 server 端，可以在没有网络的情况下进行 Zwift 课程或路线训练，多平台支持。&lt;/p&gt;
&lt;p&gt;本教程将详细介绍如何在 Windows 系统上安装和配置 Zwift 单机版，包括基础安装、账号配置、Strava 同步、训练课程上传以及移动端 Companion 配置等完整流程。&lt;/p&gt;
&lt;h2 id=&#34;一准备工作&#34;&gt;一、准备工作&lt;/h2&gt;
&lt;h3 id=&#34;11-下载必要文件&#34;&gt;1.1 下载必要文件&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ZwiftOffline 主程序&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从&lt;a href=&#34;https://github.com/zoffline/zwift-offline/releases/latest&#34;&gt;GitHub Release 页面&lt;/a&gt;下载最新的 zoffline 发布版（&lt;code&gt;.exe&lt;/code&gt;文件）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;配置辅助工具&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从&lt;a href=&#34;https://github.com/oldnapalm/zoffline-helper/releases/latest&#34;&gt;GitHub Release&lt;/a&gt;下载&lt;code&gt;zoffline-helper.zip&lt;/code&gt;配置脚本文件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Zwift 官方客户端&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前往&lt;a href=&#34;https://www.zwift.com/&#34;&gt;Zwift 官网&lt;/a&gt;，拉到页面底部，下载并安装 Zwift 软件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重要&lt;/strong&gt;：记住安装路径，后续配置需要用到（例如：&lt;code&gt;C:\Program Files (x86)\Zwift&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;12-创建运行目录&#34;&gt;1.2 创建运行目录&lt;/h3&gt;
&lt;p&gt;创建一个目录用于存放 ZwiftOffline 相关文件，例如新建&lt;code&gt;zwiftoffline&lt;/code&gt;目录。&lt;/p&gt;
&lt;h2 id=&#34;二基础安装与配置&#34;&gt;二、基础安装与配置&lt;/h2&gt;
&lt;h3 id=&#34;21-初始化-zwiftoffline&#34;&gt;2.1 初始化 ZwiftOffline&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;将下载的&lt;code&gt;zoffline.exe&lt;/code&gt;文件保存到&lt;code&gt;zwiftoffline&lt;/code&gt;目录&lt;/li&gt;
&lt;li&gt;运行&lt;code&gt;zoffline.exe&lt;/code&gt;，程序会在同目录下自动创建&lt;code&gt;storage&lt;/code&gt;目录（用于保存 Zwift 进度）&lt;/li&gt;
&lt;li&gt;看到&lt;code&gt;storage&lt;/code&gt;目录生成后，按&lt;code&gt;Ctrl+C&lt;/code&gt;关闭 zoffline，稍后进行配置&lt;/li&gt;
&lt;/ol&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//2025/02/10/ed838fae543dc8d607c231d89fc4888b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/10/ed838fae543dc8d607c231d89fc4888b.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;22-安装并更新-zwift-客户端&#34;&gt;2.2 安装并更新 Zwift 客户端&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;运行刚安装的 Zwift 程序，客户端会开始更新游戏本体
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 重要提示&lt;/strong&gt;：必须在配置网络之前完成此步骤，因为后续配置会修改 hosts 文件，导致无法连接到 Zwift 服务器下载游戏本体&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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//2025/02/09/734f7507b1e68958bba2ea2525216752.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/734f7507b1e68958bba2ea2525216752.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;23-配置网络连接&#34;&gt;2.3 配置网络连接&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;将下载的&lt;code&gt;zoffline-helper.zip&lt;/code&gt;解压到&lt;code&gt;zwiftoffline&lt;/code&gt;目录&lt;/li&gt;
&lt;li&gt;进入&lt;code&gt;zwiftoffline/zoffline-helper&lt;/code&gt;目录&lt;/li&gt;
&lt;li&gt;右键点击&lt;code&gt;configure_client.bat&lt;/code&gt;，选择&lt;strong&gt;以管理员身份运行&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;脚本会自动配置 zoffline，期间会弹窗让你选择 Zwift 安装目录（即之前记住的路径）&lt;/li&gt;
&lt;/ol&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//2025/02/19/52b44adfe177bde733156c3d2faefe5e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/19/52b44adfe177bde733156c3d2faefe5e.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 start=&#34;5&#34;&gt;
&lt;li&gt;配置完成后，会显示如下结果：&lt;/li&gt;
&lt;/ol&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//2025/02/09/6769deefaa7c142c42411a43af345e8d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/6769deefaa7c142c42411a43af345e8d.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;24-启动并验证&#34;&gt;2.4 启动并验证&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;进入&lt;code&gt;zwiftoffline&lt;/code&gt;目录，双击运行&lt;code&gt;zoffline.exe&lt;/code&gt;（文件名可能因版本而异，如&lt;code&gt;zoffline_1.0.140279.exe&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&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//2025/02/09/029bd1af360095a4a279b52c822cd5c7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/029bd1af360095a4a279b52c822cd5c7.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 start=&#34;2&#34;&gt;
&lt;li&gt;启动原版 Zwift 程序，检查登录界面
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;成功标志&lt;/strong&gt;：显示 ZwiftOffline 的登录页面（如下图）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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//2025/02/09/e577c5169d652b1ae201a337a0d292f8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/e577c5169d652b1ae201a337a0d292f8.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;ul&gt;
&lt;li&gt;&lt;strong&gt;失败标志&lt;/strong&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//2025/02/19/d8c93c7c78d2f33555bed4a3be9d671f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/19/d8c93c7c78d2f33555bed4a3be9d671f.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 start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 重要提示&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;如果显示的是原版登录页面，检查是否开启了代理，关闭代理后重试&lt;/li&gt;
&lt;li&gt;将输入法切换为&lt;strong&gt;微软英文输入法&lt;/strong&gt;，点击&lt;code&gt;Start Zwift&lt;/code&gt;开始游戏&lt;/li&gt;
&lt;li&gt;使用搜狗输入法等第三方输入法可能会卡在登录页面&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;三高级配置&#34;&gt;三、高级配置&lt;/h2&gt;
&lt;h3 id=&#34;31-同步原有账号信息&#34;&gt;3.1 同步原有账号信息&lt;/h3&gt;
&lt;p&gt;如果不配置此项，使用 Zwift offline 时会显示为新账号，而不是你原来的账号信息。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;配置步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在登录页面点击设置按钮进入设置界面&lt;/li&gt;
&lt;/ol&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//2025/02/09/ce3667b3f69bf3ab3fd81c191aee51f8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/ce3667b3f69bf3ab3fd81c191aee51f8.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 start=&#34;2&#34;&gt;
&lt;li&gt;填写你的 Zwift 邮箱和密码，勾选所有选项，点击 Submit 提交&lt;/li&gt;
&lt;/ol&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//2025/02/09/9cc77352a1f299516e1f76989bdc60c3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/9cc77352a1f299516e1f76989bdc60c3.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 start=&#34;3&#34;&gt;
&lt;li&gt;配置完成后，再次登录 Zwift 时就会显示为原来的账号了&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;32-配置-strava-自动上传&#34;&gt;3.2 配置 Strava 自动上传&lt;/h3&gt;
&lt;h4 id=&#34;321-申请-strava-api&#34;&gt;3.2.1 申请 Strava API&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;登录&lt;a href=&#34;https://www.strava.com/settings/api&#34;&gt;Strava API 设置页面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;创建新应用，填写以下信息：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用名称&lt;/strong&gt;：自定义（如：ZwiftOffline）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类别&lt;/strong&gt;：其他&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;授权回调域&lt;/strong&gt;：&lt;code&gt;launcher.zwift.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网站&lt;/strong&gt;：可留空或填写个人网站&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.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 start=&#34;3&#34;&gt;
&lt;li&gt;申请完成后，记录下&lt;code&gt;Client ID&lt;/code&gt;和&lt;code&gt;Client Secret&lt;/code&gt;&lt;/li&gt;
&lt;/ol&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//2025/02/09/a5024f9e2e150810251222315be11ac9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/a5024f9e2e150810251222315be11ac9.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;h4 id=&#34;322-在-zwiftoffline-中配置-strava&#34;&gt;3.2.2 在 ZwiftOffline 中配置 Strava&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在登录页面进入设置，填写&lt;code&gt;Client ID&lt;/code&gt;和&lt;code&gt;Client Secret&lt;/code&gt;&lt;/li&gt;
&lt;/ol&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//2025/02/09/29afa480ea0c6ceba03f89a14b30c987.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/29afa480ea0c6ceba03f89a14b30c987.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//2025/02/09/84b0932cd611d78a1497f82aafa86fb7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/84b0932cd611d78a1497f82aafa86fb7.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;code&gt;Submit&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//2025/02/09/2457689c2493d63ffa77e465948d3d84.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/2457689c2493d63ffa77e465948d3d84.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;h4 id=&#34;323-授权-strava-访问&#34;&gt;3.2.3 授权 Strava 访问&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;这一步可以省略，如果通过上面的步骤没法上传，再尝试这个步骤手动配置&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;右键以管理员身份运行&lt;code&gt;zoffline-helper/disable_zoffline.bat&lt;/code&gt;，清除 hosts 配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在命令行中运行以下命令（替换&lt;code&gt;CLIENT_ID&lt;/code&gt;和&lt;code&gt;CLIENT_SECRET&lt;/code&gt;为你的实际值）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zoffline-helper/strava_auth.exe --client-id CLIENT_ID --client-secret CLIENT_SECRET
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;浏览器会自动打开或手动访问&lt;code&gt;localhost:8000&lt;/code&gt;进行授权&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&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//2025/05/18/4b0dbbd3bd2dd82b1c6da8327e494ea3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/05/18/4b0dbbd3bd2dd82b1c6da8327e494ea3.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//2025/05/18/05c02cfd9cd98aaf1e198d2f99b58f5a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/05/18/05c02cfd9cd98aaf1e198d2f99b58f5a.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 start=&#34;4&#34;&gt;
&lt;li&gt;授权完成后，将生成的&lt;code&gt;strava_token.txt&lt;/code&gt;文件移动到&lt;code&gt;storage/1&lt;/code&gt;目录下&lt;/li&gt;
&lt;li&gt;右键以管理员身份运行&lt;code&gt;zoffline-helper/configure_client.bat&lt;/code&gt;，重新配置 hosts&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;33-上传自定义训练课程&#34;&gt;3.3 上传自定义训练课程&lt;/h3&gt;
&lt;p&gt;如果你习惯在 Intervals ICU 等平台创建训练课程，可以手动上传到 Zwift 单机版。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;上传步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;将你的&lt;code&gt;.zwo&lt;/code&gt;训练课程文件保存到以下目录：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Documents/Zwift/Workouts/1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&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//2025/02/15/f18d29b2d3e548bf2fe0878d54b2f45f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/15/f18d29b2d3e548bf2fe0878d54b2f45f.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 start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 注意&lt;/strong&gt;：不是&lt;code&gt;zwiftoffline\storage\1\customworkouts&lt;/code&gt;目录，如果放错位置会导致课程无法上传&lt;/li&gt;
&lt;/ol&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//2025/02/15/edd5d0ab56c733e794d196838851dfe3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/15/edd5d0ab56c733e794d196838851dfe3.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-配置-garmin-connect&#34;&gt;3.4 配置 Garmin Connect&lt;/h3&gt;
&lt;p&gt;Garmin Connect 可以同步你的 Zwift 活动数据到 Garmin 设备。&lt;/p&gt;
&lt;h4 id=&#34;341-配置账号&#34;&gt;3.4.1 配置账号&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PC 端配置&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 ZwiftOffline 登录窗口点击&amp;quot;Settings - Garmin&amp;quot;按钮&lt;/li&gt;
&lt;li&gt;输入你的 Garmin Connect 账号和密码&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安卓端配置&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;访问 &lt;code&gt;https://&amp;lt;zoffline_ip&amp;gt;/garmin/zoffline/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输入你的 Garmin Connect 账号和密码&lt;/li&gt;
&lt;li&gt;将&lt;code&gt;&amp;lt;zoffline_ip&amp;gt;&lt;/code&gt;替换为运行 zoffline 的电脑 IP 地址&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;342-多因素认证mfa&#34;&gt;3.4.2 多因素认证（MFA）&lt;/h4&gt;
&lt;p&gt;如果你的账号启用了多因素认证，需要额外配置：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用 Python 脚本（推荐）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运行&lt;code&gt;garmin_auth.py&lt;/code&gt;脚本&lt;/li&gt;
&lt;li&gt;脚本会在运行目录下生成&lt;code&gt;garth&lt;/code&gt;文件夹&lt;/li&gt;
&lt;li&gt;将&lt;code&gt;garth&lt;/code&gt;文件夹移动到&lt;code&gt;storage/1&lt;/code&gt;目录下&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用 Windows 可执行文件&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果未安装 Python，可以从&lt;a href=&#34;https://github.com/oldnapalm/zoffline-helper/releases/latest&#34;&gt;zoffline-helper Release 页面&lt;/a&gt;下载&lt;code&gt;garmin_auth.exe&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;运行&lt;code&gt;garmin_auth.exe&lt;/code&gt;，同样会生成&lt;code&gt;garth&lt;/code&gt;文件夹&lt;/li&gt;
&lt;li&gt;将&lt;code&gt;garth&lt;/code&gt;文件夹移动到&lt;code&gt;storage/1&lt;/code&gt;目录下&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;35-配置-intervalsicu&#34;&gt;3.5 配置 Intervals.icu&lt;/h3&gt;
&lt;p&gt;Intervals.icu 是一个强大的训练分析平台，可以同步 Zwift 活动数据。&lt;/p&gt;
&lt;h4 id=&#34;351-获取-api-凭证&#34;&gt;3.5.1 获取 API 凭证&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;登录 &lt;a href=&#34;https://intervals.icu/settings&#34;&gt;Intervals.icu 设置页面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;进入&amp;quot;Developer Settings&amp;quot;（开发者设置）部分&lt;/li&gt;
&lt;li&gt;复制以下信息：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Athlete ID&lt;/strong&gt;（运动员 ID）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Key&lt;/strong&gt;（API 密钥）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;352-配置账号&#34;&gt;3.5.2 配置账号&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PC 端配置&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 ZwiftOffline 登录窗口点击&amp;quot;Settings - Intervals&amp;quot;按钮&lt;/li&gt;
&lt;li&gt;输入之前复制的 Athlete ID 和 API Key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安卓端配置&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;访问 &lt;code&gt;https://&amp;lt;zoffline_ip&amp;gt;/intervals/zoffline/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输入 Athlete ID 和 API Key&lt;/li&gt;
&lt;li&gt;将&lt;code&gt;&amp;lt;zoffline_ip&amp;gt;&lt;/code&gt;替换为运行 zoffline 的电脑 IP 地址&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;36-启用多人游戏&#34;&gt;3.6 启用多人游戏&lt;/h3&gt;
&lt;p&gt;多人游戏功能允许多个用户同时连接到同一个 zoffline 服务器进行骑行。&lt;/p&gt;
&lt;h4 id=&#34;361-基本配置&#34;&gt;3.6.1 基本配置&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;multiplayer.txt&lt;/code&gt;文件（文件内容为空）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;362-远程服务器配置&#34;&gt;3.6.2 远程服务器配置&lt;/h4&gt;
&lt;p&gt;如果 zoffline 运行在与 Zwift 客户端不同的 PC 上：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;server-ip.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;文件内容填写运行 zoffline 的 PC 的 IP 地址&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;端口要求&lt;/strong&gt;：确保以下端口在运行 zoffline 的 PC 上开放：
&lt;ul&gt;
&lt;li&gt;TCP 端口：80, 443, 3025&lt;/li&gt;
&lt;li&gt;UDP 端口：3024&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;363-创建账号&#34;&gt;3.6.3 创建账号&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;启动 Zwift，创建新账号&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 注意&lt;/strong&gt;：此账号仅存在于你的 zoffline 服务器上，与官方 Zwift 账号无关&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;364-启用密码重置功能可选&#34;&gt;3.6.4 启用密码重置功能（可选）&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;gmail_credentials.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;文件内容格式：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;gmail 账号&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;应用密码&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;恢复 URL 主机（可选）&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;获取应用密码&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;访问 &lt;a href=&#34;https://security.google.com/settings/security/apppasswords&#34;&gt;https://security.google.com/settings/security/apppasswords&lt;/a&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;&lt;strong&gt;恢复 URL 主机&lt;/strong&gt;（可选）：
&lt;ul&gt;
&lt;li&gt;第三行可以填写恢复 URL 的主机地址&lt;/li&gt;
&lt;li&gt;如果不填写，将使用服务器 IP 地址作为默认值&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;37-启用-ghosts幽灵&#34;&gt;3.7 启用 Ghosts（幽灵）&lt;/h3&gt;
&lt;p&gt;Ghosts 功能可以保存你之前的骑行记录，并在相同路线上显示为&amp;quot;幽灵&amp;quot;与你一起骑行。&lt;/p&gt;
&lt;h4 id=&#34;371-启用功能&#34;&gt;3.7.1 启用功能&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PC 端&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 zoffline 启动器窗口中勾选&amp;quot;Enable ghosts&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安卓端&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;访问 &lt;code&gt;https://&amp;lt;zoffline_ip&amp;gt;/user/zoffline/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;勾选&amp;quot;Enable ghosts&amp;quot;&lt;/li&gt;
&lt;li&gt;点击&amp;quot;Start Zwift&amp;quot;保存设置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;372-使用说明&#34;&gt;3.7.2 使用说明&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;保存幽灵&lt;/strong&gt;：当你保存活动时，幽灵会自动保存在&lt;code&gt;storage/&amp;lt;player_id&amp;gt;/ghosts/&amp;lt;world&amp;gt;/&amp;lt;route&amp;gt;&lt;/code&gt;目录下&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加载幽灵&lt;/strong&gt;：下次骑行相同路线时，幽灵会自动加载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重新编组&lt;/strong&gt;：在聊天中输入&lt;code&gt;.regroup&lt;/code&gt;命令可以重新编组幽灵&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;373-自定义装备&#34;&gt;3.7.3 自定义装备&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;ghost_profile.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;可以使用&lt;code&gt;find_equip.py&lt;/code&gt;脚本来自动填充此文件&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;38-启用-bots机器人&#34;&gt;3.8 启用 Bots（机器人）&lt;/h3&gt;
&lt;p&gt;Bots 功能可以将保存的幽灵转换为持续骑行的机器人，无论你选择什么路线，它们都会继续骑行。&lt;/p&gt;
&lt;h4 id=&#34;381-启用功能&#34;&gt;3.8.1 启用功能&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;enable_bots.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可选&lt;/strong&gt;：文件内容可以包含一个倍数（如：&lt;code&gt;2&lt;/code&gt;表示双倍机器人数量）
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 警告&lt;/strong&gt;：如果机器人数量过多，可能导致性能问题或功能失效&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;382-控制命令&#34;&gt;3.8.2 控制命令&lt;/h4&gt;
&lt;p&gt;在聊天中使用以下命令控制机器人：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.group&lt;/code&gt;：编组机器人&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.groupall&lt;/code&gt;：编组所有机器人（包括使用倍数时的重复机器人）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.autogroup&lt;/code&gt;：自动编组（当你改变路线时）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.autogroupall&lt;/code&gt;：自动编组所有机器人&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.stopautogroup&lt;/code&gt;：停止自动编组&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.disperse&lt;/code&gt;：随机化机器人位置&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;383-自定义机器人&#34;&gt;3.8.3 自定义机器人&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;bot.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;可以自定义机器人的名称、国籍和装备&lt;/li&gt;
&lt;li&gt;可以使用&lt;code&gt;get_pro_names.py&lt;/code&gt;脚本来自动填充此文件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;随机机器人&lt;/strong&gt;：如果需要随机机器人，可以参考&lt;a href=&#34;https://github.com/oldnapalm/zoffline-bots&#34;&gt;zoffline-bots 仓库&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;39-配置-robopacers机器人配速员&#34;&gt;3.9 配置 RoboPacers（机器人配速员）&lt;/h3&gt;
&lt;p&gt;RoboPacers 是使用功率模拟器保存的幽灵，可以创建完美的循环配速员。&lt;/p&gt;
&lt;h4 id=&#34;391-获取-robopacers&#34;&gt;3.9.1 获取 RoboPacers&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;可以从&lt;a href=&#34;https://github.com/oldnapalm/zoffline-bots&#34;&gt;zoffline-bots 仓库&lt;/a&gt;下载预制的 RoboPacers&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;392-创建要求&#34;&gt;3.9.2 创建要求&lt;/h4&gt;
&lt;p&gt;要创建可用的 RoboPacer，需要满足以下条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;更新频率&lt;/strong&gt;：必须使用 1 秒的更新频率录制（默认是 3 秒）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;循环要求&lt;/strong&gt;：活动必须从同一位置和速度开始和结束，否则机器人无法平滑循环&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置文件要求&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;必须包含唯一的玩家 ID&lt;/li&gt;
&lt;li&gt;必须包含路线 ID&lt;/li&gt;
&lt;li&gt;这样当你加入机器人时，会在交叉路口走相同的转弯&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;393-编辑工具&#34;&gt;3.9.3 编辑工具&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;bot_editor.py 脚本&lt;/strong&gt;：可以用于修改以下文件：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;profile.bin&lt;/code&gt;：设置名称、玩家 ID 和路线 ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;route.bin&lt;/code&gt;：裁剪多余的点以创建完美循环&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;394-创建动态-robopacer&#34;&gt;3.9.4 创建动态 RoboPacer&lt;/h4&gt;
&lt;p&gt;要创建动态 RoboPacer（在上坡时增加功率，下坡时减少功率）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用 &lt;a href=&#34;https://github.com/oldnapalm/zwift-offline/blob/master/standalone_power.py&#34;&gt;standalone_power.py&lt;/a&gt; 脚本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件要求&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;2 个 ANT 适配器&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mch/python-ant&#34;&gt;python-ant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/oldnapalm/zwift-offline/blob/master/PowerMeterTx.py&#34;&gt;PowerMeterTx.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;310-使用-bookmarks书签&#34;&gt;3.10 使用 Bookmarks（书签）&lt;/h3&gt;
&lt;p&gt;书签功能可以保存你的位置，方便快速跳转到特定地点。&lt;/p&gt;
&lt;h4 id=&#34;3101-自动保存书签&#34;&gt;3.10.1 自动保存书签&lt;/h4&gt;
&lt;p&gt;当你完成活动时，你的最后位置会自动保存为书签。&lt;/p&gt;
&lt;h4 id=&#34;3102-手动保存书签&#34;&gt;3.10.2 手动保存书签&lt;/h4&gt;
&lt;p&gt;在聊天中使用以下命令保存书签：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.bookmark &amp;lt;名称&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;code&gt;.bookmark 起点&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&#34;3103-使用书签&#34;&gt;3.10.3 使用书签&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;从书签开始新活动&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在主页屏幕选择&amp;quot;Join a Zwifter&amp;quot;&lt;/li&gt;
&lt;li&gt;选择你想要的书签&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;传送到书签&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用操作栏上的传送图标&lt;/li&gt;
&lt;li&gt;选择目标书签进行传送&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;311-启用历史排行榜&#34;&gt;3.11 启用历史排行榜&lt;/h3&gt;
&lt;p&gt;历史排行榜功能可以覆盖 60 分钟的实时结果和 90 天的个人记录，显示所有时间的最佳成绩。&lt;/p&gt;
&lt;h4 id=&#34;3111-启用功能&#34;&gt;3.11.1 启用功能&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;all_time_leaderboards.txt&lt;/code&gt;文件（文件内容可以为空）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;3112-功能说明&#34;&gt;3.11.2 功能说明&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;排行榜&lt;/strong&gt;：显示所有时间的最佳成绩，而不仅仅是最近 60 分钟或 90 天的记录&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;荣誉衫&lt;/strong&gt;：仍然有效期为 60 分钟，但只有在创造新的历史记录时才会授予&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;312-解锁装备&#34;&gt;3.12 解锁装备&lt;/h3&gt;
&lt;p&gt;可以解锁特殊装备或所有装备。&lt;/p&gt;
&lt;h4 id=&#34;3121-解锁特殊装备&#34;&gt;3.12.1 解锁特殊装备&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;unlock_entitlements.txt&lt;/code&gt;文件（文件内容可以为空）&lt;/li&gt;
&lt;li&gt;这将解锁需要特殊权限的装备&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;3122-解锁所有装备&#34;&gt;3.12.2 解锁所有装备&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;unlock_all_equipment.txt&lt;/code&gt;文件（文件内容可以为空）&lt;/li&gt;
&lt;li&gt;这将解锁所有可用装备&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;313-配置-cdn-代理&#34;&gt;3.13 配置 CDN 代理&lt;/h3&gt;
&lt;p&gt;CDN 代理功能可以从 Zwift 官方服务器获取地图时间表和更新文件。&lt;/p&gt;
&lt;h4 id=&#34;3131-启用-cdn-代理&#34;&gt;3.13.1 启用 CDN 代理&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;cdn-proxy.txt&lt;/code&gt;文件（文件内容可以为空）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 限制&lt;/strong&gt;：此功能只能在 zoffline 运行在与 Zwift 客户端不同的机器上时使用&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;3132-禁用代理&#34;&gt;3.13.2 禁用代理&lt;/h4&gt;
&lt;p&gt;默认情况下，zoffline 会尝试使用 Google 公共 DNS 来解析 Zwift 主机名，即使 zoffline 与 Zwift 客户端运行在同一台机器上也能工作。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果要禁用此代理功能，在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;disable_proxy.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;3133-从-zoffline-提供更新文件&#34;&gt;3.13.3 从 zoffline 提供更新文件&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;运行&lt;code&gt;get_gameassets.py&lt;/code&gt;脚本下载游戏文件&lt;/li&gt;
&lt;li&gt;这样 zoffline 可以直接提供更新文件，而不需要从 Zwift 服务器获取&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;四移动端-companion-配置&#34;&gt;四、移动端 Companion 配置&lt;/h2&gt;
&lt;p&gt;Zwift Companion 是配套的移动端社交软件，可用于与骑友互动、配对设备、点赞、使用道具等功能。使用离线版 Zwift 需要重新打包 Companion 应用。&lt;/p&gt;
&lt;h3 id=&#34;41-准备工作&#34;&gt;4.1 准备工作&lt;/h3&gt;
&lt;p&gt;官方原版 Companion 会对网络连接和证书做严格验证，需要对其进行&amp;quot;打补丁并重签名&amp;quot;才能连接到本地 zoffline 服务器。&lt;/p&gt;
&lt;h3 id=&#34;42-替换-apk-证书并重新签名&#34;&gt;4.2 替换 APK 证书并重新签名&lt;/h3&gt;
&lt;h4 id=&#34;421-安装-apk-mitm&#34;&gt;4.2.1 安装 apk-mitm&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;安装 Node.js（如果尚未安装）&lt;/li&gt;
&lt;li&gt;使用以下命令全局安装 apk-mitm：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install -g apk-mitm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;其他系统可参考&lt;a href=&#34;https://github.com/shroudedcode/apk-mitm&#34;&gt;apk-mitm 项目 README&lt;/a&gt;自行安装&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;422-修改-apktool-配置&#34;&gt;4.2.2 修改 apktool 配置&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;使用&lt;code&gt;npm root -g&lt;/code&gt;查找 apk-mitm 的实际安装路径&lt;/li&gt;
&lt;li&gt;打开&lt;code&gt;apk-mitm/dist/tools/apktool.js&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;修改&lt;code&gt;decode&lt;/code&gt;方法，添加&lt;code&gt;-resm&lt;/code&gt;和&lt;code&gt;dummy&lt;/code&gt;参数：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;decode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;inputPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;outputPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;decode&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;-resm&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// add this
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;dummy&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// add this
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;inputPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;--output&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;outputPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;--frame-path&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;frameworkPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;decoding&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;423-生成修改后的-apk&#34;&gt;4.2.3 生成修改后的 APK&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;从&lt;a href=&#34;https://github.com/zoffline/zwift-offline&#34;&gt;Zwift Offline 仓库&lt;/a&gt;下载以下文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ssl/cert-zwift-com.pem&lt;/code&gt;（证书文件）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zwift-offline-companion.apk&lt;/code&gt;（原始 APK 文件）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将这两个文件复制到同一目录（如：&lt;code&gt;C:\Users\Administrator\Desktop\zwift-offline-companion&lt;/code&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在该目录下运行以下命令：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apk-mitm --certificate cert-zwift-com.pem zwift-offline-companion.apk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;命令执行完成后，会生成&lt;code&gt;zwift-offline-companion-patched.apk&lt;/code&gt;文件，将其拷贝到手机安装即可&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;43-配置域名重定向&#34;&gt;4.3 配置域名重定向&lt;/h3&gt;
&lt;h4 id=&#34;431-安装-virtual-hosts&#34;&gt;4.3.1 安装 Virtual Hosts&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;下载&lt;a href=&#34;https://github.com/x-falcon/Virtual-Hosts/releases/latest&#34;&gt;virtual hosts&lt;/a&gt;软件&lt;/li&gt;
&lt;li&gt;安装到手机中&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;432-创建-hosts-文件&#34;&gt;4.3.2 创建 hosts 文件&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;创建&lt;code&gt;hosts.txt&lt;/code&gt;文件，内容如下（将&lt;code&gt;&amp;lt;zoffline ip&amp;gt;&lt;/code&gt;替换为你运行 zoffline 的电脑 IP 地址）：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;zoffline ip&amp;gt; us-or-rly101.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;zoffline ip&amp;gt; secure.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 重要提示&lt;/strong&gt;：如果在 PC 上配置过 hosts 文件，可能包含以下映射：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1 cdn.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;此映射不要添加到手机端的 hosts 文件中！&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;将&lt;code&gt;hosts.txt&lt;/code&gt;文件复制到手机，放在便于访问的位置&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;433-启用域名重定向&#34;&gt;4.3.3 启用域名重定向&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;打开 Virtual Hosts 软件&lt;/li&gt;
&lt;li&gt;选择之前创建的&lt;code&gt;hosts.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;启用域名重定向功能&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;44-使用-companion&#34;&gt;4.4 使用 Companion&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;使用离线版的账号登录 Companion，即可正常使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证连接&lt;/strong&gt;：在打开和登录 Companion 时，观察 PC 端运行的 Zwift offline 命令行窗口是否有日志输出
&lt;ul&gt;
&lt;li&gt;如果有日志输出，说明连接正常&lt;/li&gt;
&lt;li&gt;如果没有任何输出，说明请求未成功转发，需要检查 IP 地址配置是否正确&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;五安卓设备直接使用-zwift-offline&#34;&gt;五、安卓设备直接使用 Zwift Offline&lt;/h2&gt;
&lt;p&gt;除了在 PC 上使用 Zwift Offline，你也可以直接在安卓设备上运行 Zwift 并连接到 zoffline 服务器。这种方式不需要 root 权限，但需要在每次安装或更新 Zwift 后重新打补丁。&lt;/p&gt;
&lt;h3 id=&#34;51-安装必要应用&#34;&gt;5.1 安装必要应用&lt;/h3&gt;
&lt;h4 id=&#34;511-下载并安装-zofflineobb&#34;&gt;5.1.1 下载并安装 ZofflineObb&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;从&lt;a href=&#34;https://github.com/Argon2000/ZofflineObbAndroid/releases/latest&#34;&gt;GitHub Release 页面&lt;/a&gt;下载&lt;code&gt;ZofflineObb.apk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;安装到安卓设备上（需要允许安装未知来源应用）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;512-下载并安装-virtual-hosts&#34;&gt;5.1.2 下载并安装 Virtual Hosts&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;从&lt;a href=&#34;https://github.com/x-falcon/Virtual-Hosts/releases/latest&#34;&gt;GitHub Release 页面&lt;/a&gt;下载&lt;code&gt;app-Github-release.apk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;安装到安卓设备上&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;52-配置域名重定向&#34;&gt;5.2 配置域名重定向&lt;/h3&gt;
&lt;h4 id=&#34;521-创建-hoststxt-文件&#34;&gt;5.2.1 创建 hosts.txt 文件&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;创建&lt;code&gt;hosts.txt&lt;/code&gt;文件，可以使用文本编辑器应用或在线工具（如&lt;a href=&#34;https://passwordsgenerator.net/text-editor/&#34;&gt;在线文本编辑器&lt;/a&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件内容如下（将&lt;code&gt;&amp;lt;zoffline ip&amp;gt;&lt;/code&gt;替换为运行 zoffline 的电脑 IP 地址）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;zoffline ip&amp;gt; us-or-rly101.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;zoffline ip&amp;gt; secure.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;zoffline ip&amp;gt; cdn.zwift.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;⚠️ 注意&lt;/strong&gt;：与 Companion 配置不同，安卓设备直接使用需要包含&lt;code&gt;cdn.zwift.com&lt;/code&gt;的映射&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将&lt;code&gt;hosts.txt&lt;/code&gt;文件保存到手机，放在便于访问的位置&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;522-关闭-private-dns&#34;&gt;5.2.2 关闭 Private DNS&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;进入安卓设备的&lt;strong&gt;设置&lt;/strong&gt; &amp;gt; &lt;strong&gt;网络和互联网&lt;/strong&gt; &amp;gt; &lt;strong&gt;高级&lt;/strong&gt; &amp;gt; &lt;strong&gt;Private DNS&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;选择&lt;strong&gt;关闭&lt;/strong&gt;或&lt;strong&gt;自动&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;523-配置-virtual-hosts&#34;&gt;5.2.3 配置 Virtual Hosts&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;打开 Virtual Hosts 应用&lt;/li&gt;
&lt;li&gt;选择之前创建的&lt;code&gt;hosts.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;启用域名重定向功能（确保按钮处于开启状态）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;524-替代方案使用-dns-配置&#34;&gt;5.2.4 替代方案：使用 DNS 配置&lt;/h4&gt;
&lt;p&gt;如果你不想使用 Virtual Hosts 应用，也可以使用以下方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在运行 zoffline 的 PC 上，在&lt;code&gt;storage&lt;/code&gt;目录下创建&lt;code&gt;fake-dns.txt&lt;/code&gt;文件&lt;/li&gt;
&lt;li&gt;在手机的 Wi-Fi 设置中，将&lt;strong&gt;DNS 1&lt;/strong&gt;设置为运行 zoffline 的 PC 的 IP 地址&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意&lt;/strong&gt;：如果你的路由器支持，也可以在路由器层面配置 DNS 记录，这样所有设备都可以自动使用&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;53-安装并打补丁-zwift&#34;&gt;5.3 安装并打补丁 Zwift&lt;/h3&gt;
&lt;h4 id=&#34;531-安装-zwift&#34;&gt;5.3.1 安装 Zwift&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;从 Google Play 商店安装或更新 Zwift&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;⚠️ 重要&lt;/strong&gt;：安装或更新后，&lt;strong&gt;不要立即启动&lt;/strong&gt; Zwift&lt;/li&gt;
&lt;li&gt;如果已经启动过 Zwift，需要先清除数据：
&lt;ul&gt;
&lt;li&gt;进入&lt;strong&gt;设置&lt;/strong&gt; &amp;gt; &lt;strong&gt;应用&lt;/strong&gt; &amp;gt; &lt;strong&gt;Zwift&lt;/strong&gt; &amp;gt; &lt;strong&gt;存储&lt;/strong&gt; &amp;gt; &lt;strong&gt;清除数据&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;或者卸载后重新安装&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;532-运行-zofflineobb-打补丁&#34;&gt;5.3.2 运行 ZofflineObb 打补丁&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;打开&lt;code&gt;ZofflineObb&lt;/code&gt;应用&lt;/li&gt;
&lt;li&gt;运行补丁程序（允许访问存储权限）&lt;/li&gt;
&lt;li&gt;等待补丁过程完成（通常需要 5-10 分钟）&lt;/li&gt;
&lt;li&gt;补丁完成后，应用会提示完成&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;原理说明&lt;/strong&gt;：ZofflineObb 会修改 Zwift 应用，使其接受 zoffline 服务器的自签名证书，这是连接离线服务器的关键步骤。&lt;/p&gt;
&lt;h3 id=&#34;54-运行-zwift&#34;&gt;5.4 运行 Zwift&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;确保 Virtual Hosts 按钮处于开启状态&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;启动 Zwift 应用&lt;/li&gt;
&lt;li&gt;使用任意邮箱和密码登录，或创建新用户（如果启用了多人游戏功能）&lt;/li&gt;
&lt;li&gt;Zwift 应该能够正常验证下载并运行&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;55-重要提示&#34;&gt;5.5 重要提示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;每次更新后需要重新打补丁&lt;/strong&gt;：无论是从 Google Play 更新 Zwift，还是重新安装，都需要重新运行 ZofflineObb 打补丁&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保持网络连接&lt;/strong&gt;：确保安卓设备与运行 zoffline 的 PC 在同一局域网内&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防火墙设置&lt;/strong&gt;：确保 PC 的防火墙允许 zoffline 的端口访问&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;六常见问题&#34;&gt;六、常见问题&lt;/h2&gt;
&lt;h3 id=&#34;61-卡在蓝色登录界面&#34;&gt;6.1 卡在蓝色登录界面&lt;/h3&gt;
&lt;p&gt;&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//2025/02/20/03f08f3fa8d593d33b38647a46f22341.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/20/03f08f3fa8d593d33b38647a46f22341.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;ol&gt;
&lt;li&gt;在任务管理器中关闭 Zwift 进程&lt;/li&gt;
&lt;li&gt;将输入法切换为&lt;strong&gt;微软英文输入法&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;重新启动 Zwift 并登录&lt;/li&gt;
&lt;/ol&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//2025/02/20/48a6263e5ffe475ec5753b13cffd2ec1.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/20/48a6263e5ffe475ec5753b13cffd2ec1.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;62-配置后无法更新-zwift&#34;&gt;6.2 配置后无法更新 Zwift&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;原因：&lt;/strong&gt; &lt;code&gt;configure_client.bat&lt;/code&gt;会修改 Hosts 文件，导致无法连接到 Zwift 服务器&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;关闭正在运行的&lt;code&gt;zoffline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;进入&lt;code&gt;zwiftoffline/zoffline-helper&lt;/code&gt;目录&lt;/li&gt;
&lt;li&gt;右键点击&lt;code&gt;disable_zoffline.bat&lt;/code&gt;，选择&lt;strong&gt;以管理员身份运行&lt;/strong&gt;，清除网络配置&lt;/li&gt;
&lt;li&gt;打开 Zwift 进行更新&lt;/li&gt;
&lt;li&gt;更新完成后，重新运行&lt;code&gt;configure_client.bat&lt;/code&gt;恢复配置&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;63-无法自动上传活动到-strava&#34;&gt;6.3 无法自动上传活动到 Strava&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;可能原因及解决方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;检查&lt;code&gt;strava_token.txt&lt;/code&gt;文件是否已正确放置在&lt;code&gt;storage/1&lt;/code&gt;目录下&lt;/li&gt;
&lt;li&gt;确认 Strava API 的&lt;code&gt;Client ID&lt;/code&gt;和&lt;code&gt;Client Secret&lt;/code&gt;配置正确&lt;/li&gt;
&lt;li&gt;检查网络连接，确保 zoffline 可以访问 Strava API&lt;/li&gt;
&lt;li&gt;查看 zoffline 命令行窗口的错误日志，根据具体错误信息进行排查&lt;/li&gt;
&lt;li&gt;检查在申请 API 时填写的授权回调域是否正确&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;64-安卓客户端无法登录&#34;&gt;6.4 安卓客户端无法登录&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;可能原因及解决方法：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于 Companion 应用：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;检查 Virtual Hosts 是否已正确启用&lt;/li&gt;
&lt;li&gt;确认 hosts 文件中的 IP 地址是否正确（应为运行 zoffline 的电脑 IP，而非 127.0.0.1）&lt;/li&gt;
&lt;li&gt;确保手机和电脑在同一局域网内&lt;/li&gt;
&lt;li&gt;检查防火墙设置，确保 zoffline 的端口未被阻止&lt;/li&gt;
&lt;li&gt;重新安装修改后的 APK 文件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;对于直接使用 Zwift 应用：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;确认已关闭 Private DNS 设置&lt;/li&gt;
&lt;li&gt;检查 Virtual Hosts 是否已正确启用，按钮必须处于开启状态&lt;/li&gt;
&lt;li&gt;确认 hosts.txt 文件包含所有三个域名映射（包括&lt;code&gt;cdn.zwift.com&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;检查是否在安装/更新 Zwift 后运行了 ZofflineObb 打补丁&lt;/li&gt;
&lt;li&gt;如果已启动过 Zwift，尝试清除应用数据后重新打补丁&lt;/li&gt;
&lt;li&gt;确认手机和运行 zoffline 的 PC 在同一局域网内&lt;/li&gt;
&lt;li&gt;检查 PC 防火墙设置，确保 zoffline 端口未被阻止&lt;/li&gt;
&lt;/ol&gt;
</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p><a href="https://github.com/zoffline/zwift-offline?tab=readme-ov-file">ZwiftOffline</a>是一个开源社区自制的 Zwift 的 server 端，可以在没有网络的情况下进行 Zwift 课程或路线训练，多平台支持。</p>
<p>本教程将详细介绍如何在 Windows 系统上安装和配置 Zwift 单机版，包括基础安装、账号配置、Strava 同步、训练课程上传以及移动端 Companion 配置等完整流程。</p>
<h2 id="一准备工作">一、准备工作</h2>
<h3 id="11-下载必要文件">1.1 下载必要文件</h3>
<ol>
<li>
<p><strong>ZwiftOffline 主程序</strong></p>
<ul>
<li>从<a href="https://github.com/zoffline/zwift-offline/releases/latest">GitHub Release 页面</a>下载最新的 zoffline 发布版（<code>.exe</code>文件）</li>
</ul>
</li>
<li>
<p><strong>配置辅助工具</strong></p>
<ul>
<li>从<a href="https://github.com/oldnapalm/zoffline-helper/releases/latest">GitHub Release</a>下载<code>zoffline-helper.zip</code>配置脚本文件</li>
</ul>
</li>
<li>
<p><strong>Zwift 官方客户端</strong></p>
<ul>
<li>前往<a href="https://www.zwift.com/">Zwift 官网</a>，拉到页面底部，下载并安装 Zwift 软件</li>
<li><strong>重要</strong>：记住安装路径，后续配置需要用到（例如：<code>C:\Program Files (x86)\Zwift</code>）</li>
</ul>
</li>
</ol>
<h3 id="12-创建运行目录">1.2 创建运行目录</h3>
<p>创建一个目录用于存放 ZwiftOffline 相关文件，例如新建<code>zwiftoffline</code>目录。</p>
<h2 id="二基础安装与配置">二、基础安装与配置</h2>
<h3 id="21-初始化-zwiftoffline">2.1 初始化 ZwiftOffline</h3>
<ol>
<li>将下载的<code>zoffline.exe</code>文件保存到<code>zwiftoffline</code>目录</li>
<li>运行<code>zoffline.exe</code>，程序会在同目录下自动创建<code>storage</code>目录（用于保存 Zwift 进度）</li>
<li>看到<code>storage</code>目录生成后，按<code>Ctrl+C</code>关闭 zoffline，稍后进行配置</li>
</ol>
<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//2025/02/10/ed838fae543dc8d607c231d89fc4888b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/10/ed838fae543dc8d607c231d89fc4888b.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="22-安装并更新-zwift-客户端">2.2 安装并更新 Zwift 客户端</h3>
<ol>
<li>运行刚安装的 Zwift 程序，客户端会开始更新游戏本体
<ul>
<li><strong>⚠️ 重要提示</strong>：必须在配置网络之前完成此步骤，因为后续配置会修改 hosts 文件，导致无法连接到 Zwift 服务器下载游戏本体</li>
</ul>
</li>
</ol>
<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//2025/02/09/734f7507b1e68958bba2ea2525216752.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/734f7507b1e68958bba2ea2525216752.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="23-配置网络连接">2.3 配置网络连接</h3>
<ol>
<li>将下载的<code>zoffline-helper.zip</code>解压到<code>zwiftoffline</code>目录</li>
<li>进入<code>zwiftoffline/zoffline-helper</code>目录</li>
<li>右键点击<code>configure_client.bat</code>，选择<strong>以管理员身份运行</strong></li>
<li>脚本会自动配置 zoffline，期间会弹窗让你选择 Zwift 安装目录（即之前记住的路径）</li>
</ol>
<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//2025/02/19/52b44adfe177bde733156c3d2faefe5e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/19/52b44adfe177bde733156c3d2faefe5e.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 start="5">
<li>配置完成后，会显示如下结果：</li>
</ol>
<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//2025/02/09/6769deefaa7c142c42411a43af345e8d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/6769deefaa7c142c42411a43af345e8d.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="24-启动并验证">2.4 启动并验证</h3>
<ol>
<li>进入<code>zwiftoffline</code>目录，双击运行<code>zoffline.exe</code>（文件名可能因版本而异，如<code>zoffline_1.0.140279.exe</code>）</li>
</ol>
<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//2025/02/09/029bd1af360095a4a279b52c822cd5c7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/029bd1af360095a4a279b52c822cd5c7.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 start="2">
<li>启动原版 Zwift 程序，检查登录界面
<ul>
<li><strong>成功标志</strong>：显示 ZwiftOffline 的登录页面（如下图）</li>
</ul>
</li>
</ol>
<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//2025/02/09/e577c5169d652b1ae201a337a0d292f8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/e577c5169d652b1ae201a337a0d292f8.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>
<ul>
<li><strong>失败标志</strong>：如果显示官方原版登录界面，说明配置失败</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//2025/02/19/d8c93c7c78d2f33555bed4a3be9d671f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/19/d8c93c7c78d2f33555bed4a3be9d671f.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 start="3">
<li><strong>⚠️ 重要提示</strong>：
<ul>
<li>如果显示的是原版登录页面，检查是否开启了代理，关闭代理后重试</li>
<li>将输入法切换为<strong>微软英文输入法</strong>，点击<code>Start Zwift</code>开始游戏</li>
<li>使用搜狗输入法等第三方输入法可能会卡在登录页面</li>
</ul>
</li>
</ol>
<h2 id="三高级配置">三、高级配置</h2>
<h3 id="31-同步原有账号信息">3.1 同步原有账号信息</h3>
<p>如果不配置此项，使用 Zwift offline 时会显示为新账号，而不是你原来的账号信息。</p>
<p><strong>配置步骤：</strong></p>
<ol>
<li>在登录页面点击设置按钮进入设置界面</li>
</ol>
<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//2025/02/09/ce3667b3f69bf3ab3fd81c191aee51f8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/ce3667b3f69bf3ab3fd81c191aee51f8.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 start="2">
<li>填写你的 Zwift 邮箱和密码，勾选所有选项，点击 Submit 提交</li>
</ol>
<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//2025/02/09/9cc77352a1f299516e1f76989bdc60c3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/9cc77352a1f299516e1f76989bdc60c3.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 start="3">
<li>配置完成后，再次登录 Zwift 时就会显示为原来的账号了</li>
</ol>
<h3 id="32-配置-strava-自动上传">3.2 配置 Strava 自动上传</h3>
<h4 id="321-申请-strava-api">3.2.1 申请 Strava API</h4>
<ol>
<li>登录<a href="https://www.strava.com/settings/api">Strava API 设置页面</a></li>
<li>创建新应用，填写以下信息：
<ul>
<li><strong>应用名称</strong>：自定义（如：ZwiftOffline）</li>
<li><strong>类别</strong>：其他</li>
<li><strong>授权回调域</strong>：<code>launcher.zwift.com</code></li>
<li><strong>网站</strong>：可留空或填写个人网站</li>
</ul>
</li>
</ol>
<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//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/d8024be9518e5a262538aa5d48c7fbb0.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 start="3">
<li>申请完成后，记录下<code>Client ID</code>和<code>Client Secret</code></li>
</ol>
<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//2025/02/09/a5024f9e2e150810251222315be11ac9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/a5024f9e2e150810251222315be11ac9.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>
<h4 id="322-在-zwiftoffline-中配置-strava">3.2.2 在 ZwiftOffline 中配置 Strava</h4>
<ol>
<li>在登录页面进入设置，填写<code>Client ID</code>和<code>Client Secret</code></li>
</ol>
<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//2025/02/09/29afa480ea0c6ceba03f89a14b30c987.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/29afa480ea0c6ceba03f89a14b30c987.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//2025/02/09/84b0932cd611d78a1497f82aafa86fb7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/84b0932cd611d78a1497f82aafa86fb7.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>点击<code>Submit</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//2025/02/09/2457689c2493d63ffa77e465948d3d84.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/09/2457689c2493d63ffa77e465948d3d84.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>
<h4 id="323-授权-strava-访问">3.2.3 授权 Strava 访问</h4>
<blockquote>
<p>这一步可以省略，如果通过上面的步骤没法上传，再尝试这个步骤手动配置</p>
</blockquote>
<ol>
<li>
<p>右键以管理员身份运行<code>zoffline-helper/disable_zoffline.bat</code>，清除 hosts 配置</p>
</li>
<li>
<p>在命令行中运行以下命令（替换<code>CLIENT_ID</code>和<code>CLIENT_SECRET</code>为你的实际值）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">zoffline-helper/strava_auth.exe --client-id CLIENT_ID --client-secret CLIENT_SECRET
</span></span></code></pre></div></li>
<li>
<p>浏览器会自动打开或手动访问<code>localhost:8000</code>进行授权</p>
</li>
</ol>
<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//2025/05/18/4b0dbbd3bd2dd82b1c6da8327e494ea3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/05/18/4b0dbbd3bd2dd82b1c6da8327e494ea3.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//2025/05/18/05c02cfd9cd98aaf1e198d2f99b58f5a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/05/18/05c02cfd9cd98aaf1e198d2f99b58f5a.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 start="4">
<li>授权完成后，将生成的<code>strava_token.txt</code>文件移动到<code>storage/1</code>目录下</li>
<li>右键以管理员身份运行<code>zoffline-helper/configure_client.bat</code>，重新配置 hosts</li>
</ol>
<h3 id="33-上传自定义训练课程">3.3 上传自定义训练课程</h3>
<p>如果你习惯在 Intervals ICU 等平台创建训练课程，可以手动上传到 Zwift 单机版。</p>
<p><strong>上传步骤：</strong></p>
<ol>
<li>
<p>将你的<code>.zwo</code>训练课程文件保存到以下目录：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Documents/Zwift/Workouts/1
</span></span></code></pre></div></li>
</ol>
<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//2025/02/15/f18d29b2d3e548bf2fe0878d54b2f45f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/15/f18d29b2d3e548bf2fe0878d54b2f45f.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 start="2">
<li><strong>⚠️ 注意</strong>：不是<code>zwiftoffline\storage\1\customworkouts</code>目录，如果放错位置会导致课程无法上传</li>
</ol>
<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//2025/02/15/edd5d0ab56c733e794d196838851dfe3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/15/edd5d0ab56c733e794d196838851dfe3.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="34-配置-garmin-connect">3.4 配置 Garmin Connect</h3>
<p>Garmin Connect 可以同步你的 Zwift 活动数据到 Garmin 设备。</p>
<h4 id="341-配置账号">3.4.1 配置账号</h4>
<ol>
<li>
<p><strong>PC 端配置</strong></p>
<ul>
<li>在 ZwiftOffline 登录窗口点击&quot;Settings - Garmin&quot;按钮</li>
<li>输入你的 Garmin Connect 账号和密码</li>
</ul>
</li>
<li>
<p><strong>安卓端配置</strong></p>
<ul>
<li>访问 <code>https://&lt;zoffline_ip&gt;/garmin/zoffline/</code></li>
<li>输入你的 Garmin Connect 账号和密码</li>
<li>将<code>&lt;zoffline_ip&gt;</code>替换为运行 zoffline 的电脑 IP 地址</li>
</ul>
</li>
</ol>
<h4 id="342-多因素认证mfa">3.4.2 多因素认证（MFA）</h4>
<p>如果你的账号启用了多因素认证，需要额外配置：</p>
<ol>
<li>
<p><strong>使用 Python 脚本（推荐）</strong></p>
<ul>
<li>运行<code>garmin_auth.py</code>脚本</li>
<li>脚本会在运行目录下生成<code>garth</code>文件夹</li>
<li>将<code>garth</code>文件夹移动到<code>storage/1</code>目录下</li>
</ul>
</li>
<li>
<p><strong>使用 Windows 可执行文件</strong></p>
<ul>
<li>如果未安装 Python，可以从<a href="https://github.com/oldnapalm/zoffline-helper/releases/latest">zoffline-helper Release 页面</a>下载<code>garmin_auth.exe</code></li>
<li>运行<code>garmin_auth.exe</code>，同样会生成<code>garth</code>文件夹</li>
<li>将<code>garth</code>文件夹移动到<code>storage/1</code>目录下</li>
</ul>
</li>
</ol>
<h3 id="35-配置-intervalsicu">3.5 配置 Intervals.icu</h3>
<p>Intervals.icu 是一个强大的训练分析平台，可以同步 Zwift 活动数据。</p>
<h4 id="351-获取-api-凭证">3.5.1 获取 API 凭证</h4>
<ol>
<li>登录 <a href="https://intervals.icu/settings">Intervals.icu 设置页面</a></li>
<li>进入&quot;Developer Settings&quot;（开发者设置）部分</li>
<li>复制以下信息：
<ul>
<li><strong>Athlete ID</strong>（运动员 ID）</li>
<li><strong>API Key</strong>（API 密钥）</li>
</ul>
</li>
</ol>
<h4 id="352-配置账号">3.5.2 配置账号</h4>
<ol>
<li>
<p><strong>PC 端配置</strong></p>
<ul>
<li>在 ZwiftOffline 登录窗口点击&quot;Settings - Intervals&quot;按钮</li>
<li>输入之前复制的 Athlete ID 和 API Key</li>
</ul>
</li>
<li>
<p><strong>安卓端配置</strong></p>
<ul>
<li>访问 <code>https://&lt;zoffline_ip&gt;/intervals/zoffline/</code></li>
<li>输入 Athlete ID 和 API Key</li>
<li>将<code>&lt;zoffline_ip&gt;</code>替换为运行 zoffline 的电脑 IP 地址</li>
</ul>
</li>
</ol>
<h3 id="36-启用多人游戏">3.6 启用多人游戏</h3>
<p>多人游戏功能允许多个用户同时连接到同一个 zoffline 服务器进行骑行。</p>
<h4 id="361-基本配置">3.6.1 基本配置</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>multiplayer.txt</code>文件（文件内容为空）</li>
</ol>
<h4 id="362-远程服务器配置">3.6.2 远程服务器配置</h4>
<p>如果 zoffline 运行在与 Zwift 客户端不同的 PC 上：</p>
<ol>
<li>在<code>storage</code>目录下创建<code>server-ip.txt</code>文件</li>
<li>文件内容填写运行 zoffline 的 PC 的 IP 地址</li>
<li><strong>端口要求</strong>：确保以下端口在运行 zoffline 的 PC 上开放：
<ul>
<li>TCP 端口：80, 443, 3025</li>
<li>UDP 端口：3024</li>
</ul>
</li>
</ol>
<h4 id="363-创建账号">3.6.3 创建账号</h4>
<ol>
<li>启动 Zwift，创建新账号</li>
<li><strong>⚠️ 注意</strong>：此账号仅存在于你的 zoffline 服务器上，与官方 Zwift 账号无关</li>
</ol>
<h4 id="364-启用密码重置功能可选">3.6.4 启用密码重置功能（可选）</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>gmail_credentials.txt</code>文件</li>
<li>文件内容格式：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&lt;gmail 账号&gt;
</span></span><span class="line"><span class="cl">&lt;应用密码&gt;
</span></span><span class="line"><span class="cl">&lt;恢复 URL 主机（可选）&gt;
</span></span></code></pre></div><ol start="3">
<li><strong>获取应用密码</strong>：
<ul>
<li>访问 <a href="https://security.google.com/settings/security/apppasswords">https://security.google.com/settings/security/apppasswords</a></li>
<li>创建应用密码，允许服务器登录</li>
<li>将生成的应用密码填入文件第二行</li>
</ul>
</li>
<li><strong>恢复 URL 主机</strong>（可选）：
<ul>
<li>第三行可以填写恢复 URL 的主机地址</li>
<li>如果不填写，将使用服务器 IP 地址作为默认值</li>
</ul>
</li>
</ol>
<h3 id="37-启用-ghosts幽灵">3.7 启用 Ghosts（幽灵）</h3>
<p>Ghosts 功能可以保存你之前的骑行记录，并在相同路线上显示为&quot;幽灵&quot;与你一起骑行。</p>
<h4 id="371-启用功能">3.7.1 启用功能</h4>
<ol>
<li>
<p><strong>PC 端</strong></p>
<ul>
<li>在 zoffline 启动器窗口中勾选&quot;Enable ghosts&quot;</li>
</ul>
</li>
<li>
<p><strong>安卓端</strong></p>
<ul>
<li>访问 <code>https://&lt;zoffline_ip&gt;/user/zoffline/</code></li>
<li>勾选&quot;Enable ghosts&quot;</li>
<li>点击&quot;Start Zwift&quot;保存设置</li>
</ul>
</li>
</ol>
<h4 id="372-使用说明">3.7.2 使用说明</h4>
<ol>
<li><strong>保存幽灵</strong>：当你保存活动时，幽灵会自动保存在<code>storage/&lt;player_id&gt;/ghosts/&lt;world&gt;/&lt;route&gt;</code>目录下</li>
<li><strong>加载幽灵</strong>：下次骑行相同路线时，幽灵会自动加载</li>
<li><strong>重新编组</strong>：在聊天中输入<code>.regroup</code>命令可以重新编组幽灵</li>
</ol>
<h4 id="373-自定义装备">3.7.3 自定义装备</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>ghost_profile.txt</code>文件</li>
<li>可以使用<code>find_equip.py</code>脚本来自动填充此文件</li>
</ol>
<h3 id="38-启用-bots机器人">3.8 启用 Bots（机器人）</h3>
<p>Bots 功能可以将保存的幽灵转换为持续骑行的机器人，无论你选择什么路线，它们都会继续骑行。</p>
<h4 id="381-启用功能">3.8.1 启用功能</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>enable_bots.txt</code>文件</li>
<li><strong>可选</strong>：文件内容可以包含一个倍数（如：<code>2</code>表示双倍机器人数量）
<ul>
<li><strong>⚠️ 警告</strong>：如果机器人数量过多，可能导致性能问题或功能失效</li>
</ul>
</li>
</ol>
<h4 id="382-控制命令">3.8.2 控制命令</h4>
<p>在聊天中使用以下命令控制机器人：</p>
<ul>
<li><code>.group</code>：编组机器人</li>
<li><code>.groupall</code>：编组所有机器人（包括使用倍数时的重复机器人）</li>
<li><code>.autogroup</code>：自动编组（当你改变路线时）</li>
<li><code>.autogroupall</code>：自动编组所有机器人</li>
<li><code>.stopautogroup</code>：停止自动编组</li>
<li><code>.disperse</code>：随机化机器人位置</li>
</ul>
<h4 id="383-自定义机器人">3.8.3 自定义机器人</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>bot.txt</code>文件</li>
<li>可以自定义机器人的名称、国籍和装备</li>
<li>可以使用<code>get_pro_names.py</code>脚本来自动填充此文件</li>
<li><strong>随机机器人</strong>：如果需要随机机器人，可以参考<a href="https://github.com/oldnapalm/zoffline-bots">zoffline-bots 仓库</a></li>
</ol>
<h3 id="39-配置-robopacers机器人配速员">3.9 配置 RoboPacers（机器人配速员）</h3>
<p>RoboPacers 是使用功率模拟器保存的幽灵，可以创建完美的循环配速员。</p>
<h4 id="391-获取-robopacers">3.9.1 获取 RoboPacers</h4>
<ol>
<li>可以从<a href="https://github.com/oldnapalm/zoffline-bots">zoffline-bots 仓库</a>下载预制的 RoboPacers</li>
</ol>
<h4 id="392-创建要求">3.9.2 创建要求</h4>
<p>要创建可用的 RoboPacer，需要满足以下条件：</p>
<ol>
<li><strong>更新频率</strong>：必须使用 1 秒的更新频率录制（默认是 3 秒）</li>
<li><strong>循环要求</strong>：活动必须从同一位置和速度开始和结束，否则机器人无法平滑循环</li>
<li><strong>配置文件要求</strong>：
<ul>
<li>必须包含唯一的玩家 ID</li>
<li>必须包含路线 ID</li>
<li>这样当你加入机器人时，会在交叉路口走相同的转弯</li>
</ul>
</li>
</ol>
<h4 id="393-编辑工具">3.9.3 编辑工具</h4>
<ol>
<li><strong>bot_editor.py 脚本</strong>：可以用于修改以下文件：
<ul>
<li><code>profile.bin</code>：设置名称、玩家 ID 和路线 ID</li>
<li><code>route.bin</code>：裁剪多余的点以创建完美循环</li>
</ul>
</li>
</ol>
<h4 id="394-创建动态-robopacer">3.9.4 创建动态 RoboPacer</h4>
<p>要创建动态 RoboPacer（在上坡时增加功率，下坡时减少功率）：</p>
<ol>
<li>使用 <a href="https://github.com/oldnapalm/zwift-offline/blob/master/standalone_power.py">standalone_power.py</a> 脚本</li>
<li><strong>硬件要求</strong>：
<ul>
<li>2 个 ANT 适配器</li>
<li><a href="https://github.com/mch/python-ant">python-ant</a></li>
<li><a href="https://github.com/oldnapalm/zwift-offline/blob/master/PowerMeterTx.py">PowerMeterTx.py</a></li>
</ul>
</li>
</ol>
<h3 id="310-使用-bookmarks书签">3.10 使用 Bookmarks（书签）</h3>
<p>书签功能可以保存你的位置，方便快速跳转到特定地点。</p>
<h4 id="3101-自动保存书签">3.10.1 自动保存书签</h4>
<p>当你完成活动时，你的最后位置会自动保存为书签。</p>
<h4 id="3102-手动保存书签">3.10.2 手动保存书签</h4>
<p>在聊天中使用以下命令保存书签：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">.bookmark &lt;名称&gt;
</span></span></code></pre></div><p>例如：<code>.bookmark 起点</code></p>
<h4 id="3103-使用书签">3.10.3 使用书签</h4>
<ol>
<li>
<p><strong>从书签开始新活动</strong>：</p>
<ul>
<li>在主页屏幕选择&quot;Join a Zwifter&quot;</li>
<li>选择你想要的书签</li>
</ul>
</li>
<li>
<p><strong>传送到书签</strong>：</p>
<ul>
<li>使用操作栏上的传送图标</li>
<li>选择目标书签进行传送</li>
</ul>
</li>
</ol>
<h3 id="311-启用历史排行榜">3.11 启用历史排行榜</h3>
<p>历史排行榜功能可以覆盖 60 分钟的实时结果和 90 天的个人记录，显示所有时间的最佳成绩。</p>
<h4 id="3111-启用功能">3.11.1 启用功能</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>all_time_leaderboards.txt</code>文件（文件内容可以为空）</li>
</ol>
<h4 id="3112-功能说明">3.11.2 功能说明</h4>
<ul>
<li><strong>排行榜</strong>：显示所有时间的最佳成绩，而不仅仅是最近 60 分钟或 90 天的记录</li>
<li><strong>荣誉衫</strong>：仍然有效期为 60 分钟，但只有在创造新的历史记录时才会授予</li>
</ul>
<h3 id="312-解锁装备">3.12 解锁装备</h3>
<p>可以解锁特殊装备或所有装备。</p>
<h4 id="3121-解锁特殊装备">3.12.1 解锁特殊装备</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>unlock_entitlements.txt</code>文件（文件内容可以为空）</li>
<li>这将解锁需要特殊权限的装备</li>
</ol>
<h4 id="3122-解锁所有装备">3.12.2 解锁所有装备</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>unlock_all_equipment.txt</code>文件（文件内容可以为空）</li>
<li>这将解锁所有可用装备</li>
</ol>
<h3 id="313-配置-cdn-代理">3.13 配置 CDN 代理</h3>
<p>CDN 代理功能可以从 Zwift 官方服务器获取地图时间表和更新文件。</p>
<h4 id="3131-启用-cdn-代理">3.13.1 启用 CDN 代理</h4>
<ol>
<li>在<code>storage</code>目录下创建<code>cdn-proxy.txt</code>文件（文件内容可以为空）</li>
<li><strong>⚠️ 限制</strong>：此功能只能在 zoffline 运行在与 Zwift 客户端不同的机器上时使用</li>
</ol>
<h4 id="3132-禁用代理">3.13.2 禁用代理</h4>
<p>默认情况下，zoffline 会尝试使用 Google 公共 DNS 来解析 Zwift 主机名，即使 zoffline 与 Zwift 客户端运行在同一台机器上也能工作。</p>
<ol>
<li>如果要禁用此代理功能，在<code>storage</code>目录下创建<code>disable_proxy.txt</code>文件</li>
</ol>
<h4 id="3133-从-zoffline-提供更新文件">3.13.3 从 zoffline 提供更新文件</h4>
<ol>
<li>运行<code>get_gameassets.py</code>脚本下载游戏文件</li>
<li>这样 zoffline 可以直接提供更新文件，而不需要从 Zwift 服务器获取</li>
</ol>
<h2 id="四移动端-companion-配置">四、移动端 Companion 配置</h2>
<p>Zwift Companion 是配套的移动端社交软件，可用于与骑友互动、配对设备、点赞、使用道具等功能。使用离线版 Zwift 需要重新打包 Companion 应用。</p>
<h3 id="41-准备工作">4.1 准备工作</h3>
<p>官方原版 Companion 会对网络连接和证书做严格验证，需要对其进行&quot;打补丁并重签名&quot;才能连接到本地 zoffline 服务器。</p>
<h3 id="42-替换-apk-证书并重新签名">4.2 替换 APK 证书并重新签名</h3>
<h4 id="421-安装-apk-mitm">4.2.1 安装 apk-mitm</h4>
<ol>
<li>安装 Node.js（如果尚未安装）</li>
<li>使用以下命令全局安装 apk-mitm：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install -g apk-mitm
</span></span></code></pre></div><ol start="3">
<li>其他系统可参考<a href="https://github.com/shroudedcode/apk-mitm">apk-mitm 项目 README</a>自行安装</li>
</ol>
<h4 id="422-修改-apktool-配置">4.2.2 修改 apktool 配置</h4>
<ol>
<li>使用<code>npm root -g</code>查找 apk-mitm 的实际安装路径</li>
<li>打开<code>apk-mitm/dist/tools/apktool.js</code>文件</li>
<li>修改<code>decode</code>方法，添加<code>-resm</code>和<code>dummy</code>参数：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">    <span class="nx">decode</span><span class="p">(</span><span class="nx">inputPath</span><span class="p">,</span> <span class="nx">outputPath</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">run</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;decode&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;-resm&#39;</span><span class="p">,</span> <span class="c1">// add this
</span></span></span><span class="line"><span class="cl">            <span class="s1">&#39;dummy&#39;</span><span class="p">,</span> <span class="c1">// add this
</span></span></span><span class="line"><span class="cl">            <span class="nx">inputPath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;--output&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">outputPath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;--frame-path&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">frameworkPath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span> <span class="s1">&#39;decoding&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><h4 id="423-生成修改后的-apk">4.2.3 生成修改后的 APK</h4>
<ol>
<li>
<p>从<a href="https://github.com/zoffline/zwift-offline">Zwift Offline 仓库</a>下载以下文件：</p>
<ul>
<li><code>ssl/cert-zwift-com.pem</code>（证书文件）</li>
<li><code>zwift-offline-companion.apk</code>（原始 APK 文件）</li>
</ul>
</li>
<li>
<p>将这两个文件复制到同一目录（如：<code>C:\Users\Administrator\Desktop\zwift-offline-companion</code>）</p>
</li>
<li>
<p>在该目录下运行以下命令：</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apk-mitm --certificate cert-zwift-com.pem zwift-offline-companion.apk
</span></span></code></pre></div><ol start="4">
<li>命令执行完成后，会生成<code>zwift-offline-companion-patched.apk</code>文件，将其拷贝到手机安装即可</li>
</ol>
<h3 id="43-配置域名重定向">4.3 配置域名重定向</h3>
<h4 id="431-安装-virtual-hosts">4.3.1 安装 Virtual Hosts</h4>
<ol>
<li>下载<a href="https://github.com/x-falcon/Virtual-Hosts/releases/latest">virtual hosts</a>软件</li>
<li>安装到手机中</li>
</ol>
<h4 id="432-创建-hosts-文件">4.3.2 创建 hosts 文件</h4>
<ol>
<li>创建<code>hosts.txt</code>文件，内容如下（将<code>&lt;zoffline ip&gt;</code>替换为你运行 zoffline 的电脑 IP 地址）：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;zoffline ip&gt; us-or-rly101.zwift.com
</span></span><span class="line"><span class="cl">&lt;zoffline ip&gt; secure.zwift.com
</span></span></code></pre></div><ol start="2">
<li><strong>⚠️ 重要提示</strong>：如果在 PC 上配置过 hosts 文件，可能包含以下映射：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">127.0.0.1 cdn.zwift.com
</span></span></code></pre></div><p><strong>此映射不要添加到手机端的 hosts 文件中！</strong></p>
<ol start="3">
<li>将<code>hosts.txt</code>文件复制到手机，放在便于访问的位置</li>
</ol>
<h4 id="433-启用域名重定向">4.3.3 启用域名重定向</h4>
<ol>
<li>打开 Virtual Hosts 软件</li>
<li>选择之前创建的<code>hosts.txt</code>文件</li>
<li>启用域名重定向功能</li>
</ol>
<h3 id="44-使用-companion">4.4 使用 Companion</h3>
<ol>
<li>使用离线版的账号登录 Companion，即可正常使用</li>
<li><strong>验证连接</strong>：在打开和登录 Companion 时，观察 PC 端运行的 Zwift offline 命令行窗口是否有日志输出
<ul>
<li>如果有日志输出，说明连接正常</li>
<li>如果没有任何输出，说明请求未成功转发，需要检查 IP 地址配置是否正确</li>
</ul>
</li>
</ol>
<h2 id="五安卓设备直接使用-zwift-offline">五、安卓设备直接使用 Zwift Offline</h2>
<p>除了在 PC 上使用 Zwift Offline，你也可以直接在安卓设备上运行 Zwift 并连接到 zoffline 服务器。这种方式不需要 root 权限，但需要在每次安装或更新 Zwift 后重新打补丁。</p>
<h3 id="51-安装必要应用">5.1 安装必要应用</h3>
<h4 id="511-下载并安装-zofflineobb">5.1.1 下载并安装 ZofflineObb</h4>
<ol>
<li>从<a href="https://github.com/Argon2000/ZofflineObbAndroid/releases/latest">GitHub Release 页面</a>下载<code>ZofflineObb.apk</code></li>
<li>安装到安卓设备上（需要允许安装未知来源应用）</li>
</ol>
<h4 id="512-下载并安装-virtual-hosts">5.1.2 下载并安装 Virtual Hosts</h4>
<ol>
<li>从<a href="https://github.com/x-falcon/Virtual-Hosts/releases/latest">GitHub Release 页面</a>下载<code>app-Github-release.apk</code></li>
<li>安装到安卓设备上</li>
</ol>
<h3 id="52-配置域名重定向">5.2 配置域名重定向</h3>
<h4 id="521-创建-hoststxt-文件">5.2.1 创建 hosts.txt 文件</h4>
<ol>
<li>
<p>创建<code>hosts.txt</code>文件，可以使用文本编辑器应用或在线工具（如<a href="https://passwordsgenerator.net/text-editor/">在线文本编辑器</a>）</p>
</li>
<li>
<p>文件内容如下（将<code>&lt;zoffline ip&gt;</code>替换为运行 zoffline 的电脑 IP 地址）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;zoffline ip&gt; us-or-rly101.zwift.com
</span></span><span class="line"><span class="cl">&lt;zoffline ip&gt; secure.zwift.com
</span></span><span class="line"><span class="cl">&lt;zoffline ip&gt; cdn.zwift.com
</span></span></code></pre></div><p><strong>⚠️ 注意</strong>：与 Companion 配置不同，安卓设备直接使用需要包含<code>cdn.zwift.com</code>的映射</p>
</li>
<li>
<p>将<code>hosts.txt</code>文件保存到手机，放在便于访问的位置</p>
</li>
</ol>
<h4 id="522-关闭-private-dns">5.2.2 关闭 Private DNS</h4>
<ol>
<li>进入安卓设备的<strong>设置</strong> &gt; <strong>网络和互联网</strong> &gt; <strong>高级</strong> &gt; <strong>Private DNS</strong></li>
<li>选择<strong>关闭</strong>或<strong>自动</strong></li>
</ol>
<h4 id="523-配置-virtual-hosts">5.2.3 配置 Virtual Hosts</h4>
<ol>
<li>打开 Virtual Hosts 应用</li>
<li>选择之前创建的<code>hosts.txt</code>文件</li>
<li>启用域名重定向功能（确保按钮处于开启状态）</li>
</ol>
<h4 id="524-替代方案使用-dns-配置">5.2.4 替代方案：使用 DNS 配置</h4>
<p>如果你不想使用 Virtual Hosts 应用，也可以使用以下方法：</p>
<ol>
<li>在运行 zoffline 的 PC 上，在<code>storage</code>目录下创建<code>fake-dns.txt</code>文件</li>
<li>在手机的 Wi-Fi 设置中，将<strong>DNS 1</strong>设置为运行 zoffline 的 PC 的 IP 地址</li>
<li><strong>注意</strong>：如果你的路由器支持，也可以在路由器层面配置 DNS 记录，这样所有设备都可以自动使用</li>
</ol>
<h3 id="53-安装并打补丁-zwift">5.3 安装并打补丁 Zwift</h3>
<h4 id="531-安装-zwift">5.3.1 安装 Zwift</h4>
<ol>
<li>从 Google Play 商店安装或更新 Zwift</li>
<li><strong>⚠️ 重要</strong>：安装或更新后，<strong>不要立即启动</strong> Zwift</li>
<li>如果已经启动过 Zwift，需要先清除数据：
<ul>
<li>进入<strong>设置</strong> &gt; <strong>应用</strong> &gt; <strong>Zwift</strong> &gt; <strong>存储</strong> &gt; <strong>清除数据</strong></li>
<li>或者卸载后重新安装</li>
</ul>
</li>
</ol>
<h4 id="532-运行-zofflineobb-打补丁">5.3.2 运行 ZofflineObb 打补丁</h4>
<ol>
<li>打开<code>ZofflineObb</code>应用</li>
<li>运行补丁程序（允许访问存储权限）</li>
<li>等待补丁过程完成（通常需要 5-10 分钟）</li>
<li>补丁完成后，应用会提示完成</li>
</ol>
<p><strong>原理说明</strong>：ZofflineObb 会修改 Zwift 应用，使其接受 zoffline 服务器的自签名证书，这是连接离线服务器的关键步骤。</p>
<h3 id="54-运行-zwift">5.4 运行 Zwift</h3>
<ol>
<li><strong>确保 Virtual Hosts 按钮处于开启状态</strong></li>
<li>启动 Zwift 应用</li>
<li>使用任意邮箱和密码登录，或创建新用户（如果启用了多人游戏功能）</li>
<li>Zwift 应该能够正常验证下载并运行</li>
</ol>
<h3 id="55-重要提示">5.5 重要提示</h3>
<ul>
<li><strong>每次更新后需要重新打补丁</strong>：无论是从 Google Play 更新 Zwift，还是重新安装，都需要重新运行 ZofflineObb 打补丁</li>
<li><strong>保持网络连接</strong>：确保安卓设备与运行 zoffline 的 PC 在同一局域网内</li>
<li><strong>防火墙设置</strong>：确保 PC 的防火墙允许 zoffline 的端口访问</li>
</ul>
<h2 id="六常见问题">六、常见问题</h2>
<h3 id="61-卡在蓝色登录界面">6.1 卡在蓝色登录界面</h3>
<p><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//2025/02/20/03f08f3fa8d593d33b38647a46f22341.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/20/03f08f3fa8d593d33b38647a46f22341.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>
<ol>
<li>在任务管理器中关闭 Zwift 进程</li>
<li>将输入法切换为<strong>微软英文输入法</strong></li>
<li>重新启动 Zwift 并登录</li>
</ol>
<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//2025/02/20/48a6263e5ffe475ec5753b13cffd2ec1.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2025/02/20/48a6263e5ffe475ec5753b13cffd2ec1.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="62-配置后无法更新-zwift">6.2 配置后无法更新 Zwift</h3>
<p><strong>原因：</strong> <code>configure_client.bat</code>会修改 Hosts 文件，导致无法连接到 Zwift 服务器</p>
<p><strong>解决方法：</strong></p>
<ol>
<li>关闭正在运行的<code>zoffline</code></li>
<li>进入<code>zwiftoffline/zoffline-helper</code>目录</li>
<li>右键点击<code>disable_zoffline.bat</code>，选择<strong>以管理员身份运行</strong>，清除网络配置</li>
<li>打开 Zwift 进行更新</li>
<li>更新完成后，重新运行<code>configure_client.bat</code>恢复配置</li>
</ol>
<h3 id="63-无法自动上传活动到-strava">6.3 无法自动上传活动到 Strava</h3>
<p><strong>可能原因及解决方法：</strong></p>
<ol>
<li>检查<code>strava_token.txt</code>文件是否已正确放置在<code>storage/1</code>目录下</li>
<li>确认 Strava API 的<code>Client ID</code>和<code>Client Secret</code>配置正确</li>
<li>检查网络连接，确保 zoffline 可以访问 Strava API</li>
<li>查看 zoffline 命令行窗口的错误日志，根据具体错误信息进行排查</li>
<li>检查在申请 API 时填写的授权回调域是否正确</li>
</ol>
<h3 id="64-安卓客户端无法登录">6.4 安卓客户端无法登录</h3>
<p><strong>可能原因及解决方法：</strong></p>
<p><strong>对于 Companion 应用：</strong></p>
<ol>
<li>检查 Virtual Hosts 是否已正确启用</li>
<li>确认 hosts 文件中的 IP 地址是否正确（应为运行 zoffline 的电脑 IP，而非 127.0.0.1）</li>
<li>确保手机和电脑在同一局域网内</li>
<li>检查防火墙设置，确保 zoffline 的端口未被阻止</li>
<li>重新安装修改后的 APK 文件</li>
</ol>
<p><strong>对于直接使用 Zwift 应用：</strong></p>
<ol>
<li>确认已关闭 Private DNS 设置</li>
<li>检查 Virtual Hosts 是否已正确启用，按钮必须处于开启状态</li>
<li>确认 hosts.txt 文件包含所有三个域名映射（包括<code>cdn.zwift.com</code>）</li>
<li>检查是否在安装/更新 Zwift 后运行了 ZofflineObb 打补丁</li>
<li>如果已启动过 Zwift，尝试清除应用数据后重新打补丁</li>
<li>确认手机和运行 zoffline 的 PC 在同一局域网内</li>
<li>检查 PC 防火墙设置，确保 zoffline 端口未被阻止</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>Per-CPU 变量</title>
      <link>https://lifeislife.cn/posts/per-cpu-%E5%8F%98%E9%87%8F/</link>
      <pubDate>Sat, 04 Jan 2025 10:34:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/per-cpu-%E5%8F%98%E9%87%8F/</guid>
      <description>&lt;h3 id=&#34;引入原因&#34;&gt;引入原因&lt;/h3&gt;
&lt;p&gt;Per-CPU 变量是 Linux 内核中的一个重要概念，主要解决以下问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;锁竞争问题&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 SMP 系统中，如果多个 CPU 同时访问同一个变量，需要使用锁来保证数据一致性&lt;/li&gt;
&lt;li&gt;这种锁机制会导致性能下降，特别是在高并发场景下&lt;/li&gt;
&lt;li&gt;Per-CPU 变量为每个 CPU 核心分配独立的变量副本，无需加锁即可安全访问&lt;/li&gt;
&lt;li&gt;需要注意的一点是，Per-CPU 变量是独立的变量，每个 CPU 分别有一个自己的变量，这些变量不是互相同步的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;缓存效率&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传统共享变量会导致频繁的缓存失效（cache invalidation）&lt;/li&gt;
&lt;li&gt;Per-CPU 变量位于各自 CPU 的本地缓存中，提高缓存命中率&lt;/li&gt;
&lt;li&gt;减少了 CPU 间的缓存同步开销&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;NUMA 友好&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 NUMA 架构中，访问远端内存的开销很大&lt;/li&gt;
&lt;li&gt;Per-CPU 变量确保数据位于本地节点，减少跨 NUMA 节点访问&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这时候你可能会问，为什么不直接定义 16 个变量分别用于不同的 CPU？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;代码冗余&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果手动为每个 CPU 定义变量，代码会变得冗长且难以维护。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如，对于 128 个 CPU 的系统，可能需要定义 128 个变量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;var_cpu0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;var_cpu1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;var_cpu2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;var_cpu127&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不能动态支持多 CPU&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在支持 CPU 热插拔的系统中，CPU 的数量可能动态变化。&lt;/li&gt;
&lt;li&gt;手动定义变量无法适应这种动态变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;访问效率低&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;访问特定 CPU 的变量时，需要手动计算偏移量或使用条件语句，效率较低。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;get_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;var_cpu0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;var_cpu1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缓存局部性问题&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手动定义的变量可能位于同一缓存行中，导致 &lt;strong&gt;缓存行共享（False Sharing）&lt;/strong&gt;，降低性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Per-CPU 变量的优势&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;代码简洁&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Per-CPU 变量通过内核提供的机制自动为每个 CPU 分配变量，代码更简洁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;my_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;动态适应 CPU 数量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Per-CPU 变量支持动态 CPU 数量，能够自动适应 CPU 热插拔。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如，动态分配 Per-CPU 变量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;__percpu&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_var&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;alloc_percpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;高效访问&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Per-CPU 变量通过内核提供的宏（如 &lt;code&gt;this_cpu_ptr&lt;/code&gt; 和 &lt;code&gt;per_cpu_ptr&lt;/code&gt;）高效访问当前或指定 CPU 的变量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ptr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;this_cpu_ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 访问当前 CPU 的变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ptr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;per_cpu_ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 访问指定 CPU 的变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缓存优化&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Per-CPU 变量会自动对齐到缓存行，避免缓存行共享问题。&lt;/li&gt;
&lt;li&gt;例如，内核会确保每个 CPU 的变量位于不同的缓存行中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;静态声明和定义&#34;&gt;静态声明和定义&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 1. 定义 Per-CPU 变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;                    &lt;span class=&#34;c1&#34;&gt;// 定义并初始化为 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;           &lt;span class=&#34;c1&#34;&gt;// 定义并指定初值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 2. 声明外部 Per-CPU 变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DECLARE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;                  &lt;span class=&#34;c1&#34;&gt;// 声明在其他地方定义的变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 3. 定义 Per-CPU 数组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;             &lt;span class=&#34;c1&#34;&gt;// 定义数组类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 4. 定义只读 Per-CPU 变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU_READ_MOSTLY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// 主要用于只读数据，优化缓存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;内部实现机制&#34;&gt;内部实现机制&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define DEFINE_PER_CPU(type, name) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        DEFINE_PER_CPU_SECTION(type, name, &amp;#34;&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;__attribute__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;.data..percpu&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;per_cpu_n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define DEFINE_PER_CPU_SECTION(type, name, sec) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    __PCPU_ATTRS(sec) __typeof__(type) name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define __PCPU_ATTRS(sec)                        \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    __percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))    
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define PER_CPU_BASE_SECTION &amp;#34;.data..percpu&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 在编译时定义 Per-CPU 变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;my_counter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 展开后实际上是
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;__attribute__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;.data..percpu&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;my_counter&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里为什么要引入 section 机制？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于 kernel 中的普通变量，经过了编译和链接后，会被放置到.data 或者.bss 段，系统在初始化的时候会准备好一切（例如 clear bss），由于 per CPU 变量的特殊性，内核将这些变量放置到了其他的 section，位于 kernel address space 中&lt;code&gt;__per_cpu_start&lt;/code&gt;和&lt;code&gt;__per_cpu_end&lt;/code&gt;之间，我们称之 Per-CPU 变量的原始变量。
摘自：&lt;a href=&#34;http://www.wowotech.net/kernel_synchronization/per-cpu.html&#34;&gt;Linux 内核同步机制之（二）：Per-CPU 变量&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Section 机制允许编译器将特定的数据放在目标文件的特定区域&lt;/li&gt;
&lt;li&gt;对于 Per-CPU 变量，Linux 使用 .data..percpu section 将所有 Per-CPU 变量集中存放&lt;/li&gt;
&lt;li&gt;这种集中存放便于运行时进行整体复制和管理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Per-CPU 变量如何建立副本的？&lt;/p&gt;
&lt;p&gt;在内核初始化阶段会调用&lt;code&gt;setup_per_cpu_areas&lt;/code&gt;函数，为每个 CPU 设置 Per-CPU 变量的偏移地址。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;__init&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setup_per_cpu_areas&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;delta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pcpu_base_addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;__per_cpu_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;for_each_possible_cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;__per_cpu_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pcpu_unit_offsets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;__per_cpu_offset&lt;/code&gt; 是一个全局数组，用于存储每个 CPU 的 Per-CPU 变量的偏移地址。&lt;/li&gt;
&lt;li&gt;在初始化时，内核会为每个 CPU 计算其 Per-CPU 变量的基地址，并将其存储在 &lt;code&gt;__per_cpu_offset[cpu]&lt;/code&gt; 中。&lt;/li&gt;
&lt;li&gt;这个偏移地址是相对于 Per-CPU 变量的起始地址（&lt;code&gt;__per_cpu_start&lt;/code&gt;）的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 CPU 初始化时，内核会使用调用&lt;code&gt;set_my_cpu_offset&lt;/code&gt;函数设置每个 CPU 静态 Per-CPU 变量的偏移地址。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define per_cpu_offset(x) (__per_cpu_offset(x))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;notrace&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cpu_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifndef CONFIG_CPU_V7M
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;smp_processor_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stack&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stk&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stacks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NR_CPUS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nf&#34;&gt;pr_crit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;CPU%u: bad primary CPU number&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nf&#34;&gt;BUG&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;	 * This only works on resume and secondary cores. For booting on the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;	 * boot cpu, smp_prepare_boot_cpu is called after percpu area setup.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;	 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nf&#34;&gt;set_my_cpu_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;per_cpu_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nf&#34;&gt;cpu_proc_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在&lt;code&gt;set_my_cpu_offset&lt;/code&gt;函数中，根据&lt;code&gt;ARM64_HAS_VIRT_HOST_EXTN&lt;/code&gt;的值，选择将偏移地址写入&lt;code&gt;tpidr_el1&lt;/code&gt;或者&lt;code&gt;tpidr_el2&lt;/code&gt;寄存器。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* arch/arm64/include/asm/percpu.h */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;set_my_cpu_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;volatile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ALTERNATIVE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;msr tpidr_el1, %0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;s&#34;&gt;&amp;#34;msr tpidr_el2, %0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;n&#34;&gt;ARM64_HAS_VIRT_HOST_EXTN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;::&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在访问时使用&lt;code&gt;this_cpu_ptr&lt;/code&gt;宏，展开后可以发现相当于 percpu 变量指针 ptr 加上&lt;code&gt;__my_cpu_offset&lt;/code&gt;。&lt;code&gt;__my_cpu_offset&lt;/code&gt;宏即是从当前cpu的&lt;code&gt;tpidr_el1&lt;/code&gt;、&lt;code&gt;tpidr_el2&lt;/code&gt;寄存器中取出此前设置的&lt;code&gt;__per_cpu_offset[cpu]&lt;/code&gt;值。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define this_cpu_ptr(ptr)    raw_cpu_ptr(ptr)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define raw_cpu_ptr(ptr)                        \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;({                                    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    __verify_pcpu_ptr(ptr);                        \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    arch_raw_cpu_ptr(ptr);                        \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define arch_raw_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, __my_cpu_offset)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__my_cpu_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * We want to allow caching the value, so avoid using volatile and
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * instead use a fake stack read to hazard against barrier().
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ALTERNATIVE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;mrs %0, tpidr_el1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s&#34;&gt;&amp;#34;mrs %0, tpidr_el2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;ARM64_HAS_VIRT_HOST_EXTN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;=r&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s&#34;&gt;&amp;#34;Q&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_stack_pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;使用方式&#34;&gt;使用方式&lt;/h3&gt;
&lt;p&gt;Per-CPU 变量的常用操作方式：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;静态定义&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 定义 Per-CPU 整型变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;my_percpu_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 定义 Per-CPU 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;DEFINE_PER_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;my_struct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;my_percpu_struct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;访问变量&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取本地 CPU 的变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu_var&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_cpu_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_percpu_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;put_cpu_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_percpu_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 必须配对使用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 访问指定 CPU 的变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;var_cpu0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;per_cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_percpu_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 遍历所有 CPU 的变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;for_each_possible_cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;per_cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_percpu_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 处理 value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h3 id="引入原因">引入原因</h3>
<p>Per-CPU 变量是 Linux 内核中的一个重要概念，主要解决以下问题：</p>
<ol>
<li>
<p><strong>锁竞争问题</strong></p>
<ul>
<li>在 SMP 系统中，如果多个 CPU 同时访问同一个变量，需要使用锁来保证数据一致性</li>
<li>这种锁机制会导致性能下降，特别是在高并发场景下</li>
<li>Per-CPU 变量为每个 CPU 核心分配独立的变量副本，无需加锁即可安全访问</li>
<li>需要注意的一点是，Per-CPU 变量是独立的变量，每个 CPU 分别有一个自己的变量，这些变量不是互相同步的。</li>
</ul>
</li>
<li>
<p><strong>缓存效率</strong></p>
<ul>
<li>传统共享变量会导致频繁的缓存失效（cache invalidation）</li>
<li>Per-CPU 变量位于各自 CPU 的本地缓存中，提高缓存命中率</li>
<li>减少了 CPU 间的缓存同步开销</li>
</ul>
</li>
<li>
<p><strong>NUMA 友好</strong></p>
<ul>
<li>在 NUMA 架构中，访问远端内存的开销很大</li>
<li>Per-CPU 变量确保数据位于本地节点，减少跨 NUMA 节点访问</li>
</ul>
</li>
</ol>
<p>这时候你可能会问，为什么不直接定义 16 个变量分别用于不同的 CPU？</p>
<ol>
<li>
<p>代码冗余</p>
<ul>
<li>
<p>如果手动为每个 CPU 定义变量，代码会变得冗长且难以维护。</p>
</li>
<li>
<p>例如，对于 128 个 CPU 的系统，可能需要定义 128 个变量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">var_cpu0</span><span class="p">,</span> <span class="n">var_cpu1</span><span class="p">,</span> <span class="n">var_cpu2</span><span class="p">,</span> <span class="p">...,</span> <span class="n">var_cpu127</span><span class="p">;</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p>不能动态支持多 CPU</p>
<ul>
<li>在支持 CPU 热插拔的系统中，CPU 的数量可能动态变化。</li>
<li>手动定义变量无法适应这种动态变化。</li>
</ul>
</li>
<li>
<p>访问效率低</p>
<ul>
<li>
<p>访问特定 CPU 的变量时，需要手动计算偏移量或使用条件语句，效率较低。</p>
</li>
<li>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="nf">get_var</span><span class="p">(</span><span class="kt">int</span> <span class="n">cpu</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">cpu</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">0</span><span class="o">:</span> <span class="k">return</span> <span class="o">&amp;</span><span class="n">var_cpu0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mi">1</span><span class="o">:</span> <span class="k">return</span> <span class="o">&amp;</span><span class="n">var_cpu1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">        <span class="k">default</span><span class="o">:</span> <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p>缓存局部性问题</p>
<ul>
<li>手动定义的变量可能位于同一缓存行中，导致 <strong>缓存行共享（False Sharing）</strong>，降低性能。</li>
</ul>
</li>
</ol>
<p><strong>Per-CPU 变量的优势</strong></p>
<ol>
<li>
<p>代码简洁</p>
<ul>
<li>
<p>Per-CPU 变量通过内核提供的机制自动为每个 CPU 分配变量，代码更简洁。</p>
</li>
<li>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="n">my_var</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p>动态适应 CPU 数量</p>
<ul>
<li>
<p>Per-CPU 变量支持动态 CPU 数量，能够自动适应 CPU 热插拔。</p>
</li>
<li>
<p>例如，动态分配 Per-CPU 变量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">__percpu</span> <span class="o">*</span><span class="n">my_var</span> <span class="o">=</span> <span class="nf">alloc_percpu</span><span class="p">(</span><span class="kt">int</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p>高效访问</p>
<ul>
<li>
<p>Per-CPU 变量通过内核提供的宏（如 <code>this_cpu_ptr</code> 和 <code>per_cpu_ptr</code>）高效访问当前或指定 CPU 的变量。</p>
</li>
<li>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">ptr</span> <span class="o">=</span> <span class="nf">this_cpu_ptr</span><span class="p">(</span><span class="o">&amp;</span><span class="n">my_var</span><span class="p">);</span>  <span class="c1">// 访问当前 CPU 的变量
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="o">*</span><span class="n">ptr</span> <span class="o">=</span> <span class="nf">per_cpu_ptr</span><span class="p">(</span><span class="o">&amp;</span><span class="n">my_var</span><span class="p">,</span> <span class="n">cpu</span><span class="p">);</span>  <span class="c1">// 访问指定 CPU 的变量
</span></span></span></code></pre></div></li>
</ul>
</li>
<li>
<p>缓存优化</p>
<ul>
<li>Per-CPU 变量会自动对齐到缓存行，避免缓存行共享问题。</li>
<li>例如，内核会确保每个 CPU 的变量位于不同的缓存行中。</li>
</ul>
</li>
</ol>
<h3 id="静态声明和定义">静态声明和定义</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 1. 定义 Per-CPU 变量
</span></span></span><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span>                    <span class="c1">// 定义并初始化为 0
</span></span></span><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>           <span class="c1">// 定义并指定初值
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 2. 声明外部 Per-CPU 变量
</span></span></span><span class="line"><span class="cl"><span class="nf">DECLARE_PER_CPU</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span>                  <span class="c1">// 声明在其他地方定义的变量
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 3. 定义 Per-CPU 数组
</span></span></span><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">name</span><span class="p">[</span><span class="n">SIZE</span><span class="p">]);</span>             <span class="c1">// 定义数组类型
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 4. 定义只读 Per-CPU 变量
</span></span></span><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU_READ_MOSTLY</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span>       <span class="c1">// 主要用于只读数据，优化缓存
</span></span></span></code></pre></div><h3 id="内部实现机制">内部实现机制</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define DEFINE_PER_CPU(type, name) \
</span></span></span><span class="line"><span class="cl"><span class="cp">        DEFINE_PER_CPU_SECTION(type, name, &#34;&#34;)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.data..percpu&#34;</span><span class="p">)))</span> <span class="kt">int</span> <span class="n">per_cpu_n</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define DEFINE_PER_CPU_SECTION(type, name, sec) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    __PCPU_ATTRS(sec) __typeof__(type) name
</span></span></span><span class="line"><span class="cl"><span class="cp">#define __PCPU_ATTRS(sec)                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">    __percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))    
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define PER_CPU_BASE_SECTION &#34;.data..percpu&#34;
</span></span></span></code></pre></div><p>比如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 在编译时定义 Per-CPU 变量
</span></span></span><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="n">my_counter</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 展开后实际上是
</span></span></span><span class="line"><span class="cl"><span class="nf">__attribute__</span><span class="p">((</span><span class="nf">section</span><span class="p">(</span><span class="s">&#34;.data..percpu&#34;</span><span class="p">)))</span> <span class="kt">int</span> <span class="n">my_counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></div><p>这里为什么要引入 section 机制？</p>
<blockquote>
<p>对于 kernel 中的普通变量，经过了编译和链接后，会被放置到.data 或者.bss 段，系统在初始化的时候会准备好一切（例如 clear bss），由于 per CPU 变量的特殊性，内核将这些变量放置到了其他的 section，位于 kernel address space 中<code>__per_cpu_start</code>和<code>__per_cpu_end</code>之间，我们称之 Per-CPU 变量的原始变量。
摘自：<a href="http://www.wowotech.net/kernel_synchronization/per-cpu.html">Linux 内核同步机制之（二）：Per-CPU 变量</a></p>
</blockquote>
<ul>
<li>Section 机制允许编译器将特定的数据放在目标文件的特定区域</li>
<li>对于 Per-CPU 变量，Linux 使用 .data..percpu section 将所有 Per-CPU 变量集中存放</li>
<li>这种集中存放便于运行时进行整体复制和管理</li>
</ul>
<p>Per-CPU 变量如何建立副本的？</p>
<p>在内核初始化阶段会调用<code>setup_per_cpu_areas</code>函数，为每个 CPU 设置 Per-CPU 变量的偏移地址。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">__init</span> <span class="nf">setup_per_cpu_areas</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">delta</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cpu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="n">delta</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">pcpu_base_addr</span> <span class="o">-</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">__per_cpu_start</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">for_each_possible_cpu</span><span class="p">(</span><span class="n">cpu</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">__per_cpu_offset</span><span class="p">[</span><span class="n">cpu</span><span class="p">]</span> <span class="o">=</span> <span class="n">delta</span> <span class="o">+</span> <span class="n">pcpu_unit_offsets</span><span class="p">[</span><span class="n">cpu</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li><code>__per_cpu_offset</code> 是一个全局数组，用于存储每个 CPU 的 Per-CPU 变量的偏移地址。</li>
<li>在初始化时，内核会为每个 CPU 计算其 Per-CPU 变量的基地址，并将其存储在 <code>__per_cpu_offset[cpu]</code> 中。</li>
<li>这个偏移地址是相对于 Per-CPU 变量的起始地址（<code>__per_cpu_start</code>）的。</li>
</ul>
<p>在 CPU 初始化时，内核会使用调用<code>set_my_cpu_offset</code>函数设置每个 CPU 静态 Per-CPU 变量的偏移地址。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define per_cpu_offset(x) (__per_cpu_offset(x))
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">notrace</span> <span class="nf">cpu_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="cp">#ifndef CONFIG_CPU_V7M
</span></span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cpu</span> <span class="o">=</span> <span class="nf">smp_processor_id</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="k">struct</span> <span class="n">stack</span> <span class="o">*</span><span class="n">stk</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">stacks</span><span class="p">[</span><span class="n">cpu</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">cpu</span> <span class="o">&gt;=</span> <span class="n">NR_CPUS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nf">pr_crit</span><span class="p">(</span><span class="s">&#34;CPU%u: bad primary CPU number</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">cpu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="nf">BUG</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * This only works on resume and secondary cores. For booting on the
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * boot cpu, smp_prepare_boot_cpu is called after percpu area setup.
</span></span></span><span class="line"><span class="cl"><span class="cm">	 */</span>
</span></span><span class="line"><span class="cl">	<span class="nf">set_my_cpu_offset</span><span class="p">(</span><span class="nf">per_cpu_offset</span><span class="p">(</span><span class="n">cpu</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">cpu_proc_init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在<code>set_my_cpu_offset</code>函数中，根据<code>ARM64_HAS_VIRT_HOST_EXTN</code>的值，选择将偏移地址写入<code>tpidr_el1</code>或者<code>tpidr_el2</code>寄存器。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* arch/arm64/include/asm/percpu.h */</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="kt">void</span> <span class="nf">set_my_cpu_offset</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">off</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">asm</span> <span class="k">volatile</span><span class="p">(</span><span class="nf">ALTERNATIVE</span><span class="p">(</span><span class="s">&#34;msr tpidr_el1, %0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="s">&#34;msr tpidr_el2, %0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="n">ARM64_HAS_VIRT_HOST_EXTN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">::</span> <span class="s">&#34;r&#34;</span> <span class="p">(</span><span class="n">off</span><span class="p">)</span> <span class="o">:</span> <span class="s">&#34;memory&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在访问时使用<code>this_cpu_ptr</code>宏，展开后可以发现相当于 percpu 变量指针 ptr 加上<code>__my_cpu_offset</code>。<code>__my_cpu_offset</code>宏即是从当前cpu的<code>tpidr_el1</code>、<code>tpidr_el2</code>寄存器中取出此前设置的<code>__per_cpu_offset[cpu]</code>值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define this_cpu_ptr(ptr)    raw_cpu_ptr(ptr)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define raw_cpu_ptr(ptr)                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">({                                    \
</span></span></span><span class="line"><span class="cl"><span class="cp">    __verify_pcpu_ptr(ptr);                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">    arch_raw_cpu_ptr(ptr);                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">})
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define arch_raw_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, __my_cpu_offset)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">__my_cpu_offset</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">off</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">     * We want to allow caching the value, so avoid using volatile and
</span></span></span><span class="line"><span class="cl"><span class="cm">     * instead use a fake stack read to hazard against barrier().
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">asm</span><span class="p">(</span><span class="nf">ALTERNATIVE</span><span class="p">(</span><span class="s">&#34;mrs %0, tpidr_el1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s">&#34;mrs %0, tpidr_el2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">ARM64_HAS_VIRT_HOST_EXTN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="s">&#34;=r&#34;</span> <span class="p">(</span><span class="n">off</span><span class="p">)</span> <span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;Q&#34;</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="p">)</span><span class="n">current_stack_pointer</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">off</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="使用方式">使用方式</h3>
<p>Per-CPU 变量的常用操作方式：</p>
<p><strong>静态定义</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 定义 Per-CPU 整型变量
</span></span></span><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="n">my_percpu_var</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 定义 Per-CPU 结构体
</span></span></span><span class="line"><span class="cl"><span class="nf">DEFINE_PER_CPU</span><span class="p">(</span><span class="k">struct</span> <span class="n">my_struct</span><span class="p">,</span> <span class="n">my_percpu_struct</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>访问变量</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 获取本地 CPU 的变量
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">cpu_var</span> <span class="o">=</span> <span class="nf">get_cpu_var</span><span class="p">(</span><span class="n">my_percpu_var</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">put_cpu_var</span><span class="p">(</span><span class="n">my_percpu_var</span><span class="p">);</span>  <span class="c1">// 必须配对使用
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 访问指定 CPU 的变量
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">var_cpu0</span> <span class="o">=</span> <span class="nf">per_cpu</span><span class="p">(</span><span class="n">my_percpu_var</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 遍历所有 CPU 的变量
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">cpu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">for_each_possible_cpu</span><span class="p">(</span><span class="n">cpu</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">value</span> <span class="o">=</span> <span class="nf">per_cpu</span><span class="p">(</span><span class="n">my_percpu_var</span><span class="p">,</span> <span class="n">cpu</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理 value
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>速联SRAM RIVAL升级功率计</title>
      <link>https://lifeislife.cn/posts/%E9%80%9F%E8%81%94sram-rival%E5%8D%87%E7%BA%A7%E5%8A%9F%E7%8E%87%E8%AE%A1/</link>
      <pubDate>Sat, 02 Nov 2024 10:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E9%80%9F%E8%81%94sram-rival%E5%8D%87%E7%BA%A7%E5%8A%9F%E7%8E%87%E8%AE%A1/</guid>
      <description>&lt;h1 id=&#34;为什么要升级&#34;&gt;为什么要升级&lt;/h1&gt;
&lt;p&gt;人傻钱多（bushi）。平时骑台子能实时看到自己输出功率，判断自己的状态，也能更好的控制自己的训练强度，不至于过度训练。外出路骑没有功率计总觉得不得劲，特别是在蹭风或者团骑时，不知道自己的状态如何，因为路骑人比较兴奋，也许功率已经超过自己的FTP但是可能并不觉得累，但是体能是有限的，不累只是精神上的感觉，也许骑不了多久精神头过了就被拉爆了。所以十分想要一个功率计能够实时看到自己的输出功率，能让路骑尽量维持在自己想要的强度。&lt;/p&gt;
&lt;h1 id=&#34;前期准备&#34;&gt;前期准备&lt;/h1&gt;
&lt;p&gt;先了解和功率计相关的几个零件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;盘片
&lt;ul&gt;
&lt;li&gt;SRAM Rival 107BCD 盘片
&lt;ul&gt;
&lt;li&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//2024/11/02/d8dbeaea4c456b12ab411f003817fa8e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/d8dbeaea4c456b12ab411f003817fa8e.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;盘爪
&lt;ul&gt;
&lt;li&gt;SRAM Red AXS/Force
&lt;ul&gt;
&lt;li&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//2024/11/02/731cdbc91ef46d8b83f288335e93d881.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/731cdbc91ef46d8b83f288335e93d881.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;盘钉：固定盘片和盘爪
&lt;ul&gt;
&lt;li&gt;SRAM RIVAL 107/94 BCD M8X4.75X8.75 盘钉
&lt;ul&gt;
&lt;li&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//2024/11/02/36b8ba6a6ef7cf56fb28e6a5bfb7c497.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/36b8ba6a6ef7cf56fb28e6a5bfb7c497.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;曲柄
&lt;ul&gt;
&lt;li&gt;SRAM RIVAL AXS QUARQ DUB&lt;/li&gt;
&lt;li&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//2024/11/02/27d80f442010abcb997944f0cbda099a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/27d80f442010abcb997944f0cbda099a.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;曲柄螺丝
&lt;ul&gt;
&lt;li&gt;SRAM DUB AXS RED或FROCE 曲柄螺丝&lt;/li&gt;
&lt;li&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//2024/11/02/ef2e8c647b649f55846b0df00219e73e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/ef2e8c647b649f55846b0df00219e73e.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;中轴
&lt;ul&gt;
&lt;li&gt;和曲柄连接在一起&lt;/li&gt;
&lt;li&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//2024/11/02/e4009029b373cefeb313f49b3b6953b3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/e4009029b373cefeb313f49b3b6953b3.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;功率计的种类有很多，有脚踏功率计，盘爪功率计、曲柄功率计和贴片功率计等等，每种功率计都有自己的优缺点，因为我也不是专家所以就不详细介绍了，想要了解的可以参考视频&lt;a href=&#34;https://www.bilibili.com/video/BV1hj411s7mm/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;公路车功率计介绍和选择安装建议_哔哩哔哩_bilibili&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我在看了一些介绍视频以及根据自己目前使用套件的情况，最终选择了使用人群最广的盘爪式功率计，盘爪功率计就是将上面提到的盘爪这个邻接用功率计替换，功率计本身就是盘爪。下面是我升级选购的一些思路，供和我一样的小白参考。&lt;/p&gt;
&lt;p&gt;当我决定使用盘爪式功率计时，就需要选择具体哪一款，哪一个品牌的功率计，可以直接淘宝搜索“盘爪功率计”，会有很多选择，如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;迈金 P505&lt;/li&gt;
&lt;li&gt;SRAM QUARQ&lt;/li&gt;
&lt;li&gt;Sigeyi 思各异&lt;/li&gt;
&lt;li&gt;XCADEY&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来就是根据自己的钱包大小来选择了，当然还有一个最重要的参数，就是BCD，这个参数是盘爪功率计的螺丝孔的间距，不同的品牌不同的型号的盘爪功率计BCD是不一样的，这个参数决定了功率计能否安装到牙盘上，我使用的是SRAM Rival eTap AXS套件，这个套件的BCD是107，所以我需要选择BCD为107的盘爪功率计。这时查看各个功率计详情可以看到迈金不支持107BCD，所以可以直接排除这款功率计了。在剩下的几款功率计中，SRAM QUARQ是SRAM自家的产品，我觉得原配最好，所以选择了SRAM QUARQ。&lt;/p&gt;
&lt;p&gt;关于盘片和盘爪规格的介绍可以参考视频&lt;a href=&#34;https://www.bilibili.com/video/BV1dM4m1y7c3/?spm_id_from=333.999.0.0&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;【自行车维修手册】第17期 公路车盘片选择与安装_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在确定了功率计后，就需要考虑如何安装了，RIVAL套件中，曲柄和盘爪是一体的，这意味着如果要在不更换盘片的情况下加装功率计，就需要更换曲柄。更换曲柄就需要了解曲柄的规格，有不同类型的中轴比如GXP和DU，有不同的曲柄长度，还有不同的安装规格，比如3钉，5钉，8钉等等。因为我们已经选好了盘爪功率计，而盘爪功率计是和曲柄安装在一起的，所以盘爪功率计的规格已经确定了曲柄的安装规格，比如QUARQ的盘爪功率计是8钉，所以我们需要选择8钉的曲柄。接下来就是选择曲柄的长度，曲柄的长度根据自己的身体数据来选择，没有Fitting的话就看原来曲柄的长度，换一个一样长度的即可，曲柄的长度标注在曲柄上，比如我的RIVAL套件是170mm。&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//2024/11/05/ad08bcbc03336c4db6c6fd64f55cec02.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/ad08bcbc03336c4db6c6fd64f55cec02.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//2024/11/05/25f13723a0ef847e602331a4af015bd9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/25f13723a0ef847e602331a4af015bd9.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;根据这些信息我选择了SRAM FORCE DUB 8 钉曲柄。&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//2024/11/05/1992762c24b909be05270aad2493df57.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/1992762c24b909be05270aad2493df57.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;a href=&#34;https://www.bilibili.com/video/BV1iW421R7se/?spm_id_from=333.999.0.0&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;【自行车维修手册】第15期 自行车曲柄牙盘规格区分介绍_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;至此主要的零件已经选购完毕，接下来就是等待零件到货，然后开始安装。但是安装少不了工具，需要准备的工具有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;8 mm 内六角扳手，拆卸安装曲柄&lt;/li&gt;
&lt;li&gt;空心钢管，用于拆卸安装曲柄，当做加力杆，曲柄的螺丝扭力高达50N.m，普通扳手力臂太小，很难拧下来&lt;/li&gt;
&lt;li&gt;6 mm 内六角扳手，拆卸安装脚踏&lt;/li&gt;
&lt;li&gt;2 mm 内六角扳手，调整曲柄锁紧环&lt;/li&gt;
&lt;li&gt;扭力扳手，安装曲柄螺丝&lt;/li&gt;
&lt;li&gt;界面脂&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;拆卸&#34;&gt;拆卸&lt;/h1&gt;
&lt;p&gt;曲柄拆卸方式可以参考视频&lt;a href=&#34;https://www.bilibili.com/video/BV13w4m1271e/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;技师手册十四（连载）速联RIVAL牙盘曲柄拆装_哔哩哔哩_bilibili&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//2024/11/05/98c7b773a4f766c79beff19d2fd9a6fe.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/98c7b773a4f766c79beff19d2fd9a6fe.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;h1 id=&#34;安装&#34;&gt;安装&lt;/h1&gt;
&lt;p&gt;安装就是把螺丝拧上，但是有一些细节需要注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;曲柄和盘爪连接的螺丝的扭力值为 4 N.m&lt;/li&gt;
&lt;li&gt;大牙盘正面有一个金属圆柱，这个圆柱体需要在曲柄正下方
&lt;ul&gt;
&lt;li&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//2024/11/06/8d795b69827bc54175e92387ff25e31e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/06/8d795b69827bc54175e92387ff25e31e.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;/li&gt;
&lt;li&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//2024/11/06/86d9decd4c459c518c3bd83e284fcd6b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/06/86d9decd4c459c518c3bd83e284fcd6b.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;小牙盘上有一个凸起的尖尖，这个尖尖需要在曲柄正下方
&lt;ul&gt;
&lt;li&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//2024/11/06/047489de549a5d449be3d655314c06c2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/06/047489de549a5d449be3d655314c06c2.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;小牙盘标注齿数和型号的那面正对自己才是正确的方向&lt;/li&gt;
&lt;li&gt;牙盘的盘钉扭力值为 10 N.m&lt;/li&gt;
&lt;li&gt;每个螺丝都抹上界面脂，因为牙盘上的螺丝都会受到很大的力，所以需要抹上界面脂，这样可以减小螺丝的摩擦力，减小螺丝的磨损，也可以防止异响&lt;/li&gt;
&lt;li&gt;中轴的大螺丝抹上黄油，扭力值至少为50 N.m，扭紧时最好用上空心钢管&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;参考资料&#34;&gt;参考资料&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.bilibili.com/video/BV1iW421R7se/?spm_id_from=333.999.0.0&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;【自行车维修手册】第15期 自行车曲柄牙盘规格区分介绍_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.bilibili.com/video/BV1dM4m1y7c3/?spm_id_from=333.999.0.0&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;【自行车维修手册】第17期 公路车盘片选择与安装_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.bilibili.com/video/BV1hj411s7mm/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;公路车功率计介绍和选择安装建议_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h1 id="为什么要升级">为什么要升级</h1>
<p>人傻钱多（bushi）。平时骑台子能实时看到自己输出功率，判断自己的状态，也能更好的控制自己的训练强度，不至于过度训练。外出路骑没有功率计总觉得不得劲，特别是在蹭风或者团骑时，不知道自己的状态如何，因为路骑人比较兴奋，也许功率已经超过自己的FTP但是可能并不觉得累，但是体能是有限的，不累只是精神上的感觉，也许骑不了多久精神头过了就被拉爆了。所以十分想要一个功率计能够实时看到自己的输出功率，能让路骑尽量维持在自己想要的强度。</p>
<h1 id="前期准备">前期准备</h1>
<p>先了解和功率计相关的几个零件：</p>
<ul>
<li>盘片
<ul>
<li>SRAM Rival 107BCD 盘片
<ul>
<li>

<!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//2024/11/02/d8dbeaea4c456b12ab411f003817fa8e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/d8dbeaea4c456b12ab411f003817fa8e.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></li>
</ul>
</li>
</ul>
</li>
<li>盘爪
<ul>
<li>SRAM Red AXS/Force
<ul>
<li>

<!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//2024/11/02/731cdbc91ef46d8b83f288335e93d881.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/731cdbc91ef46d8b83f288335e93d881.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></li>
</ul>
</li>
</ul>
</li>
<li>盘钉：固定盘片和盘爪
<ul>
<li>SRAM RIVAL 107/94 BCD M8X4.75X8.75 盘钉
<ul>
<li>

<!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//2024/11/02/36b8ba6a6ef7cf56fb28e6a5bfb7c497.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/36b8ba6a6ef7cf56fb28e6a5bfb7c497.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></li>
</ul>
</li>
</ul>
</li>
<li>曲柄
<ul>
<li>SRAM RIVAL AXS QUARQ DUB</li>
<li>

<!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//2024/11/02/27d80f442010abcb997944f0cbda099a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/27d80f442010abcb997944f0cbda099a.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></li>
</ul>
</li>
<li>曲柄螺丝
<ul>
<li>SRAM DUB AXS RED或FROCE 曲柄螺丝</li>
<li>

<!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//2024/11/02/ef2e8c647b649f55846b0df00219e73e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/ef2e8c647b649f55846b0df00219e73e.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></li>
</ul>
</li>
<li>中轴
<ul>
<li>和曲柄连接在一起</li>
<li>

<!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//2024/11/02/e4009029b373cefeb313f49b3b6953b3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/02/e4009029b373cefeb313f49b3b6953b3.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></li>
</ul>
</li>
</ul>
<p>功率计的种类有很多，有脚踏功率计，盘爪功率计、曲柄功率计和贴片功率计等等，每种功率计都有自己的优缺点，因为我也不是专家所以就不详细介绍了，想要了解的可以参考视频<a href="https://www.bilibili.com/video/BV1hj411s7mm/?spm_id_from=333.337.search-card.all.click&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">公路车功率计介绍和选择安装建议_哔哩哔哩_bilibili</a>。</p>
<p>我在看了一些介绍视频以及根据自己目前使用套件的情况，最终选择了使用人群最广的盘爪式功率计，盘爪功率计就是将上面提到的盘爪这个邻接用功率计替换，功率计本身就是盘爪。下面是我升级选购的一些思路，供和我一样的小白参考。</p>
<p>当我决定使用盘爪式功率计时，就需要选择具体哪一款，哪一个品牌的功率计，可以直接淘宝搜索“盘爪功率计”，会有很多选择，如：</p>
<ul>
<li>迈金 P505</li>
<li>SRAM QUARQ</li>
<li>Sigeyi 思各异</li>
<li>XCADEY</li>
</ul>
<p>接下来就是根据自己的钱包大小来选择了，当然还有一个最重要的参数，就是BCD，这个参数是盘爪功率计的螺丝孔的间距，不同的品牌不同的型号的盘爪功率计BCD是不一样的，这个参数决定了功率计能否安装到牙盘上，我使用的是SRAM Rival eTap AXS套件，这个套件的BCD是107，所以我需要选择BCD为107的盘爪功率计。这时查看各个功率计详情可以看到迈金不支持107BCD，所以可以直接排除这款功率计了。在剩下的几款功率计中，SRAM QUARQ是SRAM自家的产品，我觉得原配最好，所以选择了SRAM QUARQ。</p>
<p>关于盘片和盘爪规格的介绍可以参考视频<a href="https://www.bilibili.com/video/BV1dM4m1y7c3/?spm_id_from=333.999.0.0&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">【自行车维修手册】第17期 公路车盘片选择与安装_哔哩哔哩_bilibili</a></p>
<p>在确定了功率计后，就需要考虑如何安装了，RIVAL套件中，曲柄和盘爪是一体的，这意味着如果要在不更换盘片的情况下加装功率计，就需要更换曲柄。更换曲柄就需要了解曲柄的规格，有不同类型的中轴比如GXP和DU，有不同的曲柄长度，还有不同的安装规格，比如3钉，5钉，8钉等等。因为我们已经选好了盘爪功率计，而盘爪功率计是和曲柄安装在一起的，所以盘爪功率计的规格已经确定了曲柄的安装规格，比如QUARQ的盘爪功率计是8钉，所以我们需要选择8钉的曲柄。接下来就是选择曲柄的长度，曲柄的长度根据自己的身体数据来选择，没有Fitting的话就看原来曲柄的长度，换一个一样长度的即可，曲柄的长度标注在曲柄上，比如我的RIVAL套件是170mm。</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//2024/11/05/ad08bcbc03336c4db6c6fd64f55cec02.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/ad08bcbc03336c4db6c6fd64f55cec02.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//2024/11/05/25f13723a0ef847e602331a4af015bd9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/25f13723a0ef847e602331a4af015bd9.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>根据这些信息我选择了SRAM FORCE DUB 8 钉曲柄。</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//2024/11/05/1992762c24b909be05270aad2493df57.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/1992762c24b909be05270aad2493df57.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>关于曲柄规格的介绍可以参考视频<a href="https://www.bilibili.com/video/BV1iW421R7se/?spm_id_from=333.999.0.0&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">【自行车维修手册】第15期 自行车曲柄牙盘规格区分介绍_哔哩哔哩_bilibili</a></p>
<p>至此主要的零件已经选购完毕，接下来就是等待零件到货，然后开始安装。但是安装少不了工具，需要准备的工具有：</p>
<ul>
<li>8 mm 内六角扳手，拆卸安装曲柄</li>
<li>空心钢管，用于拆卸安装曲柄，当做加力杆，曲柄的螺丝扭力高达50N.m，普通扳手力臂太小，很难拧下来</li>
<li>6 mm 内六角扳手，拆卸安装脚踏</li>
<li>2 mm 内六角扳手，调整曲柄锁紧环</li>
<li>扭力扳手，安装曲柄螺丝</li>
<li>界面脂</li>
</ul>
<h1 id="拆卸">拆卸</h1>
<p>曲柄拆卸方式可以参考视频<a href="https://www.bilibili.com/video/BV13w4m1271e/?spm_id_from=333.337.search-card.all.click&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">技师手册十四（连载）速联RIVAL牙盘曲柄拆装_哔哩哔哩_bilibili</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//2024/11/05/98c7b773a4f766c79beff19d2fd9a6fe.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/05/98c7b773a4f766c79beff19d2fd9a6fe.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>
<h1 id="安装">安装</h1>
<p>安装就是把螺丝拧上，但是有一些细节需要注意：</p>
<ul>
<li>曲柄和盘爪连接的螺丝的扭力值为 4 N.m</li>
<li>大牙盘正面有一个金属圆柱，这个圆柱体需要在曲柄正下方
<ul>
<li>

<!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//2024/11/06/8d795b69827bc54175e92387ff25e31e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/06/8d795b69827bc54175e92387ff25e31e.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></li>
<li>

<!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//2024/11/06/86d9decd4c459c518c3bd83e284fcd6b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/06/86d9decd4c459c518c3bd83e284fcd6b.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></li>
</ul>
</li>
<li>小牙盘上有一个凸起的尖尖，这个尖尖需要在曲柄正下方
<ul>
<li>

<!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//2024/11/06/047489de549a5d449be3d655314c06c2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/11/06/047489de549a5d449be3d655314c06c2.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></li>
</ul>
</li>
<li>小牙盘标注齿数和型号的那面正对自己才是正确的方向</li>
<li>牙盘的盘钉扭力值为 10 N.m</li>
<li>每个螺丝都抹上界面脂，因为牙盘上的螺丝都会受到很大的力，所以需要抹上界面脂，这样可以减小螺丝的摩擦力，减小螺丝的磨损，也可以防止异响</li>
<li>中轴的大螺丝抹上黄油，扭力值至少为50 N.m，扭紧时最好用上空心钢管</li>
</ul>
<h1 id="参考资料">参考资料</h1>
<ul>
<li><a href="https://www.bilibili.com/video/BV1iW421R7se/?spm_id_from=333.999.0.0&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">【自行车维修手册】第15期 自行车曲柄牙盘规格区分介绍_哔哩哔哩_bilibili</a></li>
<li><a href="https://www.bilibili.com/video/BV1dM4m1y7c3/?spm_id_from=333.999.0.0&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">【自行车维修手册】第17期 公路车盘片选择与安装_哔哩哔哩_bilibili</a></li>
<li><a href="https://www.bilibili.com/video/BV1hj411s7mm/?spm_id_from=333.337.search-card.all.click&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">公路车功率计介绍和选择安装建议_哔哩哔哩_bilibili</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>PaperMod添加Follow RSS订阅认证</title>
      <link>https://lifeislife.cn/posts/papermod%E6%B7%BB%E5%8A%A0follow-rss%E8%AE%A4%E8%AF%81/</link>
      <pubDate>Sun, 27 Oct 2024 10:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/papermod%E6%B7%BB%E5%8A%A0follow-rss%E8%AE%A4%E8%AF%81/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://follow.is/&#34;&gt;Follow&lt;/a&gt;是一个开源RSS阅读器，可以发布自己的订阅源，比如将自己的博客RSS发布到Follow上，其他用户在Follow上订阅你的博客时，就可以看到这是你发布的源，并且可以为源的发布者进行打赏。当然自己的小博客也不指望其他人能订阅，只是想完成Follow的成就任务，完成认证后可以获得Power值，可以用来打赏其他用户，以及购买其他服务，比如订阅付费的列表等。&lt;/p&gt;
&lt;p&gt;PaperMod已经嵌入了RSS订阅功能，所以认证会很方便，所谓认证就是在自己的网站中加入一段代码，Follow会检测这段代码，如果检测到了，就会认为这个网站是你发布的源，从而完成认证。&lt;/p&gt;
&lt;h2 id=&#34;开启papermod的rss订阅功能&#34;&gt;开启PaperMod的RSS订阅功能&lt;/h2&gt;
&lt;p&gt;首先要在&lt;code&gt;config.yaml&lt;/code&gt;中开启RSS订阅功能，如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;socialIcons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;RSS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{网站链接}/index.xml&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ShowFullTextinRSS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;socialIcons&lt;/code&gt;参数用于添加博客主页的图标，这里添加了一个RSS订阅图标，其中&lt;code&gt;{网站链接}&lt;/code&gt;是你的网站链接，比如我的博客是&lt;code&gt;https://lifeislife.cn/&lt;/code&gt;，那么这里就填&lt;code&gt;https://lifeislife.cn/index.xml&lt;/code&gt;。&lt;code&gt;ShowFullTextinRSS&lt;/code&gt;参数是控制RSS订阅的内容是否显示全文，如果为&lt;code&gt;true&lt;/code&gt;，则显示全文，否则只显示摘要。如果为&lt;code&gt;false&lt;/code&gt;，那么RSS订阅时只会显示摘要，点击后才会跳转到博客查看全文。为了方便用户阅读，建议设置为&lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;前往follow进行订阅&#34;&gt;前往Follow进行订阅&lt;/h2&gt;
&lt;p&gt;打开博客主页，点击RSS图标，会跳转到&lt;code&gt;https://lifeislife.cn/index.xml&lt;/code&gt;，这是博客的RSS订阅地址，复制这个地址，然后打开Follow，点击右上角的&lt;code&gt;+&lt;/code&gt;，选择&lt;code&gt;Add Feed&lt;/code&gt;，粘贴刚才复制的地址，点击&lt;code&gt;Add Feed&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//2024/10/27/7d7532aa54da04576dae497e9cfb6fc4.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/7d7532aa54da04576dae497e9cfb6fc4.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//2024/10/27/d3c2de4fcb2d37832125d4836d5edc82.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/d3c2de4fcb2d37832125d4836d5edc82.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;h2 id=&#34;获取认证代码&#34;&gt;获取认证代码&lt;/h2&gt;
&lt;p&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//2024/10/27/108168c99edec36b04dc668b83ce65e4.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/108168c99edec36b04dc668b83ce65e4.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;h2 id=&#34;添加认证代码&#34;&gt;添加认证代码&lt;/h2&gt;
&lt;p&gt;需要将上面得到的代码添加到博客的RSS订阅页面的&lt;code&gt;description&lt;/code&gt;标签中，这个标签在&lt;code&gt;themes/PaperMod/layouts/_default/rss.xml&lt;/code&gt;中。为了便于修改，通常我会将博客主题原目录中的&lt;code&gt;themes/PaperMod/layouts/_default/rss.xml&lt;/code&gt;复制到我的博客根目录&lt;code&gt;layouts/_default/rss.xml&lt;/code&gt;，然后在&lt;code&gt;layouts/_default/rss.xml&lt;/code&gt;中修改&lt;code&gt;description&lt;/code&gt;标签。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;feedId:57980998056508425+userId:73222296380546048 Recent content {{ if ne .Title site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ site.Title }}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上就是添加好的状态，保存修改并提交，然后重新部署博客。&lt;/p&gt;
&lt;h2 id=&#34;完成认证&#34;&gt;完成认证&lt;/h2&gt;
&lt;p&gt;回到Follow，点击&lt;code&gt;认证&lt;/code&gt;按钮，如果博客已经被正常部署，那么会显示认证成功。点击头像-&amp;gt;成就，就可以获取60Power的奖励。&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//2024/10/27/8f26a0e397aae374084234e7e2380a58.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/8f26a0e397aae374084234e7e2380a58.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;
</description>
      <content:encoded><![CDATA[<p><a href="https://follow.is/">Follow</a>是一个开源RSS阅读器，可以发布自己的订阅源，比如将自己的博客RSS发布到Follow上，其他用户在Follow上订阅你的博客时，就可以看到这是你发布的源，并且可以为源的发布者进行打赏。当然自己的小博客也不指望其他人能订阅，只是想完成Follow的成就任务，完成认证后可以获得Power值，可以用来打赏其他用户，以及购买其他服务，比如订阅付费的列表等。</p>
<p>PaperMod已经嵌入了RSS订阅功能，所以认证会很方便，所谓认证就是在自己的网站中加入一段代码，Follow会检测这段代码，如果检测到了，就会认为这个网站是你发布的源，从而完成认证。</p>
<h2 id="开启papermod的rss订阅功能">开启PaperMod的RSS订阅功能</h2>
<p>首先要在<code>config.yaml</code>中开启RSS订阅功能，如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">socialIcons</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">RSS</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;{网站链接}/index.xml&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ShowFullTextinRSS</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><p><code>socialIcons</code>参数用于添加博客主页的图标，这里添加了一个RSS订阅图标，其中<code>{网站链接}</code>是你的网站链接，比如我的博客是<code>https://lifeislife.cn/</code>，那么这里就填<code>https://lifeislife.cn/index.xml</code>。<code>ShowFullTextinRSS</code>参数是控制RSS订阅的内容是否显示全文，如果为<code>true</code>，则显示全文，否则只显示摘要。如果为<code>false</code>，那么RSS订阅时只会显示摘要，点击后才会跳转到博客查看全文。为了方便用户阅读，建议设置为<code>true</code>。</p>
<h2 id="前往follow进行订阅">前往Follow进行订阅</h2>
<p>打开博客主页，点击RSS图标，会跳转到<code>https://lifeislife.cn/index.xml</code>，这是博客的RSS订阅地址，复制这个地址，然后打开Follow，点击右上角的<code>+</code>，选择<code>Add Feed</code>，粘贴刚才复制的地址，点击<code>Add Feed</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//2024/10/27/7d7532aa54da04576dae497e9cfb6fc4.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/7d7532aa54da04576dae497e9cfb6fc4.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//2024/10/27/d3c2de4fcb2d37832125d4836d5edc82.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/d3c2de4fcb2d37832125d4836d5edc82.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>
<h2 id="获取认证代码">获取认证代码</h2>
<p>在订阅列表中右击你的博客，选择认证，会弹出一个对话框，里面有一段代码，复制这段代码。</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//2024/10/27/108168c99edec36b04dc668b83ce65e4.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/108168c99edec36b04dc668b83ce65e4.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>
<h2 id="添加认证代码">添加认证代码</h2>
<p>需要将上面得到的代码添加到博客的RSS订阅页面的<code>description</code>标签中，这个标签在<code>themes/PaperMod/layouts/_default/rss.xml</code>中。为了便于修改，通常我会将博客主题原目录中的<code>themes/PaperMod/layouts/_default/rss.xml</code>复制到我的博客根目录<code>layouts/_default/rss.xml</code>，然后在<code>layouts/_default/rss.xml</code>中修改<code>description</code>标签。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">    <span class="nt">&lt;description&gt;</span>feedId:57980998056508425+userId:73222296380546048 Recent content {{ if ne .Title site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ site.Title }}<span class="nt">&lt;/description&gt;</span>
</span></span></code></pre></div><p>以上就是添加好的状态，保存修改并提交，然后重新部署博客。</p>
<h2 id="完成认证">完成认证</h2>
<p>回到Follow，点击<code>认证</code>按钮，如果博客已经被正常部署，那么会显示认证成功。点击头像-&gt;成就，就可以获取60Power的奖励。</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//2024/10/27/8f26a0e397aae374084234e7e2380a58.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/10/27/8f26a0e397aae374084234e7e2380a58.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>
]]></content:encoded>
    </item>
    <item>
      <title>Linux 清理磁盘释放空间</title>
      <link>https://lifeislife.cn/posts/linux-%E6%B8%85%E7%90%86%E7%A3%81%E7%9B%98%E9%87%8A%E6%94%BE%E7%A9%BA%E9%97%B4/</link>
      <pubDate>Sat, 21 Sep 2024 10:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/linux-%E6%B8%85%E7%90%86%E7%A3%81%E7%9B%98%E9%87%8A%E6%94%BE%E7%A9%BA%E9%97%B4/</guid>
      <description>&lt;p&gt;NAS 的系统磁盘空间告急了，当初用了一张就笔记本闲置的固态硬盘，只有 128G，心想光做系统盘应该足够了，结果还是经不住折腾，需要清理一下磁盘空间。本文记录了一些常用的清理磁盘空间的方法。&lt;/p&gt;
&lt;h2 id=&#34;查看磁盘使用情况&#34;&gt;查看磁盘使用情况&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;df -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;清理软件包&#34;&gt;清理软件包&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理旧版本缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get autoclean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理所有缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 移除孤立软件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get autoremove
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;清理日志&#34;&gt;清理日志&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 journal 日志占用的硬盘空间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;journalctl -x --disk-usage
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理日志到只剩下 10M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;journalctl --vacuum-size&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;10M
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理一天前的日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;journalctl --vacuum-time&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 永久限制 journal 日志的大小：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Journal&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SystemMaxUse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;10M   &lt;span class=&#34;c1&#34;&gt;# 硬盘中只保留最近 10M 的日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;RuntimeMaxUse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;10M  &lt;span class=&#34;c1&#34;&gt;# 内存中只保留最近 10M 的日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;清理-docker&#34;&gt;清理 Docker&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理所有停止的容器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker container prune
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理所有未使用的镜像&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker image prune
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理所有未使用的卷&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker volume prune
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理所有未使用的网络&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker network prune
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理所有未使用的镜像、容器、网络、卷，如果你使用docker的话，这一步会释放大量的空间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker system prune --volumes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;WARNING! This will remove:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - all stopped containers
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - all networks not used by at least one container
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - all volumes not used by at least one container
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - all dangling images
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - all dangling build cache
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;清理-pip-缓存&#34;&gt;清理 pip 缓存&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理 pip 缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pip cache purge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -r ~/.cache/pip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;大文件查找清理&#34;&gt;大文件查找清理&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查找大于 100M 的文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;find / -type f -size +100M
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除大于 100M 的文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;find / -type f -size +100M -delete
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>NAS 的系统磁盘空间告急了，当初用了一张就笔记本闲置的固态硬盘，只有 128G，心想光做系统盘应该足够了，结果还是经不住折腾，需要清理一下磁盘空间。本文记录了一些常用的清理磁盘空间的方法。</p>
<h2 id="查看磁盘使用情况">查看磁盘使用情况</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">df -h
</span></span></code></pre></div><h2 id="清理软件包">清理软件包</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 清理旧版本缓存</span>
</span></span><span class="line"><span class="cl">apt-get autoclean
</span></span><span class="line"><span class="cl"><span class="c1"># 清理所有缓存</span>
</span></span><span class="line"><span class="cl">apt-get clean
</span></span><span class="line"><span class="cl"><span class="c1"># 移除孤立软件</span>
</span></span><span class="line"><span class="cl">apt-get autoremove
</span></span></code></pre></div><h2 id="清理日志">清理日志</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查看 journal 日志占用的硬盘空间</span>
</span></span><span class="line"><span class="cl">journalctl -x --disk-usage
</span></span><span class="line"><span class="cl"><span class="c1"># 清理日志到只剩下 10M</span>
</span></span><span class="line"><span class="cl">journalctl --vacuum-size<span class="o">=</span>10M
</span></span><span class="line"><span class="cl"><span class="c1"># 清理一天前的日志</span>
</span></span><span class="line"><span class="cl">journalctl --vacuum-time<span class="o">=</span>1d
</span></span><span class="line"><span class="cl"><span class="c1"># 永久限制 journal 日志的大小：</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>Journal<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">SystemMaxUse</span><span class="o">=</span>10M   <span class="c1"># 硬盘中只保留最近 10M 的日志</span>
</span></span><span class="line"><span class="cl"><span class="nv">RuntimeMaxUse</span><span class="o">=</span>10M  <span class="c1"># 内存中只保留最近 10M 的日志</span>
</span></span></code></pre></div><h2 id="清理-docker">清理 Docker</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 清理所有停止的容器</span>
</span></span><span class="line"><span class="cl">docker container prune
</span></span><span class="line"><span class="cl"><span class="c1"># 清理所有未使用的镜像</span>
</span></span><span class="line"><span class="cl">docker image prune
</span></span><span class="line"><span class="cl"><span class="c1"># 清理所有未使用的卷</span>
</span></span><span class="line"><span class="cl">docker volume prune
</span></span><span class="line"><span class="cl"><span class="c1"># 清理所有未使用的网络</span>
</span></span><span class="line"><span class="cl">docker network prune
</span></span><span class="line"><span class="cl"><span class="c1"># 清理所有未使用的镜像、容器、网络、卷，如果你使用docker的话，这一步会释放大量的空间</span>
</span></span><span class="line"><span class="cl">docker system prune --volumes
</span></span><span class="line"><span class="cl">WARNING! This will remove:
</span></span><span class="line"><span class="cl">  - all stopped containers
</span></span><span class="line"><span class="cl">  - all networks not used by at least one container
</span></span><span class="line"><span class="cl">  - all volumes not used by at least one container
</span></span><span class="line"><span class="cl">  - all dangling images
</span></span><span class="line"><span class="cl">  - all dangling build cache
</span></span></code></pre></div><h2 id="清理-pip-缓存">清理 pip 缓存</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 清理 pip 缓存</span>
</span></span><span class="line"><span class="cl">pip cache purge
</span></span><span class="line"><span class="cl">rm -r ~/.cache/pip
</span></span></code></pre></div><h2 id="大文件查找清理">大文件查找清理</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 查找大于 100M 的文件</span>
</span></span><span class="line"><span class="cl">find / -type f -size +100M
</span></span><span class="line"><span class="cl"><span class="c1"># 删除大于 100M 的文件</span>
</span></span><span class="line"><span class="cl">find / -type f -size +100M -delete
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>解决systemd-journald查看日志出现Missed  kernel messages</title>
      <link>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3missed--kernel-messages/</link>
      <pubDate>Sun, 15 Sep 2024 10:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3missed--kernel-messages/</guid>
      <description>&lt;p&gt;在使用 &lt;code&gt;systemd-journald&lt;/code&gt; 进行日志管理时，遇到内核日志输出过快导致日志丢失的情况时，一般是由于 &lt;code&gt;journald&lt;/code&gt; 的缓冲区设置过小，无法处理快速输出的内核消息。本文将介绍问题原因，并给出相应的解决方案。&lt;/p&gt;
&lt;h2 id=&#34;问题描述&#34;&gt;问题描述&lt;/h2&gt;
&lt;p&gt;当内核日志输出过快时，&lt;code&gt;systemd-journald&lt;/code&gt; 可能会因其内部缓冲区设置过小而无法处理所有日志，导致部分日志信息丢失。表现为系统日志中出现类似以下的错误：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Missed xxx kernel messages
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种情况通常出现在内核大量输出调试信息、系统初始化阶段日志频繁输出，或系统发生较大故障时。&lt;/p&gt;
&lt;h2 id=&#34;原因分析&#34;&gt;原因分析&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemd-journald&lt;/code&gt; 是一个处理系统日志的服务，包括内核日志。它通过监听 &lt;code&gt;/dev/kmsg&lt;/code&gt; 获取内核日志信息。journald 使用一个内部缓冲区来暂存日志，如果日志流速过快而 &lt;code&gt;journald&lt;/code&gt; 无法及时处理，这个缓冲区会被填满，导致消息溢出或丢失。&lt;/p&gt;
&lt;p&gt;默认情况下，&lt;code&gt;journald&lt;/code&gt; 的日志缓冲区大小较小，可能无法应对高频率的日志输出。此外，&lt;code&gt;journald&lt;/code&gt; 还可能受限于某些系统资源的配置，如最大消息大小、日志文件大小等。&lt;/p&gt;
&lt;h2 id=&#34;解决方案&#34;&gt;解决方案&lt;/h2&gt;
&lt;p&gt;我们可以通过以下几个步骤来缓解或解决内核日志丢失问题：&lt;/p&gt;
&lt;h3 id=&#34;1-增加-systemd-journald-缓存大小&#34;&gt;1. 增加 &lt;code&gt;systemd-journald&lt;/code&gt; 缓存大小&lt;/h3&gt;
&lt;p&gt;通过调整 &lt;code&gt;systemd-journald&lt;/code&gt; 的配置来增大其缓冲区，可以减少日志丢失的情况。&lt;/p&gt;
&lt;p&gt;编辑 &lt;code&gt;journald&lt;/code&gt; 的配置文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/systemd/journald.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;找到以下参数并进行调整：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# RuntimeMaxUse: 设置运行时日志文件的最大使用空间。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 可以将此值设为较大的值，例如 100M。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;RuntimeMaxUse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;100M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# SystemMaxUse: 设置系统日志文件的最大使用空间。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 可以将此值设为 500M。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;SystemMaxUse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;500M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# RateLimitInterval: 限制日志接收速率的时间间隔。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 可以适当延长此时间，例如 10s。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;RateLimitInterval&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;10s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# RateLimitBurst: 限制日志接收速率的最大突发数。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 增加突发限制，比如 10000。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;RateLimitBurst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;10000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;保存并关闭文件，然后重启 &lt;code&gt;journald&lt;/code&gt; 服务使配置生效：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl restart systemd-journald
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-调整内核日志环形缓冲区的大小&#34;&gt;2. 调整内核日志环形缓冲区的大小&lt;/h3&gt;
&lt;p&gt;内核日志存储在一个环形缓冲区中，该缓冲区的大小可以通过 &lt;code&gt;dmesg&lt;/code&gt; 命令查看和调整。默认的缓冲区可能较小，可以通过 &lt;code&gt;kernel&lt;/code&gt; 命令行参数 &lt;code&gt;log_buf_len&lt;/code&gt; 来调整。&lt;/p&gt;
&lt;p&gt;编辑 &lt;code&gt;grub&lt;/code&gt; 配置文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/default/grub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;GRUB_CMDLINE_LINUX_DEFAULT&lt;/code&gt; 行中添加如下参数：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;GRUB_CMDLINE_LINUX_DEFAULT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;log_buf_len=16M&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将内核日志缓冲区设置为 16M。保存并更新 grub：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo update-grub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后重启系统以应用更改：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo reboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-禁用日志速率限制&#34;&gt;3. 禁用日志速率限制&lt;/h3&gt;
&lt;p&gt;如果日志速率限制不符合当前系统需求，可以考虑禁用速率限制，尤其是在开发和调试环境中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/systemd/journald.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在配置文件中禁用日志速率限制：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;RateLimitInterval&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;RateLimitBurst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;保存文件并重启 &lt;code&gt;systemd-journald&lt;/code&gt; 服务：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl restart systemd-journald
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>在使用 <code>systemd-journald</code> 进行日志管理时，遇到内核日志输出过快导致日志丢失的情况时，一般是由于 <code>journald</code> 的缓冲区设置过小，无法处理快速输出的内核消息。本文将介绍问题原因，并给出相应的解决方案。</p>
<h2 id="问题描述">问题描述</h2>
<p>当内核日志输出过快时，<code>systemd-journald</code> 可能会因其内部缓冲区设置过小而无法处理所有日志，导致部分日志信息丢失。表现为系统日志中出现类似以下的错误：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Missed xxx kernel messages
</span></span></code></pre></div><p>这种情况通常出现在内核大量输出调试信息、系统初始化阶段日志频繁输出，或系统发生较大故障时。</p>
<h2 id="原因分析">原因分析</h2>
<p><code>systemd-journald</code> 是一个处理系统日志的服务，包括内核日志。它通过监听 <code>/dev/kmsg</code> 获取内核日志信息。journald 使用一个内部缓冲区来暂存日志，如果日志流速过快而 <code>journald</code> 无法及时处理，这个缓冲区会被填满，导致消息溢出或丢失。</p>
<p>默认情况下，<code>journald</code> 的日志缓冲区大小较小，可能无法应对高频率的日志输出。此外，<code>journald</code> 还可能受限于某些系统资源的配置，如最大消息大小、日志文件大小等。</p>
<h2 id="解决方案">解决方案</h2>
<p>我们可以通过以下几个步骤来缓解或解决内核日志丢失问题：</p>
<h3 id="1-增加-systemd-journald-缓存大小">1. 增加 <code>systemd-journald</code> 缓存大小</h3>
<p>通过调整 <code>systemd-journald</code> 的配置来增大其缓冲区，可以减少日志丢失的情况。</p>
<p>编辑 <code>journald</code> 的配置文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/systemd/journald.conf
</span></span></code></pre></div><p>找到以下参数并进行调整：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># RuntimeMaxUse: 设置运行时日志文件的最大使用空间。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 可以将此值设为较大的值，例如 100M。</span>
</span></span><span class="line"><span class="cl"><span class="na">RuntimeMaxUse</span><span class="o">=</span><span class="s">100M</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># SystemMaxUse: 设置系统日志文件的最大使用空间。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 可以将此值设为 500M。</span>
</span></span><span class="line"><span class="cl"><span class="na">SystemMaxUse</span><span class="o">=</span><span class="s">500M</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># RateLimitInterval: 限制日志接收速率的时间间隔。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 可以适当延长此时间，例如 10s。</span>
</span></span><span class="line"><span class="cl"><span class="na">RateLimitInterval</span><span class="o">=</span><span class="s">10s</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># RateLimitBurst: 限制日志接收速率的最大突发数。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 增加突发限制，比如 10000。</span>
</span></span><span class="line"><span class="cl"><span class="na">RateLimitBurst</span><span class="o">=</span><span class="s">10000</span>
</span></span></code></pre></div><p>保存并关闭文件，然后重启 <code>journald</code> 服务使配置生效：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl restart systemd-journald
</span></span></code></pre></div><h3 id="2-调整内核日志环形缓冲区的大小">2. 调整内核日志环形缓冲区的大小</h3>
<p>内核日志存储在一个环形缓冲区中，该缓冲区的大小可以通过 <code>dmesg</code> 命令查看和调整。默认的缓冲区可能较小，可以通过 <code>kernel</code> 命令行参数 <code>log_buf_len</code> 来调整。</p>
<p>编辑 <code>grub</code> 配置文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/default/grub
</span></span></code></pre></div><p>在 <code>GRUB_CMDLINE_LINUX_DEFAULT</code> 行中添加如下参数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">GRUB_CMDLINE_LINUX_DEFAULT</span><span class="o">=</span><span class="s2">&#34;log_buf_len=16M&#34;</span>
</span></span></code></pre></div><p>这将内核日志缓冲区设置为 16M。保存并更新 grub：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-grub
</span></span></code></pre></div><p>然后重启系统以应用更改：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo reboot
</span></span></code></pre></div><h3 id="3-禁用日志速率限制">3. 禁用日志速率限制</h3>
<p>如果日志速率限制不符合当前系统需求，可以考虑禁用速率限制，尤其是在开发和调试环境中：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/systemd/journald.conf
</span></span></code></pre></div><p>在配置文件中禁用日志速率限制：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">RateLimitInterval</span><span class="o">=</span><span class="s">0</span>
</span></span><span class="line"><span class="cl"><span class="na">RateLimitBurst</span><span class="o">=</span><span class="s">0</span>
</span></span></code></pre></div><p>保存文件并重启 <code>systemd-journald</code> 服务：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl restart systemd-journald
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>PCIe Part 2 - 关于内存的一切 MMIO DMA TLPs !</title>
      <link>https://lifeislife.cn/posts/%E8%AF%91%E6%96%87-pcie-part2/</link>
      <pubDate>Sat, 31 Aug 2024 17:00:13 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%AF%91%E6%96%87-pcie-part2/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本文翻译自：&lt;a href=&#34;https://ctf.re/kernel/pcie/tutorial/dma/mmio/tlp/2024/03/26/pcie-part-2/&#34;&gt;PCIe 第 2 部分 - 关于内存：MMIO、DMA、TLP 等！– 灵魂的逆向工程 &amp;mdash; PCIe Part 2 - All About Memory: MMIO, DMA, TLPs, and more! – Reversing Engineering for the Soul&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在这个系列文章的第一部分中，我们讨论了 ECAM 以及软件和硬件数据包网络中的配置空间访问。在讨论中，引入了 TLP（Transaction Layer Packets）的概念，这是所有 PCIe 数据在层次结构中移动的通用数据包结构。我们还讨论了这些数据包如何像以太网一样传输，即路由设备使用一个地址（在这种情况下是 BDF）来发送配置空间数据包穿过网络。&lt;/p&gt;
&lt;p&gt;配置空间读取和写入只是可以直接使用设备执行 I/O 的几种方式之一。通过“configuration”这个名称，我们可以知道很明显它的意图不是为了执行大量数据传输。主要缺点是它的速度，因为配置空间数据包最多只能包含双向读取或写入的 64 位数据（通常只有 32 位）。对于如此少量的可用数据，数据包和其他链路标头的开销非常大，因此浪费了带宽。&lt;/p&gt;
&lt;p&gt;正如第 1 部分所讨论的，理解内存和地址将继续是理解 PCIe 的关键。在这篇文章中，我们将更深入地研究更快的设备 I/O 事务形式，并开始了解软件设备驱动程序如何实际与 PCIe 设备连接以完成有用的工作。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：您无需成为计算机体系结构或 TCP/IP 网络方面的专家即可从这篇文章中获得一些信息。但是，了解 TCP/IP 和虚拟内存的基础知识对于掌握本文的一些核心概念是必要的。这篇文章也以 第 1 部分 中的信息为基础。如果您需要查看这些内容，请立即查看！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;pcie-中的数据传输方法简介&#34;&gt;PCIe 中的数据传输方法简介&lt;/h2&gt;
&lt;p&gt;配置空间是一种在枚举时间内通过其 BDF 与设备通信的一种简单而有效的方式。这是一种简单的传输模式是有原因的 - 它必须是配置和可用的所有其他数据传输方法的基础。枚举设备后，配置空间已设置设备与主机一起执行实际工作所需的所有信息。配置空间仍用于允许主机监控和响应设备及其链接状态的变化，但它不会用于执行设备的实际高速传输或功能。&lt;/p&gt;
&lt;p&gt;配置空间是在枚举时间通过设备的 BDF 进行通信的一种简单有效的方式。它是一种简单的传输模式，是所有数据传输方法的基础。一旦设备被枚举，配置空间就已经设置了设备执行实际工作所需的所有信息，与主机机器一起。配置空间仍用于允许主机计算机监视和响应设备及其链接的变化，但不会用于执行设备的实际高速传输或功能。&lt;/p&gt;
&lt;p&gt;我们现在需要的是数据传输方法，让我们真正开始利用 PCIe 设计的高速传输吞吐量。吞吐量是对给定时间段内传输的字节数的度量。这意味着为了最大限度地提高吞吐量，我们必须最小化每个数据包的开销，以传输每个数据包的最大字节数。如果我们每个数据包只发送几个 DWORD（每个 4 字节），就像在配置空间的情况下一样，PCIe 高速传输能力就浪费了。&lt;/p&gt;
&lt;p&gt;废话不多说，先介绍一下 PCIe 中高速 I/O 的两种主要形式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内存映射输入/输出（简称 MMIO）- 与主机 CPU 读取和写入内存到 ECAM 以执行配置空间访问类似，MMIO 可以用来映射设备的地址空间，以执行内存传输。主机机器在其物理地址空间中配置“内存窗口”，使 CPU 拥有一个内存地址窗口，这些内存地址神奇地转换为直接读取和写入设备。内存窗口在 RC 中解码，将 CPU 的读取和写入转换为传输到设备的数据 TLPs。硬件优化使得这种方法可以实现比配置空间访问快得多的吞吐量。然而，其速度仍然远远落后于 DMA 的批量传输速度。&lt;/li&gt;
&lt;li&gt;直接内存访问（简称 DMA）- DMA 是迄今为止最常见的数据传输形式，因为它具有原始传输速度和低延迟。每当驱动程序需要在主机和设备之间沿任一方向进行任何重要大小的传输时，它肯定会是 DMA。但与 MMIO 不同的是，DMA 是由设备本身启动的，而不是由主机 CPU 启动的。主机 CPU 将通过 MMIO 告诉设备 DMA 应该去哪里，设备本身负责开始和完成 DMA 传输。这允许设备在没有 CPU 参与的情况下执行 DMA 事务，与设备必须等待主机 CPU 告诉它每次传输做什么相比，这节省了大量的 CPU 周期。由于 DMA 的普遍性和重要性，从硬件实现和软件层面了解 DMA 非常有价值。&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//2024/08/31/ba1527f1a6204106c586315818355039.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/ba1527f1a6204106c586315818355039.png&#34; alt=&#34;MMIO 方法概述&#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//2024/08/31/a3c6fbf5ac22aed25da2b62c20ecb3f3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/a3c6fbf5ac22aed25da2b62c20ecb3f3.png&#34; alt=&#34;从器件到 RAM 执行 DMA 的高级概述。当传输到 RAM 完成时，设备会中断 CPU。&#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;h2 id=&#34;mmio-简介&#34;&gt;MMIO 简介&lt;/h2&gt;
&lt;h3 id=&#34;什么是-bar&#34;&gt;什么是 BAR？&lt;/h3&gt;
&lt;p&gt;由于配置空间限制为 4096 字节，因此之后没有太多可用空间用于特定于设备的功能。如果设备想要映射 1GB 的 MMIO 空间来访问其内部 RAM，该怎么办？没有办法将其放入 4096 字节的配置空间。因此，它将需要请求一个被称为 BAR（基地址寄存器）的东西。这是通过配置空间公开的一个寄存器，允许主机机器配置其内存的一个区域，直接映射到设备上。然后主机机器上的软件通过对 BAR 的物理地址的内存读/写指令来访问 BAR，就像我们在 ECAM 的第一部分中看到的 MMIO 一样。对设备内存映射进行读取或写入的操作将直接转换为发送到层次结构上的设备的数据包。当设备需要响应时，它将通过层次结构向主机机器发送一个新的数据包。&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//2024/08/31/333e258e5613b3c17bb19e1b5bcbf87b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/333e258e5613b3c17bb19e1b5bcbf87b.png&#34; alt=&#34;在主机上运行的设备驱动程序访问 BAR 映射，这些映射转换为通过 PCIe 发送到设备的数据包。&#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 指令读取器件 MMIO 区域的内存时，会生成一个内存读取请求事务层数据包（MemRd TLP），该数据包从主机的 RC 向下传输到器件。这个 TLP 包的目的是通知设备希望读取设备，然后设备需要尽快响应请求地址上的内容。&lt;/p&gt;
&lt;p&gt;在 PCIe 中发送和接收的所有数据传输数据包都将采用 TLP 形式。回想一下第 1 部分，这些数据包是设备之间的所有通信都在 PCIe 中发生的中心抽象。这些数据包在出现数据传输错误（类似于网络中的 TCP）的情况下是可靠的，并且可以根据需要重试/重新发送。这确保了数据传输免受 PCIe 可以达到的极高速度下发生的恶劣电气干扰。我们很快就会仔细研究 TLP 的结构，但现在只需将这些视为您在 TCP 中看到的常规网络数据包。&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//2024/08/31/1167d45f8d32476dcb144eee4e65634e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/1167d45f8d32476dcb144eee4e65634e.png&#34; alt=&#34;当器件响应时，CPU 会使用器件的结果更新 register 的内容。&#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;当设备收到请求者数据包时，设备会使用 MemRd TLP 响应内存请求。此 TLP 包含从设备内存空间读取的结果，给定原始请求者数据包中的地址和大小。设备将它正在响应的特定请求数据包和发送方标记到响应数据包中，交换层次结构知道如何将响应数据包返回给请求者。然后，请求者将使用数据包中的数据来更新发起请求的 CPU 寄存器。&lt;/p&gt;
&lt;p&gt;同时，当 TLP 正在传输时，CPU 必须等待内存请求完成，并且它不能被中断或执行许多有用的工作。正如你可能看到的，如果需要执行大量这样的请求，CPU 将需要花费大量时间等待设备响应每个请求。虽然在硬件级别进行了优化，使此过程更加简化，但使用 CPU 周期等待数据传输完成仍然不是最佳选择。希望您能看到我们需要第二种类型的传输，即 DMA，来解决 BAR 访问的这些缺点。&lt;/p&gt;
&lt;p&gt;这里的另一个重点是，设备内存并不严格需要用于设备的 RAM。虽然通常会看到具有板载 RAM 的设备通过 BAR 公开其内部 RAM 的映射，但这不是必需的。例如，访问设备的 BAR 可能会访问设备的内部寄存器，也可能导致设备执行某些操作。例如，写入 BAR 是设备开始执行 DMA 的主要方式。一个核心要点是，设备 BAR 非常灵活，可用于控制设备或执行与设备之间的数据传输。&lt;/p&gt;
&lt;h3 id=&#34;如何枚举-bar&#34;&gt;如何枚举 BAR？&lt;/h3&gt;
&lt;p&gt;设备使用其配置空间从软件请求内存区域。在枚举时，由主机确定该区域将放置在物理内存中的位置。每个器件在其配置空间（称为“寄存器”，因此称为基址寄存器）中都有 6 个 32 位值，当枚举器件时，软件将读取和写入这些值。这些寄存器描述了器件希望分配的每个 MMIO 区域的长度和对齐要求，每个可能的 BAR 一个，总共 6 个不同的区域。如果设备希望能够将其 BAR 映射到 4GB 空间（64 位 BAR）以上，它可以将两个 32 位寄存器组合在一起，形成一个 64 位 BAR，最多只留下三个 64 位 BAR。&lt;/p&gt;
&lt;p&gt;PCIe&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//2024/08/31/037c6e9bb852b45b9e12c1a046ceec59.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/037c6e9bb852b45b9e12c1a046ceec59.png&#34; alt=&#34;Type 0 配置空间结构，显示 6 个 BAR。&#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;p&gt;术语注释：尽管首字母缩略词 BAR 表示基址寄存器，但你会看到上面的文本也将 MMIO 的内存窗口称为 BAR。不幸的是，这意味着配置空间中的寄存器名称也与给 device 的 MMIO 区域相同（两者都称为 BAR）。你可能需要根据上下文，以确定它们是指内存窗口，还是配置空间本身的实际寄存器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;BARs 是配置空间中另一个示例，它不是常量寄存器。在第一部分中，我们看了一些常量寄存器，比如 VendorID 和 DeviceID。但是 BARs 不是常量寄存器，它们应该由软件写入和读取。实际上，由软件写入寄存器的值是特殊的，因为将某些类型的值写入寄存器将导致读取时功能不同。如果你没有牢记设备内存并非总是 RAM，读取回来的值可能与写入的不同，那么现在正是时候这么做了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;设备内存可以是 RAM，但它并不总是 RAM，也不需要像 RAM 那样工作！&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;什么是-dma引言和理论&#34;&gt;什么是 DMA？引言和理论&lt;/h2&gt;
&lt;p&gt;到目前为止，我们已经看到了两种形式的 I/O，配置空间访问和通过 BAR 的 MMIO 访问。我们将讨论的最后一种也是最后一种访问形式是直接内存访问（DMA）。DMA 是迄今为止最快的 PCIe 批量传输方法，因为它的传输开销最小。也就是说，通过链路传输最大字节数所需的资源最少。这使得 DMA 对于真正利用 PCIe 提供的高速链路至关重要。&lt;/p&gt;
&lt;p&gt;但是，强大的力量会带来巨大的混乱。对于软件开发人员来说，DMA 是一个非常陌生的概念，因为我们在软件中没有类似的东西可以比较。对于 MMIO，我们可以将内存访问概念化为从设备内存中读取和写入的指令。但 DMA 与此非常不同。这是因为 DMA 是异步的，它不利用 CPU 来执行传输。相反，顾名思义，读取和写入的内存直接来自系统 RAM。一旦 DMA 开始，唯一涉及的各方是系统主内存的内存控制器和设备本身。因此，CPU 不会花费周期等待单个内存访问。相反，它只是启动转移，并让平台在后台自行完成 DMA。然后，平台将在传输完成时通知 CPU，通常是通过中断。&lt;/p&gt;
&lt;p&gt;让我们想一想，为什么异步执行 DMA 如此重要。考虑 CPU 从计算机上的 NVMe SSD 解密大量文件的情况。一旦主机上的 NVMe 驱动程序启动 DMA，设备就会不断以最快的速度将文件数据从 SSD 的内部存储传输到 CPU 可以访问的系统 RAM 中的位置。然后，CPU 可以使用其 100% 的处理能力来执行解密数学运算，以便在从系统内存中读取数据时解密文件块。CPU 不会花时间等待对设备进行单个内存读取，而是简单地连接数据，并允许设备尽可能快地传输，而 CPU 会尽可能快地处理它。在此期间，任何额外的数据都会在系统 RAM 中缓冲，直到 CPU 可以访问它。这样，任何过程的任何部分都不会等待其他事情发生。所有这些都以尽可能快的速度同时发生。&lt;/p&gt;
&lt;p&gt;由于 DMA 的复杂性和涉及的部件数量，我将尝试以最直接的方式解释 DMA，并用大量图表来显示该过程。更令人困惑的是，每个设备都有不同的 DMA 接口。没有用于执行 DMA 的通用软件接口，只有器件的设计人员知道如何告诉该器件执行 DMA。值得庆幸的是，某些设备类别使用普遍认可的接口，例如大多数 SSD 使用的 NVMe 接口或 USB 3.0 的 XHCI 接口。如果没有标准接口，则只有硬件设计人员知道设备如何执行 DMA，因此生产设备的公司或个人需要是编写设备驱动程序的人，而不是依赖与操作系统捆绑的通用驱动程序与设备通信。&lt;/p&gt;
&lt;h2 id=&#34;一个简单的-dma-传输---step-by-step&#34;&gt;一个简单的 DMA 传输 - Step by Step&lt;/h2&gt;
&lt;p&gt;我们 DMA 旅程的第一步是查看传输的初始设置。这涉及几个步骤，为即将到来的 DMA 传输准备系统内存、内核和设备。在这种情况下，我们将设置 DMA，以便读取系统 RAM 中存在的 DMA 缓冲区中的内存内容，并将其放入 Target Memory 的器件板载 RAM 中。此时我们已经选择将此内存从 DMA Buffer 读取到器件上地址为 &lt;code&gt;0x8000&lt;/code&gt; 中。目标是尽快将此内存从系统内存传输到设备，以便它可以开始处理它。假设在这种情况下，内存量几 M 字节，MMIO 会太慢，但为简单起见，我们将仅显示 32 字节的内存。这种传输将是最简单的 DMA 传输类型：将内存块的已知大小和地址从系统 RAM 复制到设备 RAM。&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//2024/08/31/e485c2c5093a3098acb5d4fc616de33c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/e485c2c5093a3098acb5d4fc616de33c.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;第-1-步---从操作系统分配-dma-内存&#34;&gt;第 1 步 - 从操作系统分配 DMA 内存&lt;/h3&gt;
&lt;p&gt;此过程的第一步是从 OS 分配 DMA 内存。这意味着设备驱动程序必须进行 OS API 调用，以请求 OS 为设备分配一个内存区域以将数据写入。这一点很重要，因为操作系统可能需要执行特殊的内存管理操作才能使数据对设备可用，例如删除保护或重新组织现有分配以促进请求。&lt;/p&gt;
&lt;p&gt;传统上，DMA（直接内存访问）存储器必须是连续的物理存储器，这意味着设备从某个地址和长度的起始处开始，并线性地从缓冲区的起始位置读取/写入数据直到结束。因此，操作系统必须负责组织其物理内存，以创建足够大的连续范围，以满足驱动程序请求的 DMA 缓冲区。有时，对于长时间运行或物理内存有限的系统来说，这可能非常困难。因此，这一领域的增强功能允许更现代的设备使用 Scatter-Gather 和 IOMMU Remapping 等功能传输到非连续的存储器区域。稍后，我们将看一些这些功能。但现在，我们只专注于更简单的连续内存情况。&lt;/p&gt;
&lt;p&gt;一旦请求的分配成功，API 将返回内存地址，并指向系统 RAM 中的缓冲区。这将是设备通过 DMA 访问内存的地址。DMA 意图为 API 返回的地址将被赋予一个特殊的名称; 设备逻辑地址或逻辑地址。对于我们的示例，逻辑地址等同于物理地址。设备看到的是操作系统看到的物理内存的完全相同视图，没有额外的转换。然而，在更高级的传输形式中，情况可能并非总是如此。因此最好意识到，设备给出的地址可能并非总是与其在 RAM 中实际的物理地址相同。&lt;/p&gt;
&lt;p&gt;分配缓冲区后，由于目的是将数据从此缓冲区移动到设备，因此设备驱动程序将提前使用写入设备所需的信息填充缓冲区。在此示例中，由重复 01 02 03 04 模式组成的数据正在传输到设备的 RAM。&lt;/p&gt;
&lt;h3 id=&#34;第-2-步---将-dma-地址写入设备并开始传输&#34;&gt;第 2 步 - 将 DMA 地址写入设备并开始传输&lt;/h3&gt;
&lt;p&gt;传输的下一步是准备设备执行事务所需的信息。这通常是了解器件的特定 DMA 接口最重要的地方。每个设备都以自己的方式进行编程，了解驱动程序应该如何对设备进行编程的唯一方法是参考其通用标准（如 NVMe 规范）或简单地与硬件设计人员合作。&lt;/p&gt;
&lt;p&gt;在这个例子中，我将为一个只有执行传输所需的最基本功能的设备构建一个简化的 DMA 接口。在下面的图表中，我们可以看到这个设备通过向 BAR0 MMIO 区域写入数值来进行编程。这意味着为了为这个设备编程 DMA，驱动程序必须将内存写入由 BAR0 指定的 MMIO 区域。每个寄存器在 BAR0 区域内的位置是由驱动程序编写者提前知道的，并且被集成到设备驱动程序的代码中。&lt;/p&gt;
&lt;p&gt;对于此示例、我在 BAR0 中创建了四个器件寄存器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Destination Address（目标地址） - 设备内部 RAM 中用于写入从系统 RAM 读取的数据的地址。这是我们将对已经确定的目标地址 0x8000 进行编程的地方。&lt;/li&gt;
&lt;li&gt;Source Address（源地址） - 设备将从中读取数据的系统 RAM 的逻辑地址。这将对我们希望设备读取的 DMA Buffer 的逻辑地址进行编程。&lt;/li&gt;
&lt;li&gt;Transfer Size（传输大小） - 我们要传输的大小（以字节为单位）。&lt;/li&gt;
&lt;li&gt;Initiate Transfer（启动传输标志位）- 一旦次寄存器写入 1，器件将开始在上面给出的地址之间进行传输。通过这种方式，驱动程序可以判断设备已完成填充缓冲区并准备好开始传输。这通常被称为门铃（Door Bell）寄存器。&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//2024/08/31/8c2b333dbb59e8bb8d2a92ae5766299e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8c2b333dbb59e8bb8d2a92ae5766299e.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;在上图中，驱动程序需要使用器件的 BAR0 的映射内存将必要的值写入寄存器（它如何映射此内存取决于 OS）。此图中的值如下所示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Target Memory - 我们要从器件复制的内存将为 &lt;code&gt;0x00008000&lt;/code&gt;，它映射到器件板载 RAM 中的内存区域。这将是我们的目标地址。&lt;/li&gt;
&lt;li&gt;DMA 缓冲区 - 操作系统在 &lt;code&gt;0x001FF000&lt;/code&gt; 分配内存块，因此这将是我们的源地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&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//2024/08/31/d481c375d9d43db63faf509106fd6dcd.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/d481c375d9d43db63faf509106fd6dcd.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;现在，驱动程序已经配置了执行传输所需的所有寄存器。最后一步是向启动传输寄存器写入一个值，该寄存器充当开始传输的 Door bell 寄存器。一旦写入此值，设备将驱动 DMA 传输，并独立于驱动程序或 CPU 的参与执行它。驱动程序已完成启动传输的工作，此时 CPU 可在等待设备通知系统 DMA 完成的同时，自由进行其他工作。&lt;/p&gt;
&lt;h3 id=&#34;第-3-步---设备执行-dma-事务&#34;&gt;第 3 步 - 设备执行 DMA 事务&lt;/h3&gt;
&lt;p&gt;现在，驱动程序已写入 Door bell 寄存器，设备现在将接管处理实际传输。在设备本身上，存在一个名为 DMA 引擎的模块，负责处理和维护事务的所有方面。当器件被编程时，对 BAR0 的寄存器写入正在对 DMA 引擎进行编程，其中包含开始在 PCIe 链路上发送必要的 TLP 以执行内存事务所需的信息。&lt;/p&gt;
&lt;p&gt;如上一节所述，PCIe 链路上的所有内存操作都是通过 Memory Write/Read TLP 完成的。在这里，我们将深入研究在交易发生时设备的 DMA 引擎发送和接收的 TLP。请记住，更容易将 TLP 视为在单个可靠连接上发送和接收数据的网络数据包。&lt;/p&gt;
&lt;p&gt;如在前文部分讨论过的，PCIe 链接上的所有内存操作都是通过内存写入/读取 TLPs 来完成的。在这里，我们将深入探讨设备的 DMA 引擎在传输过程中发送和接收的 TLPs。要牢记的是，将 TLPs 视为在一个单一、可靠的连接上发送和接收数据的网络数据包更易于理解。&lt;/p&gt;
&lt;h3 id=&#34;插曲快速了解-tlp&#34;&gt;插曲：快速了解 TLP&lt;/h3&gt;
&lt;p&gt;在查看链路上的 TLP 之前，让我们仔细了解一下数据包结构本身的概览。&lt;/p&gt;
&lt;p&gt;以下是内存读取请求和响应的两个 TLP。如前所述，用于内存操作的 TLP 利用请求和响应系统。执行读取的设备将生成特定地址和长度（以 4 字节 DWORD 为单位）的读取请求 TLP，然后坐下来等待完成数据包到达包含响应数据的链路。&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//2024/08/31/8354d9b795fe77dfbf296da3509b32b7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8354d9b795fe77dfbf296da3509b32b7.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;我们可以看到，有与生成请求的设备、Requester 以及唯一的 Tag 值相关的元数据。此 Tag 值用于将请求与其完成匹配。当设备生成请求时，它会使用唯一值标记 TLP 以跟踪待处理的请求。该值由请求的发送者选择，并且由发送者来跟踪其分配的 Tags。&lt;/p&gt;
&lt;p&gt;随着完成的数据通过链路到达，完成的 Tag 让设备能够将接入的数据正确地移至特定传输所需的位置。该系统允许单一设备有多个独特的未完成传输任务，尽管它们接收的数据包相互交错，但仍能保持作为独立传输任务的有序性。&lt;/p&gt;
&lt;p&gt;数据包内部还包含了必要的信息，使得 PCIe 切换层次结构能够确定请求和完成需要去向的位置。例如，内存地址被用来确定正在请求访问的设备是哪一个。在枚举期间，层次结构中的每一个设备都被编程以拥有各自独特的地址范围。切换结构根据数据包中的内存地址，确定数据包需要去向哪里以访问那个地址。&lt;/p&gt;
&lt;p&gt;设备收到并处理请求后，响应数据将以 Completion TLP 的形式发送回去。完成或“响应”数据包可以而且通常会被分段为许多较小的 TLP，这些 TLP 发送整体响应的一部分。这是因为在枚举期间，已确定设备和总线可以处理最大有效载荷大小（MPS）。MPS 可根据平台和设备功能进行配置，从 128 开始，最高可达 4096 的 2 次方大小。通常，此值约为 256 字节，这意味着需要将大型读取请求拆分为许多较小的 TLP。这些数据包中的每一个都有一个字段，该字段指示完成响应的原始请求的偏移量，有效负载中是返回的数据块。&lt;/p&gt;
&lt;p&gt;有一个常见的误解，即内存 TLP 使用 BDF 来寻址数据包需要去往的位置。其实请求仅使用内存地址来指示数据包的目的地，并且设备和目标之间的桥接设备负责将该数据包发送到其正确的位置。。然而，完成数据包确实利用请求者的 BDF 将数据返回给发起请求的设备。&lt;/p&gt;
&lt;p&gt;以下是一个展示内存读取和响应过程的图表，图中展示出请求会使用一个地址来发起请求，而完成的操作会使用请求中 Request 字段的 BDF 来发送相应应答：&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//2024/08/31/7b3a9443c759cbf8fe091903bb602eaf.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7b3a9443c759cbf8fe091903bb602eaf.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//2024/08/31/39cc0bf0067bf486aaa2062470c8e45e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/39cc0bf0067bf486aaa2062470c8e45e.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;让我们看看 DMA 引擎为了执行我们的请求而发送和接收的所有内容。由于我们请求了 32 字节的数据，因此只有一个单一的 Memory Read Request 和一个带有响应的单一 Memory Read Completion 数据包。为了便于您理解，请停止向前阅读，并考虑一下在此事务中哪个设备将发送和接收哪个 TLP。如果您需要再次查看第 2 步的图表，请向上滚动。&lt;/p&gt;
&lt;p&gt;现在，让我们深入研究一下传输的实际数据包。虽然我将继续绘制这个模拟示例，但我认为对于这个练习，当执行真实传输时，实际看到其中一些 TLP 是什么样子可能会很有趣。&lt;/p&gt;
&lt;p&gt;在实验中，我使用真实设备配置了如上文所示的同类通用参数，并启动了 DMA。这个设备会发送真实的 TLPs，将系统 RAM 中的内存读取到设备里。因此，你将有机会罕见地查看在执行这种 DMA 时发送的实际 TLPs 的例子，除非有分析器，否则这些在传输过程中几乎无法看到。&lt;/p&gt;
&lt;p&gt;要查看此实验，请点击此链接至配套文章：&lt;a href=&#34;https://ctf.re/pcie/experiment/linux/keysight/protocol-analyzer/2024/03/26/pcie-experiment-1/&#34;&gt;Experiment - Packet Dumping PCIe DMA TLPs with a Protocol Analyzer and Pcileech – Reversing Engineering for the Soul&lt;/a&gt;&lt;/p&gt;
&lt;p&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//2024/08/31/d5ceb838e8b89196864f787a56d02197.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/d5ceb838e8b89196864f787a56d02197.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;p&gt;勘误表：0x32 应该是 32&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此图中概述的步骤如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DMA 引擎创建 TLP - DMA 引擎识别出它必须从 0x001FF000 读取 32 字节。它生成一个包含此请求的 TLP，并通过其本地 PCIe 链路将其发送出去。&lt;/li&gt;
&lt;li&gt;TLP 遍历层次结构 - PCIe 的交换层次结构通过桥接设备移动此请求，直到它到达其目的地，即 RC。回想一下，RC 负责处理所有用于访问系统 RAM 的传入数据包。&lt;/li&gt;
&lt;li&gt;通知 DRAM 控制器 - RC 在内部与 DRAM 控制器通信，该控制器负责实际访问系统 DRAM 的内存。&lt;/li&gt;
&lt;li&gt;从 DRAM 读取内存 - 从地址 0x001FF000 的 DRAM 请求给定长度的 32 字节，并将其返回到值为 01 02 03 04&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;尽量不要被这些信息淹没，因为我确实知道仅针对单个内存请求 TLP 就有很多事情要做。所有这些在高层次上归结为仅从 RAM 中的地址 0x001FF000 读取 32 字节的内存。平台如何通过与 DRAM 控制器通信来实际读取系统 DRAM，仅供您参考。设备本身不知道 Root Complex 实际上是如何读取此内存的，它只是使用 TLP 启动传输。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：此处未显示更复杂的 RAM 缓存过程。在 x86-64 上，来自设备的所有内存访问都是缓存一致的，这意味着平台会自动将 CPU 缓存与设备正在访问的值同步。在其他平台（如 ARM 平台）上，由于其缓存架构，这是一个更复杂的过程。现在，我们只假设缓存一致性正在自动为我们处理，我们对此没有任何特别的担忧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当 RC 收到此 TLP 时，它会在内部标记 Requester 和 Tag 的读取内容。当它等待 DRAM 响应该值时，此请求的信息将在 RC 中等待。要概念化这一点，可以将其视为网络套接字中的“open connection”。RC 知道它需要响应什么，因此会等到响应数据可用后，再通过套接字将数据发送回去。&lt;/p&gt;
&lt;p&gt;最后，将 Completion 从 Root Complex 发送回设备。请注意，Destination 与 Requester 相同：&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//2024/08/31/e4d729f807a08b1dab3dddb0f10c88b2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/e4d729f807a08b1dab3dddb0f10c88b2.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;从 DRAM 读取内存 - DRAM 控制器从系统 DRAM 中 0x001FF000 的 DMA 缓冲区地址读取 32 字节。&lt;/li&gt;
&lt;li&gt;DRAM 控制器响应根复合体 - DRAM 控制器在内部响应从 DRAM 向 RC 请求的内存&lt;/li&gt;
&lt;li&gt;RC 生成完成 - RC 跟踪传输并为从 DRAM 读取的值创建完成 TLP。在此 TLP 中，元数据值是根据 RC 对待处理传输的了解来设置的，例如发送的字节数、传输的标记以及从原始请求的 Requester 字段复制的目标 BDF。&lt;/li&gt;
&lt;li&gt;DMA 引擎接收 TLP - DMA 引擎通过 PCIe 链路接收 TLP，并查看标签是否与原始请求的相同标签匹配。它还会在内部跟踪此值，并知道有效负载中的内存应写入 Target Memory，该内存在设备内部 RAM 中处于 0x8000。&lt;/li&gt;
&lt;li&gt;Target Memory is Written（目标内存已写入） - 设备内存中的值将更新为从数据包的 Payload 中复制的值。&lt;/li&gt;
&lt;li&gt;系统中断 - 虽然这是可选的，但大多数 DMA 引擎将配置为在 DMA 完成时中断主机 CPU。这会在设备成功完成 DMA 时向设备驱动程序发出通知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同样，仅处理这个 complete 数据包就涉及很多步骤。但是，同样，您可以将整个过程简单地视为“从设备的请求中收到 32 字节的响应”。这些步骤的其余部分只是为了向您展示此响应处理的完整端到端是什么样子。&lt;/p&gt;
&lt;p&gt;从这里，设备驱动程序会收到 DMA 已完成的通知，设备驱动程序的代码负责清理 DMA 缓冲区或将其存储起来以供下次使用。&lt;/p&gt;
&lt;p&gt;在我们的艰苦努力后，我们终于完成了一次单一的 DMA 传输事务！想到这就是我能提供的最“简单”的传输方式，真是让人惊讶。有了 IOMMU 重映射和 Scatter-Gather 能力的加入，这些事务甚至可能变得更复杂。但就现在而言，你应该对 DMA 的全部内容以及它如何在真实设备中运作有了深入的理解。&lt;/p&gt;
&lt;h2 id=&#34;尾声---关于复杂性的小说明&#34;&gt;尾声 - 关于复杂性的小说明&lt;/h2&gt;
&lt;p&gt;如果您读完这篇文章并觉得自己没有完全掌握所有抛给您的概念，或者对复杂性感到不知所措，您不必担心。这些帖子如此复杂的原因是它不仅涵盖广泛的主题，而且还涵盖广泛的专业。通常，整个系统的每个部分在行业中都有不同的团队，他们只关注他们在这个复杂机器中的“齿轮”。通常，硬件开发人员专注于设备，驱动程序开发人员专注于驱动程序代码，而操作系统开发人员专注于资源管理。这些团队之间很少有太多重叠，除非在他们的边界交接，以便另一支团队可以连接到它。&lt;/p&gt;
&lt;p&gt;这些帖子有点独特，因为它们试图将系统作为一个整体进行记录，以便于概念理解，而不是实现。这意味着，在通常划定团队边界的地方，这些帖子根本不关心。我鼓励觉得这个话题有趣的读者在自己的时间里继续深入研究。也许您可以了解一些 FPGA 并开始制作自己的设备，或者您可以购买一个设备并开始尝试对其进行逆向工程，并通过您自己的定制软件与它进行通信。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;希望您喜欢这篇深入探讨 PCIe 内存传输的文章！虽然我在这篇文章中涵盖了大量信息，但兔子洞总是更深。值得庆幸的是，通过学习配置空间访问、MMIO（BAR）和 DMA，您现在已经涵盖了 PCIe 中可用的各种形式的数据通信！对于连接到 PCIe 总线的每个设备，主机系统和设备之间的通信将通过这三种方法中的一种进行。设备的链接、资源和驱动程序软件的所有设置和配置最终都是为了促进这三种形式的通信。&lt;/p&gt;
&lt;p&gt;这篇文章花了这么长时间才发布的一个重要原因是，为了理解这一切，我必须向读者展示大量信息。很难决定什么值得写，什么太深以至于理解变得模糊。这个决定瘫痪使博客写作过程花费的时间比我预期的要长得多。再加上全职工作，很难找到时间来撰写这些帖子。&lt;/p&gt;
&lt;p&gt;在即将发布的帖子中，我期待着讨论以下一些或所有的话题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;层次结构的 PCIe 交换/桥接和枚举&lt;/li&gt;
&lt;li&gt;更高级的 DMA 主题，例如 DMA 重新映射&lt;/li&gt;
&lt;li&gt;电源管理;设备如何“睡眠”和“唤醒”&lt;/li&gt;
&lt;li&gt;平台/OS 的中断及其分配和处理&lt;/li&gt;
&lt;li&gt;设备的简单驱动程序开发示例&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;与往常一样，如果你有任何问题或想评论或讨论这个系列的某个方面，你最好在我的 discord 的 #hardware 频道中通过“@gbps”找到我，逆向工程 &lt;a href=&#34;https://discord.com/invite/rtfm&#34;&gt;discord&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;请期待未来的帖子！&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<blockquote>
<p>本文翻译自：<a href="https://ctf.re/kernel/pcie/tutorial/dma/mmio/tlp/2024/03/26/pcie-part-2/">PCIe 第 2 部分 - 关于内存：MMIO、DMA、TLP 等！– 灵魂的逆向工程 &mdash; PCIe Part 2 - All About Memory: MMIO, DMA, TLPs, and more! – Reversing Engineering for the Soul</a></p>
</blockquote>
<p>在这个系列文章的第一部分中，我们讨论了 ECAM 以及软件和硬件数据包网络中的配置空间访问。在讨论中，引入了 TLP（Transaction Layer Packets）的概念，这是所有 PCIe 数据在层次结构中移动的通用数据包结构。我们还讨论了这些数据包如何像以太网一样传输，即路由设备使用一个地址（在这种情况下是 BDF）来发送配置空间数据包穿过网络。</p>
<p>配置空间读取和写入只是可以直接使用设备执行 I/O 的几种方式之一。通过“configuration”这个名称，我们可以知道很明显它的意图不是为了执行大量数据传输。主要缺点是它的速度，因为配置空间数据包最多只能包含双向读取或写入的 64 位数据（通常只有 32 位）。对于如此少量的可用数据，数据包和其他链路标头的开销非常大，因此浪费了带宽。</p>
<p>正如第 1 部分所讨论的，理解内存和地址将继续是理解 PCIe 的关键。在这篇文章中，我们将更深入地研究更快的设备 I/O 事务形式，并开始了解软件设备驱动程序如何实际与 PCIe 设备连接以完成有用的工作。</p>
<blockquote>
<p>注意：您无需成为计算机体系结构或 TCP/IP 网络方面的专家即可从这篇文章中获得一些信息。但是，了解 TCP/IP 和虚拟内存的基础知识对于掌握本文的一些核心概念是必要的。这篇文章也以 第 1 部分 中的信息为基础。如果您需要查看这些内容，请立即查看！</p>
</blockquote>
<h2 id="pcie-中的数据传输方法简介">PCIe 中的数据传输方法简介</h2>
<p>配置空间是一种在枚举时间内通过其 BDF 与设备通信的一种简单而有效的方式。这是一种简单的传输模式是有原因的 - 它必须是配置和可用的所有其他数据传输方法的基础。枚举设备后，配置空间已设置设备与主机一起执行实际工作所需的所有信息。配置空间仍用于允许主机监控和响应设备及其链接状态的变化，但它不会用于执行设备的实际高速传输或功能。</p>
<p>配置空间是在枚举时间通过设备的 BDF 进行通信的一种简单有效的方式。它是一种简单的传输模式，是所有数据传输方法的基础。一旦设备被枚举，配置空间就已经设置了设备执行实际工作所需的所有信息，与主机机器一起。配置空间仍用于允许主机计算机监视和响应设备及其链接的变化，但不会用于执行设备的实际高速传输或功能。</p>
<p>我们现在需要的是数据传输方法，让我们真正开始利用 PCIe 设计的高速传输吞吐量。吞吐量是对给定时间段内传输的字节数的度量。这意味着为了最大限度地提高吞吐量，我们必须最小化每个数据包的开销，以传输每个数据包的最大字节数。如果我们每个数据包只发送几个 DWORD（每个 4 字节），就像在配置空间的情况下一样，PCIe 高速传输能力就浪费了。</p>
<p>废话不多说，先介绍一下 PCIe 中高速 I/O 的两种主要形式：</p>
<ul>
<li>内存映射输入/输出（简称 MMIO）- 与主机 CPU 读取和写入内存到 ECAM 以执行配置空间访问类似，MMIO 可以用来映射设备的地址空间，以执行内存传输。主机机器在其物理地址空间中配置“内存窗口”，使 CPU 拥有一个内存地址窗口，这些内存地址神奇地转换为直接读取和写入设备。内存窗口在 RC 中解码，将 CPU 的读取和写入转换为传输到设备的数据 TLPs。硬件优化使得这种方法可以实现比配置空间访问快得多的吞吐量。然而，其速度仍然远远落后于 DMA 的批量传输速度。</li>
<li>直接内存访问（简称 DMA）- DMA 是迄今为止最常见的数据传输形式，因为它具有原始传输速度和低延迟。每当驱动程序需要在主机和设备之间沿任一方向进行任何重要大小的传输时，它肯定会是 DMA。但与 MMIO 不同的是，DMA 是由设备本身启动的，而不是由主机 CPU 启动的。主机 CPU 将通过 MMIO 告诉设备 DMA 应该去哪里，设备本身负责开始和完成 DMA 传输。这允许设备在没有 CPU 参与的情况下执行 DMA 事务，与设备必须等待主机 CPU 告诉它每次传输做什么相比，这节省了大量的 CPU 周期。由于 DMA 的普遍性和重要性，从硬件实现和软件层面了解 DMA 非常有价值。</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//2024/08/31/ba1527f1a6204106c586315818355039.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/ba1527f1a6204106c586315818355039.png" alt="MMIO 方法概述"  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//2024/08/31/a3c6fbf5ac22aed25da2b62c20ecb3f3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/a3c6fbf5ac22aed25da2b62c20ecb3f3.png" alt="从器件到 RAM 执行 DMA 的高级概述。当传输到 RAM 完成时，设备会中断 CPU。"  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>
<h2 id="mmio-简介">MMIO 简介</h2>
<h3 id="什么是-bar">什么是 BAR？</h3>
<p>由于配置空间限制为 4096 字节，因此之后没有太多可用空间用于特定于设备的功能。如果设备想要映射 1GB 的 MMIO 空间来访问其内部 RAM，该怎么办？没有办法将其放入 4096 字节的配置空间。因此，它将需要请求一个被称为 BAR（基地址寄存器）的东西。这是通过配置空间公开的一个寄存器，允许主机机器配置其内存的一个区域，直接映射到设备上。然后主机机器上的软件通过对 BAR 的物理地址的内存读/写指令来访问 BAR，就像我们在 ECAM 的第一部分中看到的 MMIO 一样。对设备内存映射进行读取或写入的操作将直接转换为发送到层次结构上的设备的数据包。当设备需要响应时，它将通过层次结构向主机机器发送一个新的数据包。</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//2024/08/31/333e258e5613b3c17bb19e1b5bcbf87b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/333e258e5613b3c17bb19e1b5bcbf87b.png" alt="在主机上运行的设备驱动程序访问 BAR 映射，这些映射转换为通过 PCIe 发送到设备的数据包。"  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 指令读取器件 MMIO 区域的内存时，会生成一个内存读取请求事务层数据包（MemRd TLP），该数据包从主机的 RC 向下传输到器件。这个 TLP 包的目的是通知设备希望读取设备，然后设备需要尽快响应请求地址上的内容。</p>
<p>在 PCIe 中发送和接收的所有数据传输数据包都将采用 TLP 形式。回想一下第 1 部分，这些数据包是设备之间的所有通信都在 PCIe 中发生的中心抽象。这些数据包在出现数据传输错误（类似于网络中的 TCP）的情况下是可靠的，并且可以根据需要重试/重新发送。这确保了数据传输免受 PCIe 可以达到的极高速度下发生的恶劣电气干扰。我们很快就会仔细研究 TLP 的结构，但现在只需将这些视为您在 TCP 中看到的常规网络数据包。</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//2024/08/31/1167d45f8d32476dcb144eee4e65634e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/1167d45f8d32476dcb144eee4e65634e.png" alt="当器件响应时，CPU 会使用器件的结果更新 register 的内容。"  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>当设备收到请求者数据包时，设备会使用 MemRd TLP 响应内存请求。此 TLP 包含从设备内存空间读取的结果，给定原始请求者数据包中的地址和大小。设备将它正在响应的特定请求数据包和发送方标记到响应数据包中，交换层次结构知道如何将响应数据包返回给请求者。然后，请求者将使用数据包中的数据来更新发起请求的 CPU 寄存器。</p>
<p>同时，当 TLP 正在传输时，CPU 必须等待内存请求完成，并且它不能被中断或执行许多有用的工作。正如你可能看到的，如果需要执行大量这样的请求，CPU 将需要花费大量时间等待设备响应每个请求。虽然在硬件级别进行了优化，使此过程更加简化，但使用 CPU 周期等待数据传输完成仍然不是最佳选择。希望您能看到我们需要第二种类型的传输，即 DMA，来解决 BAR 访问的这些缺点。</p>
<p>这里的另一个重点是，设备内存并不严格需要用于设备的 RAM。虽然通常会看到具有板载 RAM 的设备通过 BAR 公开其内部 RAM 的映射，但这不是必需的。例如，访问设备的 BAR 可能会访问设备的内部寄存器，也可能导致设备执行某些操作。例如，写入 BAR 是设备开始执行 DMA 的主要方式。一个核心要点是，设备 BAR 非常灵活，可用于控制设备或执行与设备之间的数据传输。</p>
<h3 id="如何枚举-bar">如何枚举 BAR？</h3>
<p>设备使用其配置空间从软件请求内存区域。在枚举时，由主机确定该区域将放置在物理内存中的位置。每个器件在其配置空间（称为“寄存器”，因此称为基址寄存器）中都有 6 个 32 位值，当枚举器件时，软件将读取和写入这些值。这些寄存器描述了器件希望分配的每个 MMIO 区域的长度和对齐要求，每个可能的 BAR 一个，总共 6 个不同的区域。如果设备希望能够将其 BAR 映射到 4GB 空间（64 位 BAR）以上，它可以将两个 32 位寄存器组合在一起，形成一个 64 位 BAR，最多只留下三个 64 位 BAR。</p>
<p>PCIe</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//2024/08/31/037c6e9bb852b45b9e12c1a046ceec59.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/037c6e9bb852b45b9e12c1a046ceec59.png" alt="Type 0 配置空间结构，显示 6 个 BAR。"  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>
<p>术语注释：尽管首字母缩略词 BAR 表示基址寄存器，但你会看到上面的文本也将 MMIO 的内存窗口称为 BAR。不幸的是，这意味着配置空间中的寄存器名称也与给 device 的 MMIO 区域相同（两者都称为 BAR）。你可能需要根据上下文，以确定它们是指内存窗口，还是配置空间本身的实际寄存器。</p>
</blockquote>
<p>BARs 是配置空间中另一个示例，它不是常量寄存器。在第一部分中，我们看了一些常量寄存器，比如 VendorID 和 DeviceID。但是 BARs 不是常量寄存器，它们应该由软件写入和读取。实际上，由软件写入寄存器的值是特殊的，因为将某些类型的值写入寄存器将导致读取时功能不同。如果你没有牢记设备内存并非总是 RAM，读取回来的值可能与写入的不同，那么现在正是时候这么做了。</p>
<p><strong>设备内存可以是 RAM，但它并不总是 RAM，也不需要像 RAM 那样工作！</strong></p>
<h2 id="什么是-dma引言和理论">什么是 DMA？引言和理论</h2>
<p>到目前为止，我们已经看到了两种形式的 I/O，配置空间访问和通过 BAR 的 MMIO 访问。我们将讨论的最后一种也是最后一种访问形式是直接内存访问（DMA）。DMA 是迄今为止最快的 PCIe 批量传输方法，因为它的传输开销最小。也就是说，通过链路传输最大字节数所需的资源最少。这使得 DMA 对于真正利用 PCIe 提供的高速链路至关重要。</p>
<p>但是，强大的力量会带来巨大的混乱。对于软件开发人员来说，DMA 是一个非常陌生的概念，因为我们在软件中没有类似的东西可以比较。对于 MMIO，我们可以将内存访问概念化为从设备内存中读取和写入的指令。但 DMA 与此非常不同。这是因为 DMA 是异步的，它不利用 CPU 来执行传输。相反，顾名思义，读取和写入的内存直接来自系统 RAM。一旦 DMA 开始，唯一涉及的各方是系统主内存的内存控制器和设备本身。因此，CPU 不会花费周期等待单个内存访问。相反，它只是启动转移，并让平台在后台自行完成 DMA。然后，平台将在传输完成时通知 CPU，通常是通过中断。</p>
<p>让我们想一想，为什么异步执行 DMA 如此重要。考虑 CPU 从计算机上的 NVMe SSD 解密大量文件的情况。一旦主机上的 NVMe 驱动程序启动 DMA，设备就会不断以最快的速度将文件数据从 SSD 的内部存储传输到 CPU 可以访问的系统 RAM 中的位置。然后，CPU 可以使用其 100% 的处理能力来执行解密数学运算，以便在从系统内存中读取数据时解密文件块。CPU 不会花时间等待对设备进行单个内存读取，而是简单地连接数据，并允许设备尽可能快地传输，而 CPU 会尽可能快地处理它。在此期间，任何额外的数据都会在系统 RAM 中缓冲，直到 CPU 可以访问它。这样，任何过程的任何部分都不会等待其他事情发生。所有这些都以尽可能快的速度同时发生。</p>
<p>由于 DMA 的复杂性和涉及的部件数量，我将尝试以最直接的方式解释 DMA，并用大量图表来显示该过程。更令人困惑的是，每个设备都有不同的 DMA 接口。没有用于执行 DMA 的通用软件接口，只有器件的设计人员知道如何告诉该器件执行 DMA。值得庆幸的是，某些设备类别使用普遍认可的接口，例如大多数 SSD 使用的 NVMe 接口或 USB 3.0 的 XHCI 接口。如果没有标准接口，则只有硬件设计人员知道设备如何执行 DMA，因此生产设备的公司或个人需要是编写设备驱动程序的人，而不是依赖与操作系统捆绑的通用驱动程序与设备通信。</p>
<h2 id="一个简单的-dma-传输---step-by-step">一个简单的 DMA 传输 - Step by Step</h2>
<p>我们 DMA 旅程的第一步是查看传输的初始设置。这涉及几个步骤，为即将到来的 DMA 传输准备系统内存、内核和设备。在这种情况下，我们将设置 DMA，以便读取系统 RAM 中存在的 DMA 缓冲区中的内存内容，并将其放入 Target Memory 的器件板载 RAM 中。此时我们已经选择将此内存从 DMA Buffer 读取到器件上地址为 <code>0x8000</code> 中。目标是尽快将此内存从系统内存传输到设备，以便它可以开始处理它。假设在这种情况下，内存量几 M 字节，MMIO 会太慢，但为简单起见，我们将仅显示 32 字节的内存。这种传输将是最简单的 DMA 传输类型：将内存块的已知大小和地址从系统 RAM 复制到设备 RAM。</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//2024/08/31/e485c2c5093a3098acb5d4fc616de33c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/e485c2c5093a3098acb5d4fc616de33c.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="第-1-步---从操作系统分配-dma-内存">第 1 步 - 从操作系统分配 DMA 内存</h3>
<p>此过程的第一步是从 OS 分配 DMA 内存。这意味着设备驱动程序必须进行 OS API 调用，以请求 OS 为设备分配一个内存区域以将数据写入。这一点很重要，因为操作系统可能需要执行特殊的内存管理操作才能使数据对设备可用，例如删除保护或重新组织现有分配以促进请求。</p>
<p>传统上，DMA（直接内存访问）存储器必须是连续的物理存储器，这意味着设备从某个地址和长度的起始处开始，并线性地从缓冲区的起始位置读取/写入数据直到结束。因此，操作系统必须负责组织其物理内存，以创建足够大的连续范围，以满足驱动程序请求的 DMA 缓冲区。有时，对于长时间运行或物理内存有限的系统来说，这可能非常困难。因此，这一领域的增强功能允许更现代的设备使用 Scatter-Gather 和 IOMMU Remapping 等功能传输到非连续的存储器区域。稍后，我们将看一些这些功能。但现在，我们只专注于更简单的连续内存情况。</p>
<p>一旦请求的分配成功，API 将返回内存地址，并指向系统 RAM 中的缓冲区。这将是设备通过 DMA 访问内存的地址。DMA 意图为 API 返回的地址将被赋予一个特殊的名称; 设备逻辑地址或逻辑地址。对于我们的示例，逻辑地址等同于物理地址。设备看到的是操作系统看到的物理内存的完全相同视图，没有额外的转换。然而，在更高级的传输形式中，情况可能并非总是如此。因此最好意识到，设备给出的地址可能并非总是与其在 RAM 中实际的物理地址相同。</p>
<p>分配缓冲区后，由于目的是将数据从此缓冲区移动到设备，因此设备驱动程序将提前使用写入设备所需的信息填充缓冲区。在此示例中，由重复 01 02 03 04 模式组成的数据正在传输到设备的 RAM。</p>
<h3 id="第-2-步---将-dma-地址写入设备并开始传输">第 2 步 - 将 DMA 地址写入设备并开始传输</h3>
<p>传输的下一步是准备设备执行事务所需的信息。这通常是了解器件的特定 DMA 接口最重要的地方。每个设备都以自己的方式进行编程，了解驱动程序应该如何对设备进行编程的唯一方法是参考其通用标准（如 NVMe 规范）或简单地与硬件设计人员合作。</p>
<p>在这个例子中，我将为一个只有执行传输所需的最基本功能的设备构建一个简化的 DMA 接口。在下面的图表中，我们可以看到这个设备通过向 BAR0 MMIO 区域写入数值来进行编程。这意味着为了为这个设备编程 DMA，驱动程序必须将内存写入由 BAR0 指定的 MMIO 区域。每个寄存器在 BAR0 区域内的位置是由驱动程序编写者提前知道的，并且被集成到设备驱动程序的代码中。</p>
<p>对于此示例、我在 BAR0 中创建了四个器件寄存器：</p>
<ul>
<li>Destination Address（目标地址） - 设备内部 RAM 中用于写入从系统 RAM 读取的数据的地址。这是我们将对已经确定的目标地址 0x8000 进行编程的地方。</li>
<li>Source Address（源地址） - 设备将从中读取数据的系统 RAM 的逻辑地址。这将对我们希望设备读取的 DMA Buffer 的逻辑地址进行编程。</li>
<li>Transfer Size（传输大小） - 我们要传输的大小（以字节为单位）。</li>
<li>Initiate Transfer（启动传输标志位）- 一旦次寄存器写入 1，器件将开始在上面给出的地址之间进行传输。通过这种方式，驱动程序可以判断设备已完成填充缓冲区并准备好开始传输。这通常被称为门铃（Door Bell）寄存器。</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//2024/08/31/8c2b333dbb59e8bb8d2a92ae5766299e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8c2b333dbb59e8bb8d2a92ae5766299e.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>在上图中，驱动程序需要使用器件的 BAR0 的映射内存将必要的值写入寄存器（它如何映射此内存取决于 OS）。此图中的值如下所示：</p>
<ul>
<li>Target Memory - 我们要从器件复制的内存将为 <code>0x00008000</code>，它映射到器件板载 RAM 中的内存区域。这将是我们的目标地址。</li>
<li>DMA 缓冲区 - 操作系统在 <code>0x001FF000</code> 分配内存块，因此这将是我们的源地址。</li>
</ul>
<p>有了这些信息，驱动程序现在可以将值编程到设备中，如下所示：</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//2024/08/31/d481c375d9d43db63faf509106fd6dcd.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/d481c375d9d43db63faf509106fd6dcd.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>现在，驱动程序已经配置了执行传输所需的所有寄存器。最后一步是向启动传输寄存器写入一个值，该寄存器充当开始传输的 Door bell 寄存器。一旦写入此值，设备将驱动 DMA 传输，并独立于驱动程序或 CPU 的参与执行它。驱动程序已完成启动传输的工作，此时 CPU 可在等待设备通知系统 DMA 完成的同时，自由进行其他工作。</p>
<h3 id="第-3-步---设备执行-dma-事务">第 3 步 - 设备执行 DMA 事务</h3>
<p>现在，驱动程序已写入 Door bell 寄存器，设备现在将接管处理实际传输。在设备本身上，存在一个名为 DMA 引擎的模块，负责处理和维护事务的所有方面。当器件被编程时，对 BAR0 的寄存器写入正在对 DMA 引擎进行编程，其中包含开始在 PCIe 链路上发送必要的 TLP 以执行内存事务所需的信息。</p>
<p>如上一节所述，PCIe 链路上的所有内存操作都是通过 Memory Write/Read TLP 完成的。在这里，我们将深入研究在交易发生时设备的 DMA 引擎发送和接收的 TLP。请记住，更容易将 TLP 视为在单个可靠连接上发送和接收数据的网络数据包。</p>
<p>如在前文部分讨论过的，PCIe 链接上的所有内存操作都是通过内存写入/读取 TLPs 来完成的。在这里，我们将深入探讨设备的 DMA 引擎在传输过程中发送和接收的 TLPs。要牢记的是，将 TLPs 视为在一个单一、可靠的连接上发送和接收数据的网络数据包更易于理解。</p>
<h3 id="插曲快速了解-tlp">插曲：快速了解 TLP</h3>
<p>在查看链路上的 TLP 之前，让我们仔细了解一下数据包结构本身的概览。</p>
<p>以下是内存读取请求和响应的两个 TLP。如前所述，用于内存操作的 TLP 利用请求和响应系统。执行读取的设备将生成特定地址和长度（以 4 字节 DWORD 为单位）的读取请求 TLP，然后坐下来等待完成数据包到达包含响应数据的链路。</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//2024/08/31/8354d9b795fe77dfbf296da3509b32b7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8354d9b795fe77dfbf296da3509b32b7.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>我们可以看到，有与生成请求的设备、Requester 以及唯一的 Tag 值相关的元数据。此 Tag 值用于将请求与其完成匹配。当设备生成请求时，它会使用唯一值标记 TLP 以跟踪待处理的请求。该值由请求的发送者选择，并且由发送者来跟踪其分配的 Tags。</p>
<p>随着完成的数据通过链路到达，完成的 Tag 让设备能够将接入的数据正确地移至特定传输所需的位置。该系统允许单一设备有多个独特的未完成传输任务，尽管它们接收的数据包相互交错，但仍能保持作为独立传输任务的有序性。</p>
<p>数据包内部还包含了必要的信息，使得 PCIe 切换层次结构能够确定请求和完成需要去向的位置。例如，内存地址被用来确定正在请求访问的设备是哪一个。在枚举期间，层次结构中的每一个设备都被编程以拥有各自独特的地址范围。切换结构根据数据包中的内存地址，确定数据包需要去向哪里以访问那个地址。</p>
<p>设备收到并处理请求后，响应数据将以 Completion TLP 的形式发送回去。完成或“响应”数据包可以而且通常会被分段为许多较小的 TLP，这些 TLP 发送整体响应的一部分。这是因为在枚举期间，已确定设备和总线可以处理最大有效载荷大小（MPS）。MPS 可根据平台和设备功能进行配置，从 128 开始，最高可达 4096 的 2 次方大小。通常，此值约为 256 字节，这意味着需要将大型读取请求拆分为许多较小的 TLP。这些数据包中的每一个都有一个字段，该字段指示完成响应的原始请求的偏移量，有效负载中是返回的数据块。</p>
<p>有一个常见的误解，即内存 TLP 使用 BDF 来寻址数据包需要去往的位置。其实请求仅使用内存地址来指示数据包的目的地，并且设备和目标之间的桥接设备负责将该数据包发送到其正确的位置。。然而，完成数据包确实利用请求者的 BDF 将数据返回给发起请求的设备。</p>
<p>以下是一个展示内存读取和响应过程的图表，图中展示出请求会使用一个地址来发起请求，而完成的操作会使用请求中 Request 字段的 BDF 来发送相应应答：</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//2024/08/31/7b3a9443c759cbf8fe091903bb602eaf.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7b3a9443c759cbf8fe091903bb602eaf.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//2024/08/31/39cc0bf0067bf486aaa2062470c8e45e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/39cc0bf0067bf486aaa2062470c8e45e.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>让我们看看 DMA 引擎为了执行我们的请求而发送和接收的所有内容。由于我们请求了 32 字节的数据，因此只有一个单一的 Memory Read Request 和一个带有响应的单一 Memory Read Completion 数据包。为了便于您理解，请停止向前阅读，并考虑一下在此事务中哪个设备将发送和接收哪个 TLP。如果您需要再次查看第 2 步的图表，请向上滚动。</p>
<p>现在，让我们深入研究一下传输的实际数据包。虽然我将继续绘制这个模拟示例，但我认为对于这个练习，当执行真实传输时，实际看到其中一些 TLP 是什么样子可能会很有趣。</p>
<p>在实验中，我使用真实设备配置了如上文所示的同类通用参数，并启动了 DMA。这个设备会发送真实的 TLPs，将系统 RAM 中的内存读取到设备里。因此，你将有机会罕见地查看在执行这种 DMA 时发送的实际 TLPs 的例子，除非有分析器，否则这些在传输过程中几乎无法看到。</p>
<p>要查看此实验，请点击此链接至配套文章：<a href="https://ctf.re/pcie/experiment/linux/keysight/protocol-analyzer/2024/03/26/pcie-experiment-1/">Experiment - Packet Dumping PCIe DMA TLPs with a Protocol Analyzer and Pcileech – Reversing Engineering for the Soul</a></p>
<p>以下是器件生成的内存读取请求以及请求如何遍历层次结构的框图。</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//2024/08/31/d5ceb838e8b89196864f787a56d02197.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/d5ceb838e8b89196864f787a56d02197.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>
<p>勘误表：0x32 应该是 32</p>
</blockquote>
<p>此图中概述的步骤如下：</p>
<ul>
<li>DMA 引擎创建 TLP - DMA 引擎识别出它必须从 0x001FF000 读取 32 字节。它生成一个包含此请求的 TLP，并通过其本地 PCIe 链路将其发送出去。</li>
<li>TLP 遍历层次结构 - PCIe 的交换层次结构通过桥接设备移动此请求，直到它到达其目的地，即 RC。回想一下，RC 负责处理所有用于访问系统 RAM 的传入数据包。</li>
<li>通知 DRAM 控制器 - RC 在内部与 DRAM 控制器通信，该控制器负责实际访问系统 DRAM 的内存。</li>
<li>从 DRAM 读取内存 - 从地址 0x001FF000 的 DRAM 请求给定长度的 32 字节，并将其返回到值为 01 02 03 04&hellip;</li>
</ul>
<p>尽量不要被这些信息淹没，因为我确实知道仅针对单个内存请求 TLP 就有很多事情要做。所有这些在高层次上归结为仅从 RAM 中的地址 0x001FF000 读取 32 字节的内存。平台如何通过与 DRAM 控制器通信来实际读取系统 DRAM，仅供您参考。设备本身不知道 Root Complex 实际上是如何读取此内存的，它只是使用 TLP 启动传输。</p>
<blockquote>
<p>注意：此处未显示更复杂的 RAM 缓存过程。在 x86-64 上，来自设备的所有内存访问都是缓存一致的，这意味着平台会自动将 CPU 缓存与设备正在访问的值同步。在其他平台（如 ARM 平台）上，由于其缓存架构，这是一个更复杂的过程。现在，我们只假设缓存一致性正在自动为我们处理，我们对此没有任何特别的担忧。</p>
</blockquote>
<p>当 RC 收到此 TLP 时，它会在内部标记 Requester 和 Tag 的读取内容。当它等待 DRAM 响应该值时，此请求的信息将在 RC 中等待。要概念化这一点，可以将其视为网络套接字中的“open connection”。RC 知道它需要响应什么，因此会等到响应数据可用后，再通过套接字将数据发送回去。</p>
<p>最后，将 Completion 从 Root Complex 发送回设备。请注意，Destination 与 Requester 相同：</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//2024/08/31/e4d729f807a08b1dab3dddb0f10c88b2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/e4d729f807a08b1dab3dddb0f10c88b2.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>从 DRAM 读取内存 - DRAM 控制器从系统 DRAM 中 0x001FF000 的 DMA 缓冲区地址读取 32 字节。</li>
<li>DRAM 控制器响应根复合体 - DRAM 控制器在内部响应从 DRAM 向 RC 请求的内存</li>
<li>RC 生成完成 - RC 跟踪传输并为从 DRAM 读取的值创建完成 TLP。在此 TLP 中，元数据值是根据 RC 对待处理传输的了解来设置的，例如发送的字节数、传输的标记以及从原始请求的 Requester 字段复制的目标 BDF。</li>
<li>DMA 引擎接收 TLP - DMA 引擎通过 PCIe 链路接收 TLP，并查看标签是否与原始请求的相同标签匹配。它还会在内部跟踪此值，并知道有效负载中的内存应写入 Target Memory，该内存在设备内部 RAM 中处于 0x8000。</li>
<li>Target Memory is Written（目标内存已写入） - 设备内存中的值将更新为从数据包的 Payload 中复制的值。</li>
<li>系统中断 - 虽然这是可选的，但大多数 DMA 引擎将配置为在 DMA 完成时中断主机 CPU。这会在设备成功完成 DMA 时向设备驱动程序发出通知。</li>
</ul>
<p>同样，仅处理这个 complete 数据包就涉及很多步骤。但是，同样，您可以将整个过程简单地视为“从设备的请求中收到 32 字节的响应”。这些步骤的其余部分只是为了向您展示此响应处理的完整端到端是什么样子。</p>
<p>从这里，设备驱动程序会收到 DMA 已完成的通知，设备驱动程序的代码负责清理 DMA 缓冲区或将其存储起来以供下次使用。</p>
<p>在我们的艰苦努力后，我们终于完成了一次单一的 DMA 传输事务！想到这就是我能提供的最“简单”的传输方式，真是让人惊讶。有了 IOMMU 重映射和 Scatter-Gather 能力的加入，这些事务甚至可能变得更复杂。但就现在而言，你应该对 DMA 的全部内容以及它如何在真实设备中运作有了深入的理解。</p>
<h2 id="尾声---关于复杂性的小说明">尾声 - 关于复杂性的小说明</h2>
<p>如果您读完这篇文章并觉得自己没有完全掌握所有抛给您的概念，或者对复杂性感到不知所措，您不必担心。这些帖子如此复杂的原因是它不仅涵盖广泛的主题，而且还涵盖广泛的专业。通常，整个系统的每个部分在行业中都有不同的团队，他们只关注他们在这个复杂机器中的“齿轮”。通常，硬件开发人员专注于设备，驱动程序开发人员专注于驱动程序代码，而操作系统开发人员专注于资源管理。这些团队之间很少有太多重叠，除非在他们的边界交接，以便另一支团队可以连接到它。</p>
<p>这些帖子有点独特，因为它们试图将系统作为一个整体进行记录，以便于概念理解，而不是实现。这意味着，在通常划定团队边界的地方，这些帖子根本不关心。我鼓励觉得这个话题有趣的读者在自己的时间里继续深入研究。也许您可以了解一些 FPGA 并开始制作自己的设备，或者您可以购买一个设备并开始尝试对其进行逆向工程，并通过您自己的定制软件与它进行通信。</p>
<h2 id="总结">总结</h2>
<p>希望您喜欢这篇深入探讨 PCIe 内存传输的文章！虽然我在这篇文章中涵盖了大量信息，但兔子洞总是更深。值得庆幸的是，通过学习配置空间访问、MMIO（BAR）和 DMA，您现在已经涵盖了 PCIe 中可用的各种形式的数据通信！对于连接到 PCIe 总线的每个设备，主机系统和设备之间的通信将通过这三种方法中的一种进行。设备的链接、资源和驱动程序软件的所有设置和配置最终都是为了促进这三种形式的通信。</p>
<p>这篇文章花了这么长时间才发布的一个重要原因是，为了理解这一切，我必须向读者展示大量信息。很难决定什么值得写，什么太深以至于理解变得模糊。这个决定瘫痪使博客写作过程花费的时间比我预期的要长得多。再加上全职工作，很难找到时间来撰写这些帖子。</p>
<p>在即将发布的帖子中，我期待着讨论以下一些或所有的话题：</p>
<ul>
<li>层次结构的 PCIe 交换/桥接和枚举</li>
<li>更高级的 DMA 主题，例如 DMA 重新映射</li>
<li>电源管理;设备如何“睡眠”和“唤醒”</li>
<li>平台/OS 的中断及其分配和处理</li>
<li>设备的简单驱动程序开发示例</li>
</ul>
<p>与往常一样，如果你有任何问题或想评论或讨论这个系列的某个方面，你最好在我的 discord 的 #hardware 频道中通过“@gbps”找到我，逆向工程 <a href="https://discord.com/invite/rtfm">discord</a></p>
<p>请期待未来的帖子！</p>
]]></content:encoded>
    </item>
    <item>
      <title>PCIe Part 1 - 面向 Windows 初学者的 PCIe 实用教程</title>
      <link>https://lifeislife.cn/posts/%E8%AF%91%E6%96%87-pcie-part1/</link>
      <pubDate>Sat, 31 Aug 2024 10:00:13 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%AF%91%E6%96%87-pcie-part1/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本文翻译自：&lt;a href=&#34;https://ctf.re/windows/kernel/pcie/tutorial/2023/02/14/pcie-part-1/&#34;&gt;面向 Windows 初学者的 PCIe 实用教程（第 1 部分）– 灵魂的逆向工程 &amp;mdash; A Practical Tutorial on PCIe for Total Beginners on Windows (Part 1) – Reversing Engineering for the Soul&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hello！我最近一直在与一些朋友和同事交谈，他们有兴趣了解更多关于 PCIe 的信息，但对复杂性或缺乏适合初学者的简单资源感到害怕。我最近经常使用 PCIe，觉得可能值得以博客文章的形式分享我的一些经验。&lt;/p&gt;
&lt;p&gt;本文主要供具有计算机系统背景并且喜欢亲身实践的人员使用。它也适用于 PCIe 的初学者，或者是对通用概念有所理解但却无法将它们联系在一起的人。&lt;/p&gt;
&lt;p&gt;第一件事是第一件事：不要被吓倒。有很多首字母缩略词和令人困惑的概念，当你&amp;quot;明白&amp;quot;时，它们就会变得简单。当时要迈出一步，不要害怕提出问题！（如果你想问我问题，可以考虑在 &lt;a href=&#34;https://discord.gg/rtfm&#34;&gt;Reverse Engineering Discord&lt;/a&gt; 的 #hardware 频道@Gbps ping 我）&lt;/p&gt;
&lt;p&gt;我&lt;strong&gt;打算&lt;/strong&gt;在这个系列中做几件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从软件方面将 PCIe 分解为我认为最重要的内容，以学习和为现代 PC/服务器系统构建一个良好的基线思想模型。&lt;/li&gt;
&lt;li&gt;展示使用各种工具（通常是 WinDbg）在 Windows 上调查 PCIe 层次结构和设备的实际示例。&lt;/li&gt;
&lt;li&gt;为避免造成混淆，我会有意识地简化或略过一些具体的细节。在这里，可能会有一些术语的使用不准确，甚至信息本身也可能在技术上有所出入。但是，这样做的目的是为了学习整个系统，而不是规范的具体细节。PCIe 是复杂的，当我们处于初学阶段时，陷入过多的细节和特殊情况是没有意义的。&lt;/li&gt;
&lt;li&gt;我们希望通过将这项技术与你已经熟悉的概念相联系，来揭开其神秘面纱。PCIe 并未重新发明轮子，通过理解与它类似的技术，你可能已经比你自己意识到的了解得更多。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我&lt;strong&gt;不打算&lt;/strong&gt;用这个系列做以下事情：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;详细了解传统 PCI 或 PCI-X。一般来说，这项技术除了历史价值之外并不重要。&lt;/li&gt;
&lt;li&gt;演示如何为 PCIe 设备编写设备驱动程序。这是非常特定于操作系统的，并且比这里要讨论的要高得多。&lt;/li&gt;
&lt;li&gt;详细介绍 PCIe 的链路层。该规范的一半以上都花在了这个主题上，并包含了一些世界上最前沿的高速数据传输技术。我不处理这边的事情，但是将来我可能会谈论使用 FPGA 构建 PCIe 设备（我以前做过）。&lt;/li&gt;
&lt;li&gt;帮助你使用 PCIe 在视频游戏中作弊。是的，它存在。不，我不会帮忙。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这并不是对技术或协议的全面研究。要获得真正详尽的了解，你应该参考永远难以捉摸的 PCI-SIG PCI Express 基本规范。这是实现所有 PCIe 代码所依据的规范。目前，在撰写本文时，我们使用的是该规范的 6.0 版，但 3.0 及更高版本的版本都与现代 PCIe 完全相关。如何获得这种昂贵的规格对读者来说是一项练习。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：我有时会在“PCI”和“PCIe”之间来回切换，将技术描述为一种习惯的力量。除非另有说明，否则本系列中的所有内容都是关于 PCIe 的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;什么是-pcie我为什么要关注&#34;&gt;什么是 PCIe，我为什么要关注？&lt;/h2&gt;
&lt;p&gt;PCIe 代表 Peripheral Component Interconnect Express，外围设备组件互联传输。它于 2003 年首次推出，是从早期 PC 时代越来越流行的旧 PCI 和 PCI-X 规范演变而来的（为 Express 添加了“e”以区分它）。&lt;/p&gt;
&lt;p&gt;大多数使用计算机的人都认为它是主板上插入显卡或适配器卡的 PCIe 插槽，但 PCIe 不仅仅是这几个扩展 Port。PCIe 是现代 CPU 与连接到系统的几乎所有设备通信的基础。&lt;/p&gt;
&lt;p&gt;自推出以来，PCIe 的受欢迎程度飙升，成为短距离高速数据传输的近乎通用的标准。几乎所有的 M.2 SSD 都使用 NVMe over PCIe 作为其传输协议。Thunderbolt 3 能够使用外部线将 PCIe 设备直接动态热插拔到系统（支持扩展坞和 eGPU 等技术）。在此基础上，USB4 正在扩展 Thunderbolt 3，以使这种 PCIe 路由技术能够达到开放的 USB 规范。CXL 等新型传输协议，用于数据中心服务器，以 PCIe 为基础规范并在其上扩展他们的特别功能。&lt;/p&gt;
&lt;p&gt;即使与之通信的设备本身不使用 PCIe 作为其物理层协议，系统仍必须使用 PCI 的软件接口进行通信。这是因为系统使用适配器（通常称为主机控制器），这些适配器是 PCI 设备，有助于将来自 CPU 的 PCI 请求转换为主机控制器支持的任何协议或总线。例如，此测试计算机上的所有 USB 3.1 都使用 USB XHCI 协议，该协议是一种通信协议，通过与 USB 主机控制器通信的 PCI 驱动程序将 PCIe 桥接到 USB。&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//2024/08/31/4ab558e7fb773c276583ff528ede9df0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/4ab558e7fb773c276583ff528ede9df0.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;毋庸置疑，PCI 如今无处不在，并且已被计算机世界的各个部分完全采用。因此，我们必须对这项技术有很好的理解，以更好地理解现代计算。&lt;/p&gt;
&lt;h2 id=&#34;研究-pcie-层次结构---一种分组交换网络&#34;&gt;研究 PCIe 层次结构 - 一种分组交换网络&lt;/h2&gt;
&lt;p&gt;从传统的 PCI 转变到 PCIe 最重要的变化是从真正的总线拓扑结构转变为点对点链接。你可以将这看作是以太网集线器向今天的以太网交换机的演变。每个链接都是一个单独的点对点链接，其路由方式就像在一个分组交换的以太网网络上的以太网线一样。这意味着 PCIe 实际上并不是一个“总线协议”，尽管在各种文献和技术规范中让人困惑的频繁使用“总线”这个词。人们必须仔细理解，这个词“总线”并不意味着多个 PCIe 设备在同一个物理链接上进行通信。数据包（也被称为 TLPs）经过每个单独的链接，层次结构中的交换设备使用数据包内的路由信息将数据包传送到正确的 Port。&lt;/p&gt;
&lt;p&gt;在我们进入 PCIe 的技术细节之前，首先我们需要谈谈整个系统的布局。我们研究 PCIe 层次结构的第一种方法是通过 Windows 设备管理器。大多数熟悉 Windows 的人以前都用过它，但没有多少人知道 View &amp;gt; Devices by Connection 中发现的非常方便的功能。&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//2024/08/31/5e0afe87ec5a2f1049d03ceca8038673.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/5e0afe87ec5a2f1049d03ceca8038673.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;通过选择此视图，我们可以从根 PNP（Plug-N-Play）节点看到系统的完整拓扑。PNP 根节点是 Windows 上所有设备树的根，无论它们使用什么总线或协议。每个设备（无论是虚拟设备还是物理设备）都被枚举并放置在此 PNP 树上。我们可以利用 Device Manager 的这个视图来查看这个树的布局。&lt;/p&gt;
&lt;p&gt;特别是，我们希望在系统上找到 PCI 设备的布局。这样，我们就可以开始构建 PCI 树在这台机器上的外观的可视化模型。为此，我们需要找到 PCI 树的根：RC。RC（缩写为 RC）是系统上所有 PCIe 的所有者。它物理上位于 CPU 芯片上，负责充当所有 PCIe 设备接收和发送数据包的主机。它可以被认为是软件（在你的机器上执行的指令）和硬件（PCIe 和 RAM 的外部世界）之间的桥梁。&lt;/p&gt;
&lt;p&gt;在这个系统中，它位于这里的 PNP 层次结构中：&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//2024/08/31/485cfac384549d6d081e49b7c94e55f8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/485cfac384549d6d081e49b7c94e55f8.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;p&gt;注意：你现在可能会问：“如果 PCI 主导了一切，为什么 PCI 根复合物不在树的顶部？答案是由于 PCIe 总线不是启动期间固件提供的系统初始布局。相反，ACPI（高级配置和电源接口）是描述 PCIe 到操作系统存在的东西。虽然你永远不会在 PC 中看到它，但可以描述一个没有 PCI 总线的系统，所有内容都完全由 ACPI 提供。我们稍后会详细讨论 ACPI，但现在不要太担心这个，只要知道 ACPI 是固件告诉我们RC在哪里的方式，然后帮助操作系统枚举树中的 PCI 设备。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以现在我们知道 RC 是 PCIe 树的顶部，现在让我们看一下它下面的所有内容：&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//2024/08/31/8168e2ee37f0a29a65ce5e35bcae0361.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8168e2ee37f0a29a65ce5e35bcae0361.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;不出所料，此 PCI 总线上有许多设备。在这里，我们可以看到负责音频、集成显卡、USB、串行和 SATA 的各种控制器。此外，我们还看到其中一些设备称为 PCI Express Root Port。Root Port 是RC上的一个 Port，另一个 PCIe 端点（又名物理“设备”）或交换机（又名“路由器”）可以连接到该 Port。出于 PCI 规范的考虑，你将听到 Endpoint 称为 Type 0 设备，而 Switch（或网桥）称为 Type 1 设备，因为一个被配置为用于通信的设备，另一个被配置为用于路由数据包的设备。RC 将具有与其物理支持的一样多的 Root Port。也就是说，可以连接到 CPU 芯片的次数越多。CPU 上的一些 Root Port 可能直接路由到物理 PCIe 插槽，而其他 Root Port 可能路由到其他类型的插槽，如 NVMe 插槽。它也可能被路由到另一个 PCIe 交换设备，该设备可以将数据包路由到多个 Port，从而一次路由到多个端点。&lt;/p&gt;
&lt;p&gt;我会继续提出这个比较，但我觉得这很重要——如果你已经了解以太网交换机，你就已经了解 PCIe 交换机。你可以想象这些 Root Port 就像台式计算机上的以太网 Port。你可以将这些直接连接到其他设备（例如摄像头），也可以将它们连接到像家用路由器/调制解调器这样的交换机，这将交换数据包以公开更多连接，以便与更多设备和机器通信。在这种情况下，以太网线是将一个 PCIe Port 连接到另一个 PCIe Port 的铜线，从而使其成为“点对点”。&lt;/p&gt;
&lt;p&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//2024/08/31/f9830973b55ef87fb9bf88615fb6c3cd.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/f9830973b55ef87fb9bf88615fb6c3cd.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;在 PCI 中，系统上的所有“总线”都用 0 到 255（含）之间的数字标识。此外，所有设备都使用“设备 ID”和“功能 ID”进行标识。这通常被描述为 Bus/Device/Function，或简称为 BDF。在更正确的规范术语中，这称为 RID（请求者 ID）。为了减少混淆，我将它称为 BDF。BDF 很重要，因为它专门告诉我们设备在 PCIe 层次结构中的位置，以便我们可以与之通信。&lt;/p&gt;
&lt;p&gt;因为这些都位于层级结构的顶层，所以我们将为这个“bus”提供一个数字标识符，即“Bus 0”或 Root Bus。我们可以通过右键单击顶级设备并选择 Properties 并查看 Location 来验证所有这些设备是否都是 Bus 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//2024/08/31/369d7cf106107b3aa9ee08b6617e6af1.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/369d7cf106107b3aa9ee08b6617e6af1.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;对于此集成图形设备，它的 BDF 为 0:2.0。它位于总线 0（根总线）上，设备 ID 为 2，功能 ID 为 0。在这种情况下，“设备”表示物理设备，例如显卡。“功能”是物理设备向系统公开的独特功能。无论出于何种意图和目的，都可以将其视为一个单独的实体。公开多个功能的设备被恰当地称为多功能设备（MFD）。这意味着它向系统公开两个或多个 PCI 连接，而实际上只有一个设备。我们很快就会看到一个真正的 MFD 示例。&lt;/p&gt;
&lt;p&gt;敏锐的读者会注意到，我们已经打破了我之前提到的“规则”：与这个独特的总线 0 相连的设备有很多。这是 PCIe 中“点对点”规则的第一个例外，只有在因为总线 0 物理上位于 CPU 的硅片上的情况下才允许这样做。也就是说，这些设备之间没有电气路径，这是一个想象中的连接。所有这些设备都存在于 CPU 封装内，并使用极高速电气互连进行路由。这些处理器互连使用的是特定于 CPU 供应商的内部协议，尽管这些协议并未公开文档，但我们仍然以 PCIe 的“语言”与它进行通信。这些端点（标记为绿色），由于其特殊性质，将被赋予一个特殊的名称：根复合集成端点（RC Integrated Endpoints，简称 RCIE），因为它们直接集成在 RC 上。&lt;/p&gt;
&lt;p&gt;这并不奇怪，你会期望集成 UHD 图形等设备将物理位于 CPU 上（因为它是 CPU 规格的一部分）。但是，我们可以通过观察其他 RCIE 来了解系统的一些更有趣的拓扑结构，例如这里也存在 RAM 控制器（与内存的 DRAM DIMM 通信的硅）和 USB 控制器（与外部 USB 设备通信的硅）。这就是为什么某些 CPU 仅支持某些类型的 RAM 和 USB 规范的原因——因为通信的设备在物理上位于 CPU 上，并且仅支持它们在物理上创建时要支持的规范。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;更新：这种说法是不正确的。一些 IO 控制器仍然可以在称为 PCH（Intel）或也称为芯片组（AMD）的分立芯片上找到，该芯片位于 CPU 附近，并且具有高速链路，使其看起来像是集成到 CPU 芯片中。上面这句话错误地说你可以在物理 CPU 上找到 USB 控制器，而它更有可能在“芯片组”上。但是，为了提高速度，与 RAM 通信的内存控制器位于 CPU 芯片上。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此图是层次结构第一级的最小化版本，但现在让我们通过在设备管理器中展开其余的 Root Ports 来构建层次结构的其余部分。&lt;/p&gt;
&lt;p&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//2024/08/31/8d9ee9bf28e10363f2b2f42b55e2e404.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8d9ee9bf28e10363f2b2f42b55e2e404.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;p&gt;注意：我已经标记了 UHD Graphics 设备和总线 0 的 BDF。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这些 Root Port 物理上位于 CPU 上，但连接到它的设备并不在其中。这台机器的外部 PCIe 插槽上连接了 3 个设备：一块 NVIDIA Quadro P400 图形卡和两个 NVMe 驱动器。通过进入设备管理器中每个设备的属性，我们可以获取并更新它们在视觉上的 BDF(总线、设备、功能) 信息。&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//2024/08/31/3ad55c7a1e1a1482a63679a009d6c414.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/3ad55c7a1e1a1482a63679a009d6c414.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;在每个 Root Port 下，我们可以看到一个设备已物理连接。但是，我们还可以看到，我们在每个 Bus 下都公开了一个新的 Bus。Root Port 充当了桥，它将我们从总线 0 桥接到新的总线，因此必须为新总线分配一个新的数字 ID，并且该 Port 下的所有设备/功能都将继承该新总线编号。这与 OS/固件在引导期间的总线枚举期间使用的逻辑相同：所有网桥和交换机都公开一条新总线，必须为其分配新的总线 ID 号。&lt;/p&gt;
&lt;p&gt;在这种情况下，我们还可以看到一个多功能设备的好例子。Quadro P400 显卡充当具有两种功能的 MFD。第一个函数是 0（BDF 01:00.0），是显卡设备本身。第二个功能是 1（BDF 01:00.1），它是音频控制器，允许从 HDMI 等 Port 播放音频。这两个功能是不同的——它们用于完全不同的目的，并且具有与之关联的单独驱动程序和配置，但它们仍然由相同的物理设备（即设备 0）实现，并且位于同一总线（即总线 1）上。这与 PCIe 的点对点规则是一致的，一个链路上只能连接一个物理设备，因此总线上只能存在一个物理设备（除了例外，总线 0）。&lt;/p&gt;
&lt;h2 id=&#34;从-windbg-探索-pcie-层次结构和设备&#34;&gt;从 WinDbg 探索 PCIe 层次结构和设备&lt;/h2&gt;
&lt;p&gt;到目前为止，我们已经通过使用 Device Manager 的“View by Connection”功能看到了标准的 PCI 总线层次结构。还有另一种更详细的方法来调查 PCIe 层次结构：使用 WinDbg 提供的可靠内核调试扩展。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：我们假设你了解如何在一台机器上设置内核调试器来继续下面的操作。你也可以用 LiveKD 来完成大部分练习。如果你并不了解如何设置，可以参考微软提供的指南：&lt;a href=&#34;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection-automatically&#34;&gt;设置 KDNET&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我已经连接到了一台与上述使用的机器不同的新测试机。我们将通过调试器的输出，来演练如何绘制这台机器的层次结构图。我们也将学习如何通过其配置内存来查找设备的信息。&lt;/p&gt;
&lt;p&gt;放入调试器后，我们将使用！pcitree 命令开始。这将转储系统上列举的 PCI 设备的文本树形图。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !pcitree
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Bus 0x0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89b9f75920&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f00 devext 0xffffdc89b0759270 devstack 0xffffdc89b0759120 &lt;span class=&#34;m&#34;&gt;0600&lt;/span&gt; Bridge/HOST to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f02 devext 0xffffdc89ba0c74c0 devstack 0xffffdc89ba0c7370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x1 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba0aa190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;2,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f04 devext 0xffffdc89ba0c94c0 devstack 0xffffdc89ba0c9370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x2 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba0a8190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 10de13bb devext 0xffffdc89ba04f270 devstack 0xffffdc89ba04f120 &lt;span class=&#34;m&#34;&gt;0300&lt;/span&gt; Display Controller/VGA
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 10de0fbc devext 0xffffdc89ba051270 devstack 0xffffdc89ba051120 &lt;span class=&#34;m&#34;&gt;0403&lt;/span&gt; Multimedia Device/Unknown Sub Class
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;3,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f08 devext 0xffffdc89ba0cb4c0 devstack 0xffffdc89ba0cb370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x3 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba08f190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;5,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f28 devext 0xffffdc89ba0cd4c0 devstack 0xffffdc89ba0cd370 &lt;span class=&#34;m&#34;&gt;0880&lt;/span&gt; Base System Device/&lt;span class=&#34;s1&#34;&gt;&amp;#39;Other&amp;#39;&lt;/span&gt; base system device
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;5,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f29 devext 0xffffdc89ba0cf4c0 devstack 0xffffdc89ba0cf370 &lt;span class=&#34;m&#34;&gt;0880&lt;/span&gt; Base System Device/&lt;span class=&#34;s1&#34;&gt;&amp;#39;Other&amp;#39;&lt;/span&gt; base system device
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;5,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f2a devext 0xffffdc89ba0d14c0 devstack 0xffffdc89ba0d1370 &lt;span class=&#34;m&#34;&gt;0880&lt;/span&gt; Base System Device/&lt;span class=&#34;s1&#34;&gt;&amp;#39;Other&amp;#39;&lt;/span&gt; base system device
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;5,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f2c devext 0xffffdc89ba0d34c0 devstack 0xffffdc89ba0d3370 &lt;span class=&#34;m&#34;&gt;0800&lt;/span&gt; Base System Device/Interrupt Controller
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;11, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d7c devext 0xffffdc89ba0d84c0 devstack 0xffffdc89ba0d8370 ff00 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Explicitly&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Undefined/Unknown Sub Class
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;11, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d62 devext 0xffffdc89ba0da4c0 devstack 0xffffdc89ba0da370 &lt;span class=&#34;m&#34;&gt;0106&lt;/span&gt; Mass Storage Controller/Unknown Sub Class
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;14, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d31 devext 0xffffdc89ba0dc4c0 devstack 0xffffdc89ba0dc370 0c03 Serial Bus Controller/USB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;16, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d3a devext 0xffffdc89ba0de4c0 devstack 0xffffdc89ba0de370 &lt;span class=&#34;m&#34;&gt;0780&lt;/span&gt; Simple Serial Communications Controller/&lt;span class=&#34;s1&#34;&gt;&amp;#39;Other&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;16, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;3&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d3d devext 0xffffdc89ba0e04c0 devstack 0xffffdc89ba0e0370 &lt;span class=&#34;m&#34;&gt;0700&lt;/span&gt; Simple Serial Communications Controller/Serial Port
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;19, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 808615a0 devext 0xffffdc89ba0e24c0 devstack 0xffffdc89ba0e2370 &lt;span class=&#34;m&#34;&gt;0200&lt;/span&gt; Network Controller/Ethernet
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1a, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d2d devext 0xffffdc89ba0e44c0 devstack 0xffffdc89ba0e4370 0c03 Serial Bus Controller/USB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1b, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d20 devext 0xffffdc89ba0254c0 devstack 0xffffdc89ba025370 &lt;span class=&#34;m&#34;&gt;0403&lt;/span&gt; Multimedia Device/Unknown Sub Class
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1c, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d10 devext 0xffffdc89ba0274c0 devstack 0xffffdc89ba027370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x4 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba0a9190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1c, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d12 devext 0xffffdc89ba02c4c0 devstack 0xffffdc89ba02c370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x5 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89b9fe6190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1c, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;3&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d16 devext 0xffffdc89ba02e4c0 devstack 0xffffdc89ba02e370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x6 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba0a7190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;12838893&lt;/span&gt; devext 0xffffdc89ba062270 devstack 0xffffdc89ba062120 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Bus 0x7 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba064250&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1c, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d18 devext 0xffffdc89ba0304c0 devstack 0xffffdc89ba030370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x8 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba0b2190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1d, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d26 devext 0xffffdc89ba0364c0 devstack 0xffffdc89ba036370 0c03 Serial Bus Controller/USB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1f, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d44 devext 0xffffdc89ba0384c0 devstack 0xffffdc89ba038370 &lt;span class=&#34;m&#34;&gt;0601&lt;/span&gt; Bridge/PCI to ISA
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1f, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d02 devext 0xffffdc89ba03a4c0 devstack 0xffffdc89ba03a370 &lt;span class=&#34;m&#34;&gt;0106&lt;/span&gt; Mass Storage Controller/Unknown Sub Class
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1f, &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;3&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80868d22 devext 0xffffdc89ba03c4c0 devstack 0xffffdc89ba03c370 0c05 Serial Bus Controller/Unknown Sub Class
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;注意：如果你遇到“无法获取 PciFdoExtensionListHead 地址”的错误，确保你的符号设置正确，并执行.reload pci.sys 操作来重新加载 PCI 的符号。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当显示此输出时，由于空格的格式设置方式，可能很难直观地看到“tree”。解释此输出的方法是查看 Bus 0x 文本的缩进。任何比 Bus 0x 行进一步缩进一组空格的东西都是该总线上的设备。我们可以看到，在器件的正下方还有其他 Bus 0x 线路。这意味着 Bus 0x 线上方的器件正在向我们公开一条新总线，并且总线编号在那里给出。&lt;/p&gt;
&lt;p&gt;让我们看一下此输出的特定部分：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Bus 0x0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89b9f75920&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f00 devext 0xffffdc89b0759270 devstack 0xffffdc89b0759120 &lt;span class=&#34;m&#34;&gt;0600&lt;/span&gt; Bridge/HOST to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f02 devext 0xffffdc89ba0c74c0 devstack 0xffffdc89ba0c7370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x1 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba0aa190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;2,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f04 devext 0xffffdc89ba0c94c0 devstack 0xffffdc89ba0c9370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x2 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba0a8190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 10de13bb devext 0xffffdc89ba04f270 devstack 0xffffdc89ba04f120 &lt;span class=&#34;m&#34;&gt;0300&lt;/span&gt; Display Controller/VGA
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 10de0fbc devext 0xffffdc89ba051270 devstack 0xffffdc89ba051120 &lt;span class=&#34;m&#34;&gt;0403&lt;/span&gt; Multimedia Device/Unknown Sub Class
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;3,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 80866f08 devext 0xffffdc89ba0cb4c0 devstack 0xffffdc89ba0cb370 &lt;span class=&#34;m&#34;&gt;0604&lt;/span&gt; Bridge/PCI to PCI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Bus 0x3 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;FDO Ext ffffdc89ba08f190&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    No devices have been enumerated on this bus.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在此输出中，我们可以看到每个设备显示的 BDF。我们还可以看到总线 0 上存在的一组 Root Port，这些 Port 下面没有列举任何设备，这意味着插槽尚未连接到任何设备。&lt;/p&gt;
&lt;p&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//2024/08/31/7a507a4048a8a03806ced4cd6714bbf7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7a507a4048a8a03806ced4cd6714bbf7.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;p&gt;注意：这只是一个巧合，即公交号恰好与桥梁/PCI 的设备编号匹配到 PCI 端口。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如你现在所知，标记为 Bridge/PCI to PCI 的设备实际上是 Root Port，而总线 2 上的设备实际上是一个多功能设备。与设备管理器不同，我们看不到！pcitree 中的设备真实名称。相反，我们只得到了一个通用的 PCI 名称，用于设备“类型”将自己宣传为什么。这是因为设备管理器 从驱动程序读取设备名称，而不是直接从 PCI 读取设备名称。&lt;/p&gt;
&lt;p&gt;要了解更多关于这个显示控制器设备的信息，我们可以使用命令 &lt;code&gt;！devext [pointer]&lt;/code&gt;，其中 &lt;code&gt;[pointer]&lt;/code&gt; 是布局中单词 &lt;code&gt;devext&lt;/code&gt; 后面的值。在本例中，它是：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0,  &lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 10de13bb devext 0xffffdc89ba04f270 devstack 0xffffdc89ba04f120 &lt;span class=&#34;m&#34;&gt;0300&lt;/span&gt; Display Controller/VGA
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;!devext 0xffffdc89ba04f270
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从这里，我们将从 Windows 中的 PCI 总线驱动程序获得此 PCI 设备的摘要的打印输出，&lt;code&gt;pci.sys&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !devext 0xffffdc89ba04f270
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PDO Extension, Bus 0x2, Device 0, Function 0.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  DevObj 0xffffdc89ba04f120  Parent FDO DevExt 0xffffdc89ba0a8190
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Device &lt;span class=&#34;nv&#34;&gt;State&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; PciStarted
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Vendor ID 10de &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;NVIDIA CORPORATION&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;  Device ID 13BB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Subsystem Vendor ID 103c &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;HEWLETT-PACKARD COMPANY&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;  Subsystem ID &lt;span class=&#34;m&#34;&gt;1098&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Header Type 0, Class Base/Sub 03/00  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Display Controller/VGA&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Programming Interface: 00, Revision: a2, IntPin: 01, RawLine &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Possible Decodes &lt;span class=&#34;o&#34;&gt;((&lt;/span&gt;cmd &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt; 7&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 7&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: BMI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Capabilities: &lt;span class=&#34;nv&#34;&gt;Ptr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;60, power msi express 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Express capabilities: &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;BIOS controlled&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Logical Device Power State: D0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Device Wake Level:          Unspecified
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  WaitWakeIrp:                &amp;lt;none&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Requirements:     Alignment Length    Minimum          Maximum
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR0    Mem:    &lt;span class=&#34;m&#34;&gt;01000000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;01000000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;0000000000000000&lt;/span&gt; 00000000ffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR1    Mem:    &lt;span class=&#34;m&#34;&gt;10000000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;10000000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;0000000000000000&lt;/span&gt; ffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR3    Mem:    &lt;span class=&#34;m&#34;&gt;02000000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;02000000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;0000000000000000&lt;/span&gt; ffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR5     Io:    &lt;span class=&#34;m&#34;&gt;00000080&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;00000080&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;0000000000000000&lt;/span&gt; 00000000ffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      ROM BAR:      &lt;span class=&#34;m&#34;&gt;00080000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;00080000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;0000000000000000&lt;/span&gt; 00000000ffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    VF BAR0 Mem:    &lt;span class=&#34;m&#34;&gt;00080000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;00080000&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;0000000000000000&lt;/span&gt; 00000000ffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Resources:        Start            Length
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR0    Mem:    00000000f2000000 &lt;span class=&#34;m&#34;&gt;01000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR1    Mem:    00000000e0000000 &lt;span class=&#34;m&#34;&gt;10000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR3    Mem:    00000000f0000000 &lt;span class=&#34;m&#34;&gt;02000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    BAR5     Io:    &lt;span class=&#34;m&#34;&gt;0000000000001000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Interrupt Requirement:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Line Based - Min &lt;span class=&#34;nv&#34;&gt;Vector&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0x0, Max &lt;span class=&#34;nv&#34;&gt;Vector&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0xffffffff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Message Based: Type - Msi, 0x1 messages requested
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Interrupt Resource:    Type - MSI, 0x1 Messages Granted
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里有很多内核知道的关于这个设备的信息。此信息是通过 配置空间（缩写为“config space”）检索的，配置空间 是系统上的内存部分，允许内核以标准化的方式枚举、查询信息和设置 PCI 设备。软件从设备读取内存以查询供应商 ID 等信息，设备（如果已打开电源）使用该信息进行响应。在下一节中，我将更多地讨论这实际上是如何发生的，但要知道这里查询的信息是从配置空间生成的。&lt;/p&gt;
&lt;p&gt;因此，让我们分解一些重要的东西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DevObj：指向 &lt;code&gt;nt！_DEVICE_OBJECT&lt;/code&gt; 结构的指针，该结构表示内核中的物理设备。&lt;/li&gt;
&lt;li&gt;Vendor ID：注册给特定设备制造商的 16 位 ID 号。此值是标准化的，PCI-SIG 必须为新供应商分配一个唯一 ID，以便它们不会重叠。在本例中，我们看到这是 NVIDIA 显卡。&lt;/li&gt;
&lt;li&gt;Device  ID：执行 PCIe 的特定芯片的 16 位 ID 号。类似的想法是，公司必须为其芯片请求一个唯一的 ID，这样它就不会与任何其他芯片冲突。&lt;/li&gt;
&lt;li&gt;Subsystem Vendor ID：芯片所在电路板的供应商 ID。在这种情况下，“HP”是显卡的生产商，而“NVIDIA”设计了图形芯片。&lt;/li&gt;
&lt;li&gt;Subsystem Device ID：芯片所在电路板的设备 ID。&lt;/li&gt;
&lt;li&gt;Logical Device Power State：此设备的电源状态。PCI 中有两种主要的电源状态，D0 = 设备已通电，D3 = 设备处于低功耗状态或完全关闭。&lt;/li&gt;
&lt;li&gt;Requirements：设备要求 OS 为其分配的内存要求。稍后会详细介绍。&lt;/li&gt;
&lt;li&gt;Resources：操作系统分配给此设备的内存资源。此设备已打开电源并启动，因此已为其分配了资源。&lt;/li&gt;
&lt;li&gt;Interrupt Requirement/Resource：与上述相同，但是对于中断则不同。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要实际获取有关此设备的完整信息，我们可以使用 &lt;a href=&#34;https://pcilookup.com/&#34;&gt;PCI Lookup&lt;/a&gt; 中的出色工具来查询有关在 PCI-SIG 中注册的 PCI 设备的公共信息。让我们将有关设备和 Vendor ID 的信息放入框中：&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//2024/08/31/a70cefe8ad2cbb145ce145c1b16e562d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/a70cefe8ad2cbb145ce145c1b16e562d.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;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//2024/08/31/098ff685f1e2ca1d27f87df7378a836a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/098ff685f1e2ca1d27f87df7378a836a.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;这告诉我们该设备是 NVIDIA 创建的 Quadro K620 显卡。子系统 ID 告诉我们，这个特定的卡 PCB 是由 HP 生产的，该公司已获得 NVIDIA 的许可。&lt;/p&gt;
&lt;p&gt;我们在 &lt;code&gt;！devext&lt;/code&gt; 中看到的很好地概述了 &lt;code&gt;pci.sys&lt;/code&gt; 在摘要中特别关心向我们展示的内容，但它只触及了配置空间中所有信息的皮毛。要将所有信息转储到配置空间中，我们可以使用扩展名 &lt;code&gt;！pci 100 B D F&lt;/code&gt;，其中 BDF 是我们相关设备的 BDF。100 是一组标志，指定我们要转储有关设备的所有信息。显示的信息将按照它在设备的 config space 中存在的顺序进行布局。每个部分的前缀是一个偏移量，例如 &lt;code&gt;02&lt;/code&gt; 表示 Device ID。这指定了从中读取此值的 config 空间的偏移量。这些偏移量在 PCI 规范中进行了详细说明，并且不会出于向后兼容性目的在 PCI 版本之间更改。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !pci &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PCI Configuration Space &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Segment:0000 Bus:02 Device:00 Function:00&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Common Header:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    00: VendorID       10de Nvidia Corporation
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    02: DeviceID       13bb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    04: Command        &lt;span class=&#34;m&#34;&gt;0507&lt;/span&gt; IOSpaceEn MemSpaceEn BusInitiate SERREn InterruptDis 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    06: Status         &lt;span class=&#34;m&#34;&gt;0010&lt;/span&gt; CapList 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    08: RevisionID     a2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    09: ProgIF         &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; VGA
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0a: SubClass       &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; VGA Compatible Controller
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0b: BaseClass      &lt;span class=&#34;m&#34;&gt;03&lt;/span&gt; Display Controller
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0c: CacheLineSize  &lt;span class=&#34;m&#34;&gt;0000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0d: LatencyTimer   &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0e: HeaderType     &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0f: BIST           &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    10: BAR0           f2000000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    14: BAR1           e000000c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    18: BAR2           &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    1c: BAR3           f000000c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    20: BAR4           &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    24: BAR5           &lt;span class=&#34;m&#34;&gt;00001001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    28: CBCISPtr       &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    2c: SubSysVenID    103c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    2e: SubSysID       &lt;span class=&#34;m&#34;&gt;1098&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    30: ROMBAR         &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    34: CapPtr         &lt;span class=&#34;m&#34;&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    3c: IntLine        &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    3d: IntPin         &lt;span class=&#34;m&#34;&gt;01&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    3e: MinGnt         &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    3f: MaxLat         &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Device Private:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    40: 1098103c &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    50: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000001&lt;/span&gt; 0023d6ce &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    60: &lt;span class=&#34;m&#34;&gt;00036801&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000008&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00817805&lt;/span&gt; fee001f8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    70: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00120010&lt;/span&gt; 012c8de1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    80: &lt;span class=&#34;m&#34;&gt;00003930&lt;/span&gt; 00453d02 &lt;span class=&#34;m&#34;&gt;11010140&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    90: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00040013&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    a0: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000006&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000002&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    b0: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;01140009&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    c0: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    d0: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    e0: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    f0: &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Capabilities:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    60: CapID          &lt;span class=&#34;m&#34;&gt;01&lt;/span&gt; PwrMgmt Capability
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    61: NextPtr        &lt;span class=&#34;m&#34;&gt;68&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    62: PwrMgmtCap     &lt;span class=&#34;m&#34;&gt;0003&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;Version&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    64: PwrMgmtCtrl    &lt;span class=&#34;m&#34;&gt;0008&lt;/span&gt; DataScale:0 DataSel:0 D0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    68: CapID          &lt;span class=&#34;m&#34;&gt;05&lt;/span&gt; MSI Capability
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    69: NextPtr        &lt;span class=&#34;m&#34;&gt;78&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    6a: MsgCtrl        64BitCapable MSIEnable MultipleMsgEnable:0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0x1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; MultipleMsgCapable:0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0x1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    6c: MsgAddrLow     fee001f8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    70: MsgAddrHi      &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    74: MsgData        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    78: CapID          &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt; PCI Express Capability
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    79: NextPtr        &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    7a: Express Caps   &lt;span class=&#34;m&#34;&gt;0012&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ver. 2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Type:LegacyEP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    7c: Device Caps    012c8de1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    80: Device Control &lt;span class=&#34;m&#34;&gt;3930&lt;/span&gt; bcre/flr MRR:1K NS ap pf ET MP:256 RO ur fe nf ce
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    82: Device Status  &lt;span class=&#34;m&#34;&gt;0000&lt;/span&gt; tp ap ur fe nf ce
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    84: Link Caps      00453d02
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    88: Link Control   &lt;span class=&#34;m&#34;&gt;0140&lt;/span&gt; es CC rl ld RCB:64 ASPM:None 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    8a: Link Status    &lt;span class=&#34;m&#34;&gt;1101&lt;/span&gt; SCC lt lte NLW:x16 LS:2.5 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    9c: DeviceCaps2    &lt;span class=&#34;m&#34;&gt;00040013&lt;/span&gt; CTR:3 CTDIS arifwd aor aoc32 aoc64 cas128 noro ltr TPH:0 OBFF:1 extfmt eetlp EETLPMax:0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    a0: DeviceControl2 &lt;span class=&#34;m&#34;&gt;0000&lt;/span&gt; CTVal:0 ctdis arifwd aor aoeb idoreq idocom ltr OBFF:0 eetlp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enhanced Capabilities:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    100: CapID         &lt;span class=&#34;m&#34;&gt;0002&lt;/span&gt; Virtual Channel Capability
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         Version       &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         NextPtr       &lt;span class=&#34;m&#34;&gt;258&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0104: Port VC Capability &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0108: Port VC Capability &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    010c: Port VC Control             &lt;span class=&#34;m&#34;&gt;0000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    010e: Port VC Status              &lt;span class=&#34;m&#34;&gt;0000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0110: VC Resource&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Cap          &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    0114: VC Resource&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Control      800000ff
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    011a: VC Resource&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Status       &lt;span class=&#34;m&#34;&gt;0000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    258: CapID         001e L1 PM SS Capability
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         Version       &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         NextPtr       &lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    25c: Capabilities  0028ff1f  PTPOV:5 PTPOS:0 PCMRT:255 L1PMS ASPML11 ASPML12 PCIPML11 PCIPML12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    260: Control1      &lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;  LTRL12TS:0 LTRL12TV:0 CMRT:0 aspml11 aspml12 pcipml11 pcipml12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    264: Control2      &lt;span class=&#34;m&#34;&gt;00000028&lt;/span&gt;  TPOV:5 TPOS:0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    128: CapID         &lt;span class=&#34;m&#34;&gt;0004&lt;/span&gt; Power Budgeting Capability
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         Version       &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         NextPtr       &lt;span class=&#34;m&#34;&gt;600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    600: CapID         000b Vendor Specific Capability
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         Version       &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         NextPtr       &lt;span class=&#34;m&#34;&gt;000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         Vendor Specific ID &lt;span class=&#34;m&#34;&gt;0001&lt;/span&gt; - Ver. &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;  Length: &lt;span class=&#34;m&#34;&gt;024&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个视图的好处是，我们可以看到有关配置空间的 Capabilities 部分的详细信息。Capabilities 是 config 空间中的一组结构，它准确描述了 device 能够实现的功能。Capabilities 包括链接速度和设备支持的中断类型等信息。PCI 规范中添加的任何新功能都将通过这些结构进行公布，这些结构在配置空间中形成了一个功能链表，可以迭代以发现设备的所有功能。并非所有这些功能都与操作系统相关，有些功能仅与本文未涵盖的硬件方面相关。现在，我不会详细介绍该设备的功能。&lt;/p&gt;
&lt;h2 id=&#34;pcie一切都与内存相关&#34;&gt;PCIe：一切都与内存相关&lt;/h2&gt;
&lt;p&gt;现在我们已经研究了几个设备和 PCI 总线的层次结构，让我们谈谈与软件和 PCI 设备的通信实际上是如何运作的。当我第一次学习 PCI 时，我很难理解当软件与 PCI 设备连接时到底发生了什么。因为整个事务对作为软件开发人员的你来说是抽象出来的，所以很难仅通过从调试工具中探入 PCI 内存来构建所发生的事情的心智模型。希望这篇文章能提供比我刚开始时所能得到的更好的概述。&lt;/p&gt;
&lt;p&gt;首先，我要做一个大胆的声明：&lt;strong&gt;所有现代 PCIe 通信都是通过内存读写完成的&lt;/strong&gt;。如果你了解 PCIe 中的内存如何工作，你就会了解 PCIe 软件通信的工作原理。（是的，在某些平台上还有其他传统的通信方式，但我们不会讨论这些方式，因为它们已被弃用）。&lt;/p&gt;
&lt;p&gt;现在，让我们谈谈现代平台上不同类型的内存。在启动的早期，操作系统的 CPU 将使用虚拟内存。也就是说，CPU 看到的内存地址是映射到物理内存世界的内存视图。&lt;/p&gt;
&lt;p&gt;就我们的目的而言，系统上有两种类型的物理内存：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RAM - 读取或写入时从计算机上的 DRAM DIMM 存储和检索的地址。这就是大多数人在想到“内存”时所想到的。&lt;/li&gt;
&lt;li&gt;Device Memory（设备内存） - 在读取或写入时与系统上的设备“对话”的地址。这里的关键词是“对话”。它不会在设备上存储内存，也不会检索设备上的内存（尽管设备可能同时能够同时检索两者）。你可能正在与之通信的地址甚至可能根本不是内存，而是一个更抽象的“device register” ，用于配置设备的内部工作。这种访问会发生什么取决于设备。你所做的只是与设备通信。你通常会看到这称为 MMIO，它全称是 Memory-Mapped I/O。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：每当设备不响应设备内存区域中访问的地址时，PCI 的设备内存将始终读取“全 1”或“所有 FF”。这是了解设备何时实际响应的便捷方法。如果你看到所有 FF，则表示你正在读取无效的设备地址。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;初学者认为所有物理内存都是 RAM，这是错误的。当软件与 PCI 区域中的 PCI 设备通信时，它不会从 RAM 读取和写入数据。相反，该设备从 RC 接收一个数据包（TLP，传输层数据包），当 PCI 区域内的地址被读/写时，你的 CPU 会立即自动生成该数据包。你无需在软件中创建这些数据包，所有这些数据包都是在访问此内存后立即完全在后台生成的。在软件中，你甚至无法查看或捕获这些数据包，而需要一个特殊的硬件测试设备来拦截和查看正在发送的数据包。稍后会详细介绍。&lt;/p&gt;
&lt;p&gt;如果有帮助，请将物理内存视为设备的映射。RAM 是为你映射到物理内存中的设备。PCI 还会自动为你映射区域。尽管它们截然不同且行为也非常不同，但它们在软件中看起来是相同的。&lt;/p&gt;
&lt;p&gt;在下图中，我们可以看到典型系统如何将虚拟内存映射到物理内存。请注意，有两个 RAM 区域和两个 PCI 内存区域。这是因为某些较旧的 PCI 设备只能寻址 32 位内存。因此，如果你的 RAM 不适合 4GB 以下的地址窗口，则一些 RAM 会上移到 4GB 以上。由于你的处理器支持 64 位地址，因此这不是问题。此外，在 4GB 行上方为支持 64 位地址的 PCI 设备创建第二个窗口。由于 4GB 区域可能非常有限，因此设备最好在 4GB 以上移动尽可能多的内存，以免弄乱下面的空间。&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//2024/08/31/2f1b3cf2195ea201e631c0d29929398b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/2f1b3cf2195ea201e631c0d29929398b.png&#34; alt=&#34;&#34;  title=&#34;如何将虚拟地址范围映射到物理地址的非常简化的视图。这忽略了物理内存中的大量 “特殊” 区域，但展示了 RAM 和设备内存是如何不同的。&#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;（Configuration Space）。&lt;/p&gt;
&lt;p&gt;配置空间位于一个名为 ECAM（Extended Configuration Access Management，扩展配置访问管理）的内存部分。因为它是一种设备内存，所以要从内核（使用虚拟内存）访问这段内存，内核必须请求内存管理器将这部分物理内存映射到一个虚拟地址上。然后，软件指令可以使用映射的虚拟地址来从物理地址读取和写入。在 Windows 上，定位和映射这段内存的工作部分由&lt;code&gt;pci.sys&lt;/code&gt;处理，部分由&lt;code&gt;acpi.sys&lt;/code&gt;处理，还有部分由内核（具体来说是 HAL）处理。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：通常，在 Windows 中映射设备内存的方式是通过 &lt;a href=&#34;https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmmapiospaceex&#34;&gt;MmMapIoSpaceEx&lt;/a&gt;，这是驱动程序可用于映射物理设备内存的 API。但是，为了进行配置空间访问，软件必须使用 &lt;a href=&#34;https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-halgetbusdatabyoffset&#34;&gt;HalGetBusDataByOffset&lt;/a&gt; 和 &lt;a href=&#34;https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-halsetbusdatabyoffset&#34;&gt;HalSetBusDataByOffset&lt;/a&gt; 来确保 &lt;code&gt;pci.sys&lt;/code&gt; 的内部状态与你正在执行的配置空间读/写保持同步。如果你尝试自己映射和更改配置空间，则可能会使 &lt;code&gt;pci.sys&lt;/code&gt; 状态不同步并导致蓝屏死机。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：ECAM/PCI 区域在物理内存中的位置取决于平台。引导时的固件将分配系统物理内存的所有特殊区域。然后，固件会在引导期间向操作系统公布这些区域的位置。在 x86-64 系统上，ECAM 区域将使用称为 MCFG 的表（结构）通过 ACPI 从固件进行通信。现在知道使用什么特定协议来检索此信息不是很重要吗，只需了解操作系统从固件中检索这些区域的地址，固件决定了将它们放在哪里。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因此，为了进行配置空间访问，内核必须将配置空间（ECAM）映射到虚拟内存。这是这样的事情会是什么样子：&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//2024/08/31/7bb04dca3ffe48821f8bf9f6f48bc71b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7bb04dca3ffe48821f8bf9f6f48bc71b.png&#34; alt=&#34;&#34;  title=&#34;ECAM 到虚拟内存的映射。可怕的是没有规模。&#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;在此之后，内核现在可以使用虚拟映射与设备的配置空间进行通信。但是这个配置空间是什么样的呢？嗯，它只是我们上面讨论的一堆配置空间结构块。设备可能具有的每个可能的 BDF 都在 ECAM 中提供了空间来对其进行配置。它的布局方式是，设备的 BDF 会告诉你其配置空间在 ECAM 中的确切位置。也就是说，给定一个 BDF，我们可以计算要添加到 ECAM 区域基数的偏移量，以便与设备通信，因为每个功能的所有 ECAM 区域的大小都相同。&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//2024/08/31/54b366fd88125e12c7b1cffd70f8f0bd.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/54b366fd88125e12c7b1cffd70f8f0bd.png&#34; alt=&#34;&#34;  title=&#34;如果设备不存在，系统将读回所有 FF（二进制中的所有 1）。这将表明设备当前在系统上未处于活动状态&#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;从这张图中，我们可以开始看到 PCIe 的枚举实际上是如何发生的。当我们读回有效的配置空间数据时，我们知道该 BDF 上存在设备。如果我们改为读回 FF，我们知道设备不在该插槽或功能中。当然，我们不会为了枚举所有设备而暴力破解每个地址，因为由于 MMIO 的开销，代价比较大。但是，这种蛮力的高级版本是我们如何快速枚举所有已通电并在配置空间上响应我们的设备。&lt;/p&gt;
&lt;h2 id=&#34;把它们放在一起---软件配置空间访问&#34;&gt;把它们放在一起 - 软件配置空间访问&lt;/h2&gt;
&lt;p&gt;现在我们了解了如何访问配置空间，我们可以将两端（层次结构和 MMIO）放在一起，以查看从内核模式读取配置空间的指令的完整路径。&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//2024/08/31/ed43867c9ad22be7633a41a8cb397d12.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/ed43867c9ad22be7633a41a8cb397d12.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;在内核模式下运行的某些代码从 ECAM 虚拟映射中读取偏移量。&lt;/li&gt;
&lt;li&gt;虚拟映射由 CPU 的页表转换为 ECAM 中的物理地址。&lt;/li&gt;
&lt;li&gt;读取物理地址，导致内部 CPU 互连中发生操作，以通知RC访问。&lt;/li&gt;
&lt;li&gt;RC将请求的数据包化版本生成为 TLP，该 TLP 显示“读取设备 02:00.0 的偏移量 0x0 处的值”，并通过层次结构发送该请求。&lt;/li&gt;
&lt;li&gt;TLP 由总线 2 上的此显示控制器接收，并看到它是一个配置空间 TLP。现在，它知道使用包含偏移量 0x0 处的值内容的配置空间响应 TLP 进行响应。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&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//2024/08/31/7636af73b1e906b7cbf1cc44fe620dfc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7636af73b1e906b7cbf1cc44fe620dfc.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;响应路径没那么有趣了。设备以含有偏移 0 处的值（我们知道这是供应商 ID）的特殊 TLP 进行响应。这个数据包找到回到请求者（即RC），然后互连通知 CPU 更新 rax 的值为 0x10DE，这是 NVIDIA 显卡的供应商 ID。然后，CPU 开始执行下一条指令。&lt;/p&gt;
&lt;p&gt;如你所想那样，通过这种方式进行访问可能比通过全部的 TLP 生成的 RAM 慢很多。这确实是事实，并且这也是存在比这种 MMIO 方法更多的方式去与设备通信的主要原因之一。在接下来的文章中，我将详细介绍另一种方法，即 DMA，以及它对于确保软件能够尽可能快地在 CPU 和设备之间传输内存的至关重要性。&lt;/p&gt;
&lt;h2 id=&#34;练习通过-windbg-手动访问-ecam&#34;&gt;练习：通过 WinDbg 手动访问 ECAM&lt;/h2&gt;
&lt;p&gt;我们看了一下 config space access 理论上是如何发生的，但让我们自己用 debugger 做同样的事情。为此，我们希望：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;找到 ECAM 在系统上的位置。&lt;/li&gt;
&lt;li&gt;计算到 ECAM 的偏移量以读取设备的供应商 ID。为此，我选择了 NVIDIA 显卡上的&lt;code&gt;Multimedia Device @ 02:00.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在该地址执行物理内存读取以检索值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第一步是找到 ECAM。鉴于 ECAM 的位置来自 ACPI，特别是 ACPI 中的 MCFG 表，这部分有点棘手。这是 firmware 用来告诉操作系统 ECAM 在系统的物理内存映射中的位置的表。关于 ACPI 以及如何将其与 PCI 结合使用，有很多内容要讨论，但现在，我将快速跳到相关部分以实现我们的目标。&lt;/p&gt;
&lt;p&gt;在我们的调试器中，我们可以通过使用&lt;code&gt;!acpicache&lt;/code&gt;来转储所有 ACPI 表的缓存副本。要转储 MCFG，请点击链接 MCFG 来转储其内容，或手动键入&lt;code&gt;!acpitable MCFG&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !acpicache
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dumping cached ACPI tables...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  XSDT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;fffff7b6c0004018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x0000bc TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  MCFG @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;fffff7b6c0005018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x00003c TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  FACP @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;fffff7b6c0007018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x4 Len: 0x0000f4 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  APIC @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;fffff7b6c0008018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x2 Len: 0x000afc TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  DMAR @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;fffff7b6c000a018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x0000c0 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  HPET @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;fffff7b6c015a018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x000038 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  TCPA @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b07209f8&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x2 Len: 0x000064 TableID: EDK2    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  SSDT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b0720a88&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x2 Len: 0x0003b3 TableID: Tpm2Tabl
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  TPM2 @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b0720e68&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x3 Len: 0x000034 TableID: EDK2    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  SSDT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b07fc018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x0013a1 TableID: Plat_Wmi
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  UEFI @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b07fd3e8&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x000042 TableID: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  BDAT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b07fd458&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x000030 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  MSDM @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b07fd4b8&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x3 Len: 0x000055 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  SLIC @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b07fd538&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x000176 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  WSMT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b07fd6d8&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x000028 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  WDDT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b0721a68&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x000040 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  SSDT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b2580018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x2 Len: 0x086372 TableID: SSDT  PM
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  NITR @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b26063b8&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x2 Len: 0x000071 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ASF! @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b2606548&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x20 Len: 0x000074 TableID:  HCG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  BGRT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b26065e8&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x1 Len: 0x000038 TableID: TIANO   
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  DSDT @&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ffffdc89b0e94018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Rev: 0x2 Len: 0x021c89 TableID: SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !acpitable MCFG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;HEADER - fffff7b6c0005018
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Signature:               MCFG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Length:                  0x0000003c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Revision:                0x01
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Checksum:                0x3c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  OEMID:                   HPQOEM
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  OEMTableID:              SLIC-WKS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  OEMRevision:             0x00000001
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  CreatorID:               INTL
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  CreatorRev:              0x20091013
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BODY - fffff7b6c000503c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fffff7b6&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;c000503c  &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; 00-00 &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; d0 &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;  ................
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fffff7b6&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;c000504c  &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; ff &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;                          ........
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要了解如何阅读此表，遗憾的是，我们需要查看 ACPI 规范。与其让你这样做，不如省去你的痛苦，把相关部分拉到这里：&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//2024/08/31/2e26ba8d45b7e876b78500fa7392440d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/2e26ba8d45b7e876b78500fa7392440d.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;code&gt;！acpitable&lt;/code&gt; 命令已经解析并显示此表中 &lt;code&gt;Creator Revision&lt;/code&gt; 之前的所有内容，因此 &lt;code&gt;BODY&lt;/code&gt; 的前 8 个字节将是偏移量 36 处的 8 个字节的 &lt;code&gt;Reserved&lt;/code&gt; 内存。因此，我们跳过这 8 个字节并找到以下结构：&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//2024/08/31/936801aafdfbd7bc6e13e82310ce925f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/936801aafdfbd7bc6e13e82310ce925f.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 个字节是 &lt;code&gt;Reserved&lt;/code&gt; 后面的区域的 &lt;code&gt;ECAM&lt;/code&gt; 区域的地址。这意味着 &lt;code&gt;ECAM&lt;/code&gt; 基址的偏移量为偏移量 8。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BODY - fffff7b6c000503c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fffff7b6&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;c000503c  &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; 00-00 &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; d0 &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;  ................
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fffff7b6&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;c000504c  &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; ff &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;                          ........
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于这个系统，ECAM 位于地址：&lt;code&gt;0xD0000000&lt;/code&gt;。（请别忘了以小端序来读取这个地址）&lt;/p&gt;
&lt;p&gt;为了验证我们得到了正确的地址，让我们读取&lt;code&gt;00:00.0&lt;/code&gt;的供应商 ID，这也是 ECAM 的前两个字节。我们将使用&lt;code&gt;!dw&lt;/code&gt;命令来完成这个操作，该命令代表的&lt;code&gt;dump physical word&lt;/code&gt;（感叹号代表物理）。这个命令要求你指定一个缓存类型，在我们的情况下，总是使用&lt;code&gt;[uc]&lt;/code&gt;或者说未缓存。它还提供了一个长度，这是由 L1 指定要读取的 word 的数量。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：请务必始终将目标设备内存的大小与我们从软件中读取的大小相匹配。这意味着，如果我们要读取的值是 16 位值（如供应商 ID），则必须执行 16 位读取。执行 32 位读取可能会更改设备响应的结果。对于配置空间，我们可以读取供应商 ID 的更大大小，但并非在所有情况下都是如此。最好养成将读取大小与目标大小匹配的习惯，以避免任何意外结果。请记住：设备内存不是 RAM。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;综上所述，我们读取 &lt;code&gt;00：00.0&lt;/code&gt; 的 VendorID，如下所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !dw &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;uc&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; D0000000 L1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#d0000000 8086&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们读取的结果值为 &lt;code&gt;0x8086&lt;/code&gt;，它恰好是 &lt;code&gt;Intel&lt;/code&gt; 的供应商 ID。为了验证这是正确的，让我们使用 &lt;code&gt;！pci&lt;/code&gt; 转储相同的内容。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !pci &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PCI Configuration Space &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Segment:0000 Bus:00 Device:00 Function:00&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Common Header:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    00: VendorID       &lt;span class=&#34;m&#34;&gt;8086&lt;/span&gt; Intel Corporation
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;从特定函数读取-vendorid&#34;&gt;从特定函数读取 VendorID&lt;/h3&gt;
&lt;p&gt;现在要计算我们希望与之通信的另一个函数（&lt;code&gt;02：00.1&lt;/code&gt; 的 NVIDIA 卡）的 ECAM 地址，我们需要通过使用目标函数的 BDF 和一些位数学计算到 ECAM 的偏移量来手动执行“数组访问”。&lt;/p&gt;
&lt;p&gt;计算方法存在于 PCIe 规范中，该规范为总线、器件和函数分配了一定数量的 ECAM 位来计算偏移量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;27&lt;/span&gt; - &lt;span class=&#34;m&#34;&gt;20&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;19&lt;/span&gt; - &lt;span class=&#34;m&#34;&gt;15&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;14&lt;/span&gt; - &lt;span class=&#34;m&#34;&gt;12&lt;/span&gt;     &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;11&lt;/span&gt; - &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Bus Nr  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Dev Nr  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Function Nr &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Register      &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过填写 BDF 并根据每个元素的位位置对结果进行移位和 OR 运算，我们可以计算出要添加到 ECAM 的偏移量。&lt;/p&gt;
&lt;p&gt;我将使用 python，但你可以使用任何你想要的计算器：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt;&amp;gt;&amp;gt; hex&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0xD0000000 + &lt;span class=&#34;o&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;lt;&amp;lt; 20) | (0 &amp;lt;&amp;lt; 15) | (1 &amp;lt;&amp;lt; 12)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;0xd020&lt;/span&gt;1000&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这意味着 &lt;code&gt;02：00.1&lt;/code&gt; 的 ECAM 区域位于 &lt;code&gt;0xD0201000&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;现在，要从函数中读取 VendorID 的值：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8: kd&amp;gt; !dw &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;uc&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; D0201000 L1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#d0201000 10de&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果是 &lt;code&gt;0x10de&lt;/code&gt;，我们从上面知道它是 NVIDIA Corporation！这意味着我们成功地从 ECAM 中读取了此函数的第一个值。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;这篇帖子最终比我预期的要长得多！我不会继续这篇文章，而是将其拆分并随着时间的推移充实该系列。关于 PCIe，我想介绍的主题太多了，但空闲时间却很少，但在下一篇文章中，我将更详细地介绍设备 BAR（一种特定于设备的 MMIO 形式）和 DMA（直接内存访问）。本系列将继续使用与以前相同的租户，更侧重于理解而不是具体细节。&lt;/p&gt;
&lt;p&gt;希望你喜欢这个对 PCIe 世界的小小了解！期待更多精彩。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://lifeislife.cn/posts/%E8%AF%91%E6%96%87-pcie-part2/&#34;&gt;单击此处查看第 2 部分！&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<blockquote>
<p>本文翻译自：<a href="https://ctf.re/windows/kernel/pcie/tutorial/2023/02/14/pcie-part-1/">面向 Windows 初学者的 PCIe 实用教程（第 1 部分）– 灵魂的逆向工程 &mdash; A Practical Tutorial on PCIe for Total Beginners on Windows (Part 1) – Reversing Engineering for the Soul</a></p>
</blockquote>
<p>Hello！我最近一直在与一些朋友和同事交谈，他们有兴趣了解更多关于 PCIe 的信息，但对复杂性或缺乏适合初学者的简单资源感到害怕。我最近经常使用 PCIe，觉得可能值得以博客文章的形式分享我的一些经验。</p>
<p>本文主要供具有计算机系统背景并且喜欢亲身实践的人员使用。它也适用于 PCIe 的初学者，或者是对通用概念有所理解但却无法将它们联系在一起的人。</p>
<p>第一件事是第一件事：不要被吓倒。有很多首字母缩略词和令人困惑的概念，当你&quot;明白&quot;时，它们就会变得简单。当时要迈出一步，不要害怕提出问题！（如果你想问我问题，可以考虑在 <a href="https://discord.gg/rtfm">Reverse Engineering Discord</a> 的 #hardware 频道@Gbps ping 我）</p>
<p>我<strong>打算</strong>在这个系列中做几件事：</p>
<ul>
<li>从软件方面将 PCIe 分解为我认为最重要的内容，以学习和为现代 PC/服务器系统构建一个良好的基线思想模型。</li>
<li>展示使用各种工具（通常是 WinDbg）在 Windows 上调查 PCIe 层次结构和设备的实际示例。</li>
<li>为避免造成混淆，我会有意识地简化或略过一些具体的细节。在这里，可能会有一些术语的使用不准确，甚至信息本身也可能在技术上有所出入。但是，这样做的目的是为了学习整个系统，而不是规范的具体细节。PCIe 是复杂的，当我们处于初学阶段时，陷入过多的细节和特殊情况是没有意义的。</li>
<li>我们希望通过将这项技术与你已经熟悉的概念相联系，来揭开其神秘面纱。PCIe 并未重新发明轮子，通过理解与它类似的技术，你可能已经比你自己意识到的了解得更多。</li>
</ul>
<p>我<strong>不打算</strong>用这个系列做以下事情：</p>
<ul>
<li>详细了解传统 PCI 或 PCI-X。一般来说，这项技术除了历史价值之外并不重要。</li>
<li>演示如何为 PCIe 设备编写设备驱动程序。这是非常特定于操作系统的，并且比这里要讨论的要高得多。</li>
<li>详细介绍 PCIe 的链路层。该规范的一半以上都花在了这个主题上，并包含了一些世界上最前沿的高速数据传输技术。我不处理这边的事情，但是将来我可能会谈论使用 FPGA 构建 PCIe 设备（我以前做过）。</li>
<li>帮助你使用 PCIe 在视频游戏中作弊。是的，它存在。不，我不会帮忙。</li>
</ul>
<p>这并不是对技术或协议的全面研究。要获得真正详尽的了解，你应该参考永远难以捉摸的 PCI-SIG PCI Express 基本规范。这是实现所有 PCIe 代码所依据的规范。目前，在撰写本文时，我们使用的是该规范的 6.0 版，但 3.0 及更高版本的版本都与现代 PCIe 完全相关。如何获得这种昂贵的规格对读者来说是一项练习。</p>
<blockquote>
<p>注意：我有时会在“PCI”和“PCIe”之间来回切换，将技术描述为一种习惯的力量。除非另有说明，否则本系列中的所有内容都是关于 PCIe 的。</p>
</blockquote>
<h2 id="什么是-pcie我为什么要关注">什么是 PCIe，我为什么要关注？</h2>
<p>PCIe 代表 Peripheral Component Interconnect Express，外围设备组件互联传输。它于 2003 年首次推出，是从早期 PC 时代越来越流行的旧 PCI 和 PCI-X 规范演变而来的（为 Express 添加了“e”以区分它）。</p>
<p>大多数使用计算机的人都认为它是主板上插入显卡或适配器卡的 PCIe 插槽，但 PCIe 不仅仅是这几个扩展 Port。PCIe 是现代 CPU 与连接到系统的几乎所有设备通信的基础。</p>
<p>自推出以来，PCIe 的受欢迎程度飙升，成为短距离高速数据传输的近乎通用的标准。几乎所有的 M.2 SSD 都使用 NVMe over PCIe 作为其传输协议。Thunderbolt 3 能够使用外部线将 PCIe 设备直接动态热插拔到系统（支持扩展坞和 eGPU 等技术）。在此基础上，USB4 正在扩展 Thunderbolt 3，以使这种 PCIe 路由技术能够达到开放的 USB 规范。CXL 等新型传输协议，用于数据中心服务器，以 PCIe 为基础规范并在其上扩展他们的特别功能。</p>
<p>即使与之通信的设备本身不使用 PCIe 作为其物理层协议，系统仍必须使用 PCI 的软件接口进行通信。这是因为系统使用适配器（通常称为主机控制器），这些适配器是 PCI 设备，有助于将来自 CPU 的 PCI 请求转换为主机控制器支持的任何协议或总线。例如，此测试计算机上的所有 USB 3.1 都使用 USB XHCI 协议，该协议是一种通信协议，通过与 USB 主机控制器通信的 PCI 驱动程序将 PCIe 桥接到 USB。</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//2024/08/31/4ab558e7fb773c276583ff528ede9df0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/4ab558e7fb773c276583ff528ede9df0.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>毋庸置疑，PCI 如今无处不在，并且已被计算机世界的各个部分完全采用。因此，我们必须对这项技术有很好的理解，以更好地理解现代计算。</p>
<h2 id="研究-pcie-层次结构---一种分组交换网络">研究 PCIe 层次结构 - 一种分组交换网络</h2>
<p>从传统的 PCI 转变到 PCIe 最重要的变化是从真正的总线拓扑结构转变为点对点链接。你可以将这看作是以太网集线器向今天的以太网交换机的演变。每个链接都是一个单独的点对点链接，其路由方式就像在一个分组交换的以太网网络上的以太网线一样。这意味着 PCIe 实际上并不是一个“总线协议”，尽管在各种文献和技术规范中让人困惑的频繁使用“总线”这个词。人们必须仔细理解，这个词“总线”并不意味着多个 PCIe 设备在同一个物理链接上进行通信。数据包（也被称为 TLPs）经过每个单独的链接，层次结构中的交换设备使用数据包内的路由信息将数据包传送到正确的 Port。</p>
<p>在我们进入 PCIe 的技术细节之前，首先我们需要谈谈整个系统的布局。我们研究 PCIe 层次结构的第一种方法是通过 Windows 设备管理器。大多数熟悉 Windows 的人以前都用过它，但没有多少人知道 View &gt; Devices by Connection 中发现的非常方便的功能。</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//2024/08/31/5e0afe87ec5a2f1049d03ceca8038673.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/5e0afe87ec5a2f1049d03ceca8038673.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>通过选择此视图，我们可以从根 PNP（Plug-N-Play）节点看到系统的完整拓扑。PNP 根节点是 Windows 上所有设备树的根，无论它们使用什么总线或协议。每个设备（无论是虚拟设备还是物理设备）都被枚举并放置在此 PNP 树上。我们可以利用 Device Manager 的这个视图来查看这个树的布局。</p>
<p>特别是，我们希望在系统上找到 PCI 设备的布局。这样，我们就可以开始构建 PCI 树在这台机器上的外观的可视化模型。为此，我们需要找到 PCI 树的根：RC。RC（缩写为 RC）是系统上所有 PCIe 的所有者。它物理上位于 CPU 芯片上，负责充当所有 PCIe 设备接收和发送数据包的主机。它可以被认为是软件（在你的机器上执行的指令）和硬件（PCIe 和 RAM 的外部世界）之间的桥梁。</p>
<p>在这个系统中，它位于这里的 PNP 层次结构中：</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//2024/08/31/485cfac384549d6d081e49b7c94e55f8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/485cfac384549d6d081e49b7c94e55f8.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>
<p>注意：你现在可能会问：“如果 PCI 主导了一切，为什么 PCI 根复合物不在树的顶部？答案是由于 PCIe 总线不是启动期间固件提供的系统初始布局。相反，ACPI（高级配置和电源接口）是描述 PCIe 到操作系统存在的东西。虽然你永远不会在 PC 中看到它，但可以描述一个没有 PCI 总线的系统，所有内容都完全由 ACPI 提供。我们稍后会详细讨论 ACPI，但现在不要太担心这个，只要知道 ACPI 是固件告诉我们RC在哪里的方式，然后帮助操作系统枚举树中的 PCI 设备。</p>
</blockquote>
<p>所以现在我们知道 RC 是 PCIe 树的顶部，现在让我们看一下它下面的所有内容：</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//2024/08/31/8168e2ee37f0a29a65ce5e35bcae0361.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8168e2ee37f0a29a65ce5e35bcae0361.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>不出所料，此 PCI 总线上有许多设备。在这里，我们可以看到负责音频、集成显卡、USB、串行和 SATA 的各种控制器。此外，我们还看到其中一些设备称为 PCI Express Root Port。Root Port 是RC上的一个 Port，另一个 PCIe 端点（又名物理“设备”）或交换机（又名“路由器”）可以连接到该 Port。出于 PCI 规范的考虑，你将听到 Endpoint 称为 Type 0 设备，而 Switch（或网桥）称为 Type 1 设备，因为一个被配置为用于通信的设备，另一个被配置为用于路由数据包的设备。RC 将具有与其物理支持的一样多的 Root Port。也就是说，可以连接到 CPU 芯片的次数越多。CPU 上的一些 Root Port 可能直接路由到物理 PCIe 插槽，而其他 Root Port 可能路由到其他类型的插槽，如 NVMe 插槽。它也可能被路由到另一个 PCIe 交换设备，该设备可以将数据包路由到多个 Port，从而一次路由到多个端点。</p>
<p>我会继续提出这个比较，但我觉得这很重要——如果你已经了解以太网交换机，你就已经了解 PCIe 交换机。你可以想象这些 Root Port 就像台式计算机上的以太网 Port。你可以将这些直接连接到其他设备（例如摄像头），也可以将它们连接到像家用路由器/调制解调器这样的交换机，这将交换数据包以公开更多连接，以便与更多设备和机器通信。在这种情况下，以太网线是将一个 PCIe Port 连接到另一个 PCIe Port 的铜线，从而使其成为“点对点”。</p>
<p>考虑到这一点，让我们开始绘制这个层次结构（部分）图表，以便我们直观地看到它的全部布局：</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//2024/08/31/f9830973b55ef87fb9bf88615fb6c3cd.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/f9830973b55ef87fb9bf88615fb6c3cd.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>在 PCI 中，系统上的所有“总线”都用 0 到 255（含）之间的数字标识。此外，所有设备都使用“设备 ID”和“功能 ID”进行标识。这通常被描述为 Bus/Device/Function，或简称为 BDF。在更正确的规范术语中，这称为 RID（请求者 ID）。为了减少混淆，我将它称为 BDF。BDF 很重要，因为它专门告诉我们设备在 PCIe 层次结构中的位置，以便我们可以与之通信。</p>
<p>因为这些都位于层级结构的顶层，所以我们将为这个“bus”提供一个数字标识符，即“Bus 0”或 Root Bus。我们可以通过右键单击顶级设备并选择 Properties 并查看 Location 来验证所有这些设备是否都是 Bus 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//2024/08/31/369d7cf106107b3aa9ee08b6617e6af1.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/369d7cf106107b3aa9ee08b6617e6af1.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>对于此集成图形设备，它的 BDF 为 0:2.0。它位于总线 0（根总线）上，设备 ID 为 2，功能 ID 为 0。在这种情况下，“设备”表示物理设备，例如显卡。“功能”是物理设备向系统公开的独特功能。无论出于何种意图和目的，都可以将其视为一个单独的实体。公开多个功能的设备被恰当地称为多功能设备（MFD）。这意味着它向系统公开两个或多个 PCI 连接，而实际上只有一个设备。我们很快就会看到一个真正的 MFD 示例。</p>
<p>敏锐的读者会注意到，我们已经打破了我之前提到的“规则”：与这个独特的总线 0 相连的设备有很多。这是 PCIe 中“点对点”规则的第一个例外，只有在因为总线 0 物理上位于 CPU 的硅片上的情况下才允许这样做。也就是说，这些设备之间没有电气路径，这是一个想象中的连接。所有这些设备都存在于 CPU 封装内，并使用极高速电气互连进行路由。这些处理器互连使用的是特定于 CPU 供应商的内部协议，尽管这些协议并未公开文档，但我们仍然以 PCIe 的“语言”与它进行通信。这些端点（标记为绿色），由于其特殊性质，将被赋予一个特殊的名称：根复合集成端点（RC Integrated Endpoints，简称 RCIE），因为它们直接集成在 RC 上。</p>
<p>这并不奇怪，你会期望集成 UHD 图形等设备将物理位于 CPU 上（因为它是 CPU 规格的一部分）。但是，我们可以通过观察其他 RCIE 来了解系统的一些更有趣的拓扑结构，例如这里也存在 RAM 控制器（与内存的 DRAM DIMM 通信的硅）和 USB 控制器（与外部 USB 设备通信的硅）。这就是为什么某些 CPU 仅支持某些类型的 RAM 和 USB 规范的原因——因为通信的设备在物理上位于 CPU 上，并且仅支持它们在物理上创建时要支持的规范。</p>
<blockquote>
<p>更新：这种说法是不正确的。一些 IO 控制器仍然可以在称为 PCH（Intel）或也称为芯片组（AMD）的分立芯片上找到，该芯片位于 CPU 附近，并且具有高速链路，使其看起来像是集成到 CPU 芯片中。上面这句话错误地说你可以在物理 CPU 上找到 USB 控制器，而它更有可能在“芯片组”上。但是，为了提高速度，与 RAM 通信的内存控制器位于 CPU 芯片上。</p>
</blockquote>
<p>此图是层次结构第一级的最小化版本，但现在让我们通过在设备管理器中展开其余的 Root Ports 来构建层次结构的其余部分。</p>
<p>这是填充的图表的样子：</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//2024/08/31/8d9ee9bf28e10363f2b2f42b55e2e404.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/8d9ee9bf28e10363f2b2f42b55e2e404.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>
<p>注意：我已经标记了 UHD Graphics 设备和总线 0 的 BDF。</p>
</blockquote>
<p>这些 Root Port 物理上位于 CPU 上，但连接到它的设备并不在其中。这台机器的外部 PCIe 插槽上连接了 3 个设备：一块 NVIDIA Quadro P400 图形卡和两个 NVMe 驱动器。通过进入设备管理器中每个设备的属性，我们可以获取并更新它们在视觉上的 BDF(总线、设备、功能) 信息。</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//2024/08/31/3ad55c7a1e1a1482a63679a009d6c414.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/3ad55c7a1e1a1482a63679a009d6c414.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>在每个 Root Port 下，我们可以看到一个设备已物理连接。但是，我们还可以看到，我们在每个 Bus 下都公开了一个新的 Bus。Root Port 充当了桥，它将我们从总线 0 桥接到新的总线，因此必须为新总线分配一个新的数字 ID，并且该 Port 下的所有设备/功能都将继承该新总线编号。这与 OS/固件在引导期间的总线枚举期间使用的逻辑相同：所有网桥和交换机都公开一条新总线，必须为其分配新的总线 ID 号。</p>
<p>在这种情况下，我们还可以看到一个多功能设备的好例子。Quadro P400 显卡充当具有两种功能的 MFD。第一个函数是 0（BDF 01:00.0），是显卡设备本身。第二个功能是 1（BDF 01:00.1），它是音频控制器，允许从 HDMI 等 Port 播放音频。这两个功能是不同的——它们用于完全不同的目的，并且具有与之关联的单独驱动程序和配置，但它们仍然由相同的物理设备（即设备 0）实现，并且位于同一总线（即总线 1）上。这与 PCIe 的点对点规则是一致的，一个链路上只能连接一个物理设备，因此总线上只能存在一个物理设备（除了例外，总线 0）。</p>
<h2 id="从-windbg-探索-pcie-层次结构和设备">从 WinDbg 探索 PCIe 层次结构和设备</h2>
<p>到目前为止，我们已经通过使用 Device Manager 的“View by Connection”功能看到了标准的 PCI 总线层次结构。还有另一种更详细的方法来调查 PCIe 层次结构：使用 WinDbg 提供的可靠内核调试扩展。</p>
<blockquote>
<p>注意：我们假设你了解如何在一台机器上设置内核调试器来继续下面的操作。你也可以用 LiveKD 来完成大部分练习。如果你并不了解如何设置，可以参考微软提供的指南：<a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection-automatically">设置 KDNET</a>。</p>
</blockquote>
<p>我已经连接到了一台与上述使用的机器不同的新测试机。我们将通过调试器的输出，来演练如何绘制这台机器的层次结构图。我们也将学习如何通过其配置内存来查找设备的信息。</p>
<p>放入调试器后，我们将使用！pcitree 命令开始。这将转储系统上列举的 PCI 设备的文本树形图。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">8: kd&gt; !pcitree
</span></span><span class="line"><span class="cl">Bus 0x0 <span class="o">(</span>FDO Ext ffffdc89b9f75920<span class="o">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f00 devext 0xffffdc89b0759270 devstack 0xffffdc89b0759120 <span class="m">0600</span> Bridge/HOST to PCI
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f02 devext 0xffffdc89ba0c74c0 devstack 0xffffdc89ba0c7370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x1 <span class="o">(</span>FDO Ext ffffdc89ba0aa190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    No devices have been enumerated on this bus.
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>2,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f04 devext 0xffffdc89ba0c94c0 devstack 0xffffdc89ba0c9370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x2 <span class="o">(</span>FDO Ext ffffdc89ba0a8190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 10de13bb devext 0xffffdc89ba04f270 devstack 0xffffdc89ba04f120 <span class="m">0300</span> Display Controller/VGA
</span></span><span class="line"><span class="cl">    <span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>1<span class="o">)</span> 10de0fbc devext 0xffffdc89ba051270 devstack 0xffffdc89ba051120 <span class="m">0403</span> Multimedia Device/Unknown Sub Class
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>3,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f08 devext 0xffffdc89ba0cb4c0 devstack 0xffffdc89ba0cb370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x3 <span class="o">(</span>FDO Ext ffffdc89ba08f190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    No devices have been enumerated on this bus.
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>5,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f28 devext 0xffffdc89ba0cd4c0 devstack 0xffffdc89ba0cd370 <span class="m">0880</span> Base System Device/<span class="s1">&#39;Other&#39;</span> base system device
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>5,  <span class="nv">f</span><span class="o">=</span>1<span class="o">)</span> 80866f29 devext 0xffffdc89ba0cf4c0 devstack 0xffffdc89ba0cf370 <span class="m">0880</span> Base System Device/<span class="s1">&#39;Other&#39;</span> base system device
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>5,  <span class="nv">f</span><span class="o">=</span>2<span class="o">)</span> 80866f2a devext 0xffffdc89ba0d14c0 devstack 0xffffdc89ba0d1370 <span class="m">0880</span> Base System Device/<span class="s1">&#39;Other&#39;</span> base system device
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>5,  <span class="nv">f</span><span class="o">=</span>4<span class="o">)</span> 80866f2c devext 0xffffdc89ba0d34c0 devstack 0xffffdc89ba0d3370 <span class="m">0800</span> Base System Device/Interrupt Controller
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>11, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d7c devext 0xffffdc89ba0d84c0 devstack 0xffffdc89ba0d8370 ff00 <span class="o">(</span>Explicitly<span class="o">)</span> Undefined/Unknown Sub Class
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>11, <span class="nv">f</span><span class="o">=</span>4<span class="o">)</span> 80868d62 devext 0xffffdc89ba0da4c0 devstack 0xffffdc89ba0da370 <span class="m">0106</span> Mass Storage Controller/Unknown Sub Class
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>14, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d31 devext 0xffffdc89ba0dc4c0 devstack 0xffffdc89ba0dc370 0c03 Serial Bus Controller/USB
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>16, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d3a devext 0xffffdc89ba0de4c0 devstack 0xffffdc89ba0de370 <span class="m">0780</span> Simple Serial Communications Controller/<span class="s1">&#39;Other&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>16, <span class="nv">f</span><span class="o">=</span>3<span class="o">)</span> 80868d3d devext 0xffffdc89ba0e04c0 devstack 0xffffdc89ba0e0370 <span class="m">0700</span> Simple Serial Communications Controller/Serial Port
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>19, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 808615a0 devext 0xffffdc89ba0e24c0 devstack 0xffffdc89ba0e2370 <span class="m">0200</span> Network Controller/Ethernet
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1a, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d2d devext 0xffffdc89ba0e44c0 devstack 0xffffdc89ba0e4370 0c03 Serial Bus Controller/USB
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1b, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d20 devext 0xffffdc89ba0254c0 devstack 0xffffdc89ba025370 <span class="m">0403</span> Multimedia Device/Unknown Sub Class
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1c, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d10 devext 0xffffdc89ba0274c0 devstack 0xffffdc89ba027370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x4 <span class="o">(</span>FDO Ext ffffdc89ba0a9190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    No devices have been enumerated on this bus.
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1c, <span class="nv">f</span><span class="o">=</span>1<span class="o">)</span> 80868d12 devext 0xffffdc89ba02c4c0 devstack 0xffffdc89ba02c370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x5 <span class="o">(</span>FDO Ext ffffdc89b9fe6190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    No devices have been enumerated on this bus.
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1c, <span class="nv">f</span><span class="o">=</span>3<span class="o">)</span> 80868d16 devext 0xffffdc89ba02e4c0 devstack 0xffffdc89ba02e370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x6 <span class="o">(</span>FDO Ext ffffdc89ba0a7190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> <span class="m">12838893</span> devext 0xffffdc89ba062270 devstack 0xffffdc89ba062120 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">    Bus 0x7 <span class="o">(</span>FDO Ext ffffdc89ba064250<span class="o">)</span>
</span></span><span class="line"><span class="cl">      No devices have been enumerated on this bus.
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1c, <span class="nv">f</span><span class="o">=</span>4<span class="o">)</span> 80868d18 devext 0xffffdc89ba0304c0 devstack 0xffffdc89ba030370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x8 <span class="o">(</span>FDO Ext ffffdc89ba0b2190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    No devices have been enumerated on this bus.
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1d, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d26 devext 0xffffdc89ba0364c0 devstack 0xffffdc89ba036370 0c03 Serial Bus Controller/USB
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1f, <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80868d44 devext 0xffffdc89ba0384c0 devstack 0xffffdc89ba038370 <span class="m">0601</span> Bridge/PCI to ISA
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1f, <span class="nv">f</span><span class="o">=</span>2<span class="o">)</span> 80868d02 devext 0xffffdc89ba03a4c0 devstack 0xffffdc89ba03a370 <span class="m">0106</span> Mass Storage Controller/Unknown Sub Class
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1f, <span class="nv">f</span><span class="o">=</span>3<span class="o">)</span> 80868d22 devext 0xffffdc89ba03c4c0 devstack 0xffffdc89ba03c370 0c05 Serial Bus Controller/Unknown Sub Class
</span></span></code></pre></div><blockquote>
<p>注意：如果你遇到“无法获取 PciFdoExtensionListHead 地址”的错误，确保你的符号设置正确，并执行.reload pci.sys 操作来重新加载 PCI 的符号。</p>
</blockquote>
<p>当显示此输出时，由于空格的格式设置方式，可能很难直观地看到“tree”。解释此输出的方法是查看 Bus 0x 文本的缩进。任何比 Bus 0x 行进一步缩进一组空格的东西都是该总线上的设备。我们可以看到，在器件的正下方还有其他 Bus 0x 线路。这意味着 Bus 0x 线上方的器件正在向我们公开一条新总线，并且总线编号在那里给出。</p>
<p>让我们看一下此输出的特定部分：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Bus 0x0 <span class="o">(</span>FDO Ext ffffdc89b9f75920<span class="o">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f00 devext 0xffffdc89b0759270 devstack 0xffffdc89b0759120 <span class="m">0600</span> Bridge/HOST to PCI
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>1,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f02 devext 0xffffdc89ba0c74c0 devstack 0xffffdc89ba0c7370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x1 <span class="o">(</span>FDO Ext ffffdc89ba0aa190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    No devices have been enumerated on this bus.
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>2,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f04 devext 0xffffdc89ba0c94c0 devstack 0xffffdc89ba0c9370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x2 <span class="o">(</span>FDO Ext ffffdc89ba0a8190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 10de13bb devext 0xffffdc89ba04f270 devstack 0xffffdc89ba04f120 <span class="m">0300</span> Display Controller/VGA
</span></span><span class="line"><span class="cl">    <span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>1<span class="o">)</span> 10de0fbc devext 0xffffdc89ba051270 devstack 0xffffdc89ba051120 <span class="m">0403</span> Multimedia Device/Unknown Sub Class
</span></span><span class="line"><span class="cl">  <span class="o">(</span><span class="nv">d</span><span class="o">=</span>3,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 80866f08 devext 0xffffdc89ba0cb4c0 devstack 0xffffdc89ba0cb370 <span class="m">0604</span> Bridge/PCI to PCI
</span></span><span class="line"><span class="cl">  Bus 0x3 <span class="o">(</span>FDO Ext ffffdc89ba08f190<span class="o">)</span>
</span></span><span class="line"><span class="cl">    No devices have been enumerated on this bus.
</span></span></code></pre></div><p>在此输出中，我们可以看到每个设备显示的 BDF。我们还可以看到总线 0 上存在的一组 Root Port，这些 Port 下面没有列举任何设备，这意味着插槽尚未连接到任何设备。</p>
<p>在这里看到树结构应该更容易，但无论如何，让我们把它画出来：</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//2024/08/31/7a507a4048a8a03806ced4cd6714bbf7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7a507a4048a8a03806ced4cd6714bbf7.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>
<p>注意：这只是一个巧合，即公交号恰好与桥梁/PCI 的设备编号匹配到 PCI 端口。</p>
</blockquote>
<p>如你现在所知，标记为 Bridge/PCI to PCI 的设备实际上是 Root Port，而总线 2 上的设备实际上是一个多功能设备。与设备管理器不同，我们看不到！pcitree 中的设备真实名称。相反，我们只得到了一个通用的 PCI 名称，用于设备“类型”将自己宣传为什么。这是因为设备管理器 从驱动程序读取设备名称，而不是直接从 PCI 读取设备名称。</p>
<p>要了解更多关于这个显示控制器设备的信息，我们可以使用命令 <code>！devext [pointer]</code>，其中 <code>[pointer]</code> 是布局中单词 <code>devext</code> 后面的值。在本例中，它是：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">(</span><span class="nv">d</span><span class="o">=</span>0,  <span class="nv">f</span><span class="o">=</span>0<span class="o">)</span> 10de13bb devext 0xffffdc89ba04f270 devstack 0xffffdc89ba04f120 <span class="m">0300</span> Display Controller/VGA
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">!devext 0xffffdc89ba04f270
</span></span></code></pre></div><p>从这里，我们将从 Windows 中的 PCI 总线驱动程序获得此 PCI 设备的摘要的打印输出，<code>pci.sys</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">8: kd&gt; !devext 0xffffdc89ba04f270
</span></span><span class="line"><span class="cl">PDO Extension, Bus 0x2, Device 0, Function 0.
</span></span><span class="line"><span class="cl">  DevObj 0xffffdc89ba04f120  Parent FDO DevExt 0xffffdc89ba0a8190
</span></span><span class="line"><span class="cl">  Device <span class="nv">State</span> <span class="o">=</span> PciStarted
</span></span><span class="line"><span class="cl">  Vendor ID 10de <span class="o">(</span>NVIDIA CORPORATION<span class="o">)</span>  Device ID 13BB
</span></span><span class="line"><span class="cl">  Subsystem Vendor ID 103c <span class="o">(</span>HEWLETT-PACKARD COMPANY<span class="o">)</span>  Subsystem ID <span class="m">1098</span>
</span></span><span class="line"><span class="cl">  Header Type 0, Class Base/Sub 03/00  <span class="o">(</span>Display Controller/VGA<span class="o">)</span>
</span></span><span class="line"><span class="cl">  Programming Interface: 00, Revision: a2, IntPin: 01, RawLine <span class="m">00</span>
</span></span><span class="line"><span class="cl">  Possible Decodes <span class="o">((</span>cmd <span class="p">&amp;</span> 7<span class="o">)</span> <span class="o">=</span> 7<span class="o">)</span>: BMI
</span></span><span class="line"><span class="cl">  Capabilities: <span class="nv">Ptr</span><span class="o">=</span>60, power msi express 
</span></span><span class="line"><span class="cl">  Express capabilities: <span class="o">(</span>BIOS controlled<span class="o">)</span> 
</span></span><span class="line"><span class="cl">  Logical Device Power State: D0
</span></span><span class="line"><span class="cl">  Device Wake Level:          Unspecified
</span></span><span class="line"><span class="cl">  WaitWakeIrp:                &lt;none&gt;
</span></span><span class="line"><span class="cl">  Requirements:     Alignment Length    Minimum          Maximum
</span></span><span class="line"><span class="cl">    BAR0    Mem:    <span class="m">01000000</span>  <span class="m">01000000</span>  <span class="m">0000000000000000</span> 00000000ffffffff
</span></span><span class="line"><span class="cl">    BAR1    Mem:    <span class="m">10000000</span>  <span class="m">10000000</span>  <span class="m">0000000000000000</span> ffffffffffffffff
</span></span><span class="line"><span class="cl">    BAR3    Mem:    <span class="m">02000000</span>  <span class="m">02000000</span>  <span class="m">0000000000000000</span> ffffffffffffffff
</span></span><span class="line"><span class="cl">    BAR5     Io:    <span class="m">00000080</span>  <span class="m">00000080</span>  <span class="m">0000000000000000</span> 00000000ffffffff
</span></span><span class="line"><span class="cl">      ROM BAR:      <span class="m">00080000</span>  <span class="m">00080000</span>  <span class="m">0000000000000000</span> 00000000ffffffff
</span></span><span class="line"><span class="cl">    VF BAR0 Mem:    <span class="m">00080000</span>  <span class="m">00080000</span>  <span class="m">0000000000000000</span> 00000000ffffffff
</span></span><span class="line"><span class="cl">  Resources:        Start            Length
</span></span><span class="line"><span class="cl">    BAR0    Mem:    00000000f2000000 <span class="m">01000000</span>
</span></span><span class="line"><span class="cl">    BAR1    Mem:    00000000e0000000 <span class="m">10000000</span>
</span></span><span class="line"><span class="cl">    BAR3    Mem:    00000000f0000000 <span class="m">02000000</span>
</span></span><span class="line"><span class="cl">    BAR5     Io:    <span class="m">0000000000001000</span> <span class="m">00000080</span>
</span></span><span class="line"><span class="cl">  Interrupt Requirement:
</span></span><span class="line"><span class="cl">    Line Based - Min <span class="nv">Vector</span> <span class="o">=</span> 0x0, Max <span class="nv">Vector</span> <span class="o">=</span> 0xffffffff
</span></span><span class="line"><span class="cl">    Message Based: Type - Msi, 0x1 messages requested
</span></span><span class="line"><span class="cl">  Interrupt Resource:    Type - MSI, 0x1 Messages Granted
</span></span></code></pre></div><p>这里有很多内核知道的关于这个设备的信息。此信息是通过 配置空间（缩写为“config space”）检索的，配置空间 是系统上的内存部分，允许内核以标准化的方式枚举、查询信息和设置 PCI 设备。软件从设备读取内存以查询供应商 ID 等信息，设备（如果已打开电源）使用该信息进行响应。在下一节中，我将更多地讨论这实际上是如何发生的，但要知道这里查询的信息是从配置空间生成的。</p>
<p>因此，让我们分解一些重要的东西：</p>
<ul>
<li>DevObj：指向 <code>nt！_DEVICE_OBJECT</code> 结构的指针，该结构表示内核中的物理设备。</li>
<li>Vendor ID：注册给特定设备制造商的 16 位 ID 号。此值是标准化的，PCI-SIG 必须为新供应商分配一个唯一 ID，以便它们不会重叠。在本例中，我们看到这是 NVIDIA 显卡。</li>
<li>Device  ID：执行 PCIe 的特定芯片的 16 位 ID 号。类似的想法是，公司必须为其芯片请求一个唯一的 ID，这样它就不会与任何其他芯片冲突。</li>
<li>Subsystem Vendor ID：芯片所在电路板的供应商 ID。在这种情况下，“HP”是显卡的生产商，而“NVIDIA”设计了图形芯片。</li>
<li>Subsystem Device ID：芯片所在电路板的设备 ID。</li>
<li>Logical Device Power State：此设备的电源状态。PCI 中有两种主要的电源状态，D0 = 设备已通电，D3 = 设备处于低功耗状态或完全关闭。</li>
<li>Requirements：设备要求 OS 为其分配的内存要求。稍后会详细介绍。</li>
<li>Resources：操作系统分配给此设备的内存资源。此设备已打开电源并启动，因此已为其分配了资源。</li>
<li>Interrupt Requirement/Resource：与上述相同，但是对于中断则不同。</li>
</ul>
<p>要实际获取有关此设备的完整信息，我们可以使用 <a href="https://pcilookup.com/">PCI Lookup</a> 中的出色工具来查询有关在 PCI-SIG 中注册的 PCI 设备的公共信息。让我们将有关设备和 Vendor ID 的信息放入框中：</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//2024/08/31/a70cefe8ad2cbb145ce145c1b16e562d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/a70cefe8ad2cbb145ce145c1b16e562d.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>
<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//2024/08/31/098ff685f1e2ca1d27f87df7378a836a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/098ff685f1e2ca1d27f87df7378a836a.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>这告诉我们该设备是 NVIDIA 创建的 Quadro K620 显卡。子系统 ID 告诉我们，这个特定的卡 PCB 是由 HP 生产的，该公司已获得 NVIDIA 的许可。</p>
<p>我们在 <code>！devext</code> 中看到的很好地概述了 <code>pci.sys</code> 在摘要中特别关心向我们展示的内容，但它只触及了配置空间中所有信息的皮毛。要将所有信息转储到配置空间中，我们可以使用扩展名 <code>！pci 100 B D F</code>，其中 BDF 是我们相关设备的 BDF。100 是一组标志，指定我们要转储有关设备的所有信息。显示的信息将按照它在设备的 config space 中存在的顺序进行布局。每个部分的前缀是一个偏移量，例如 <code>02</code> 表示 Device ID。这指定了从中读取此值的 config 空间的偏移量。这些偏移量在 PCI 规范中进行了详细说明，并且不会出于向后兼容性目的在 PCI 版本之间更改。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">8: kd&gt; !pci <span class="m">100</span> <span class="m">2</span> <span class="m">0</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">PCI Configuration Space <span class="o">(</span>Segment:0000 Bus:02 Device:00 Function:00<span class="o">)</span>
</span></span><span class="line"><span class="cl">Common Header:
</span></span><span class="line"><span class="cl">    00: VendorID       10de Nvidia Corporation
</span></span><span class="line"><span class="cl">    02: DeviceID       13bb
</span></span><span class="line"><span class="cl">    04: Command        <span class="m">0507</span> IOSpaceEn MemSpaceEn BusInitiate SERREn InterruptDis 
</span></span><span class="line"><span class="cl">    06: Status         <span class="m">0010</span> CapList 
</span></span><span class="line"><span class="cl">    08: RevisionID     a2
</span></span><span class="line"><span class="cl">    09: ProgIF         <span class="m">00</span> VGA
</span></span><span class="line"><span class="cl">    0a: SubClass       <span class="m">00</span> VGA Compatible Controller
</span></span><span class="line"><span class="cl">    0b: BaseClass      <span class="m">03</span> Display Controller
</span></span><span class="line"><span class="cl">    0c: CacheLineSize  <span class="m">0000</span>
</span></span><span class="line"><span class="cl">    0d: LatencyTimer   <span class="m">00</span>
</span></span><span class="line"><span class="cl">    0e: HeaderType     <span class="m">80</span>
</span></span><span class="line"><span class="cl">    0f: BIST           <span class="m">00</span>
</span></span><span class="line"><span class="cl">    10: BAR0           f2000000
</span></span><span class="line"><span class="cl">    14: BAR1           e000000c
</span></span><span class="line"><span class="cl">    18: BAR2           <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    1c: BAR3           f000000c
</span></span><span class="line"><span class="cl">    20: BAR4           <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    24: BAR5           <span class="m">00001001</span>
</span></span><span class="line"><span class="cl">    28: CBCISPtr       <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    2c: SubSysVenID    103c
</span></span><span class="line"><span class="cl">    2e: SubSysID       <span class="m">1098</span>
</span></span><span class="line"><span class="cl">    30: ROMBAR         <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    34: CapPtr         <span class="m">60</span>
</span></span><span class="line"><span class="cl">    3c: IntLine        <span class="m">00</span>
</span></span><span class="line"><span class="cl">    3d: IntPin         <span class="m">01</span>
</span></span><span class="line"><span class="cl">    3e: MinGnt         <span class="m">00</span>
</span></span><span class="line"><span class="cl">    3f: MaxLat         <span class="m">00</span>
</span></span><span class="line"><span class="cl">Device Private:
</span></span><span class="line"><span class="cl">    40: 1098103c <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    50: <span class="m">00000000</span> <span class="m">00000001</span> 0023d6ce <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    60: <span class="m">00036801</span> <span class="m">00000008</span> <span class="m">00817805</span> fee001f8
</span></span><span class="line"><span class="cl">    70: <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00120010</span> 012c8de1
</span></span><span class="line"><span class="cl">    80: <span class="m">00003930</span> 00453d02 <span class="m">11010140</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    90: <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00040013</span>
</span></span><span class="line"><span class="cl">    a0: <span class="m">00000000</span> <span class="m">00000006</span> <span class="m">00000002</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    b0: <span class="m">00000000</span> <span class="m">01140009</span> <span class="m">00000000</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    c0: <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    d0: <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    e0: <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    f0: <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span> <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">Capabilities:
</span></span><span class="line"><span class="cl">    60: CapID          <span class="m">01</span> PwrMgmt Capability
</span></span><span class="line"><span class="cl">    61: NextPtr        <span class="m">68</span>
</span></span><span class="line"><span class="cl">    62: PwrMgmtCap     <span class="m">0003</span> <span class="nv">Version</span><span class="o">=</span><span class="m">3</span>
</span></span><span class="line"><span class="cl">    64: PwrMgmtCtrl    <span class="m">0008</span> DataScale:0 DataSel:0 D0 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    68: CapID          <span class="m">05</span> MSI Capability
</span></span><span class="line"><span class="cl">    69: NextPtr        <span class="m">78</span>
</span></span><span class="line"><span class="cl">    6a: MsgCtrl        64BitCapable MSIEnable MultipleMsgEnable:0 <span class="o">(</span>0x1<span class="o">)</span> MultipleMsgCapable:0 <span class="o">(</span>0x1<span class="o">)</span>
</span></span><span class="line"><span class="cl">    6c: MsgAddrLow     fee001f8
</span></span><span class="line"><span class="cl">    70: MsgAddrHi      <span class="m">0</span>
</span></span><span class="line"><span class="cl">    74: MsgData        <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    78: CapID          <span class="m">10</span> PCI Express Capability
</span></span><span class="line"><span class="cl">    79: NextPtr        <span class="m">00</span>
</span></span><span class="line"><span class="cl">    7a: Express Caps   <span class="m">0012</span> <span class="o">(</span>ver. 2<span class="o">)</span> Type:LegacyEP
</span></span><span class="line"><span class="cl">    7c: Device Caps    012c8de1
</span></span><span class="line"><span class="cl">    80: Device Control <span class="m">3930</span> bcre/flr MRR:1K NS ap pf ET MP:256 RO ur fe nf ce
</span></span><span class="line"><span class="cl">    82: Device Status  <span class="m">0000</span> tp ap ur fe nf ce
</span></span><span class="line"><span class="cl">    84: Link Caps      00453d02
</span></span><span class="line"><span class="cl">    88: Link Control   <span class="m">0140</span> es CC rl ld RCB:64 ASPM:None 
</span></span><span class="line"><span class="cl">    8a: Link Status    <span class="m">1101</span> SCC lt lte NLW:x16 LS:2.5 
</span></span><span class="line"><span class="cl">    9c: DeviceCaps2    <span class="m">00040013</span> CTR:3 CTDIS arifwd aor aoc32 aoc64 cas128 noro ltr TPH:0 OBFF:1 extfmt eetlp EETLPMax:0
</span></span><span class="line"><span class="cl">    a0: DeviceControl2 <span class="m">0000</span> CTVal:0 ctdis arifwd aor aoeb idoreq idocom ltr OBFF:0 eetlp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Enhanced Capabilities:
</span></span><span class="line"><span class="cl">    100: CapID         <span class="m">0002</span> Virtual Channel Capability
</span></span><span class="line"><span class="cl">         Version       <span class="m">1</span>
</span></span><span class="line"><span class="cl">         NextPtr       <span class="m">258</span>
</span></span><span class="line"><span class="cl">    0104: Port VC Capability <span class="m">1</span>        <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    0108: Port VC Capability <span class="m">2</span>        <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    010c: Port VC Control             <span class="m">0000</span>
</span></span><span class="line"><span class="cl">    010e: Port VC Status              <span class="m">0000</span>
</span></span><span class="line"><span class="cl">    0110: VC Resource<span class="o">[</span>0<span class="o">]</span> Cap          <span class="m">00000000</span>
</span></span><span class="line"><span class="cl">    0114: VC Resource<span class="o">[</span>0<span class="o">]</span> Control      800000ff
</span></span><span class="line"><span class="cl">    011a: VC Resource<span class="o">[</span>0<span class="o">]</span> Status       <span class="m">0000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    258: CapID         001e L1 PM SS Capability
</span></span><span class="line"><span class="cl">         Version       <span class="m">1</span>
</span></span><span class="line"><span class="cl">         NextPtr       <span class="m">128</span>
</span></span><span class="line"><span class="cl">    25c: Capabilities  0028ff1f  PTPOV:5 PTPOS:0 PCMRT:255 L1PMS ASPML11 ASPML12 PCIPML11 PCIPML12
</span></span><span class="line"><span class="cl">    260: Control1      <span class="m">00000000</span>  LTRL12TS:0 LTRL12TV:0 CMRT:0 aspml11 aspml12 pcipml11 pcipml12
</span></span><span class="line"><span class="cl">    264: Control2      <span class="m">00000028</span>  TPOV:5 TPOS:0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    128: CapID         <span class="m">0004</span> Power Budgeting Capability
</span></span><span class="line"><span class="cl">         Version       <span class="m">1</span>
</span></span><span class="line"><span class="cl">         NextPtr       <span class="m">600</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    600: CapID         000b Vendor Specific Capability
</span></span><span class="line"><span class="cl">         Version       <span class="m">1</span>
</span></span><span class="line"><span class="cl">         NextPtr       <span class="m">000</span>
</span></span><span class="line"><span class="cl">         Vendor Specific ID <span class="m">0001</span> - Ver. <span class="m">1</span>  Length: <span class="m">024</span>
</span></span></code></pre></div><p>这个视图的好处是，我们可以看到有关配置空间的 Capabilities 部分的详细信息。Capabilities 是 config 空间中的一组结构，它准确描述了 device 能够实现的功能。Capabilities 包括链接速度和设备支持的中断类型等信息。PCI 规范中添加的任何新功能都将通过这些结构进行公布，这些结构在配置空间中形成了一个功能链表，可以迭代以发现设备的所有功能。并非所有这些功能都与操作系统相关，有些功能仅与本文未涵盖的硬件方面相关。现在，我不会详细介绍该设备的功能。</p>
<h2 id="pcie一切都与内存相关">PCIe：一切都与内存相关</h2>
<p>现在我们已经研究了几个设备和 PCI 总线的层次结构，让我们谈谈与软件和 PCI 设备的通信实际上是如何运作的。当我第一次学习 PCI 时，我很难理解当软件与 PCI 设备连接时到底发生了什么。因为整个事务对作为软件开发人员的你来说是抽象出来的，所以很难仅通过从调试工具中探入 PCI 内存来构建所发生的事情的心智模型。希望这篇文章能提供比我刚开始时所能得到的更好的概述。</p>
<p>首先，我要做一个大胆的声明：<strong>所有现代 PCIe 通信都是通过内存读写完成的</strong>。如果你了解 PCIe 中的内存如何工作，你就会了解 PCIe 软件通信的工作原理。（是的，在某些平台上还有其他传统的通信方式，但我们不会讨论这些方式，因为它们已被弃用）。</p>
<p>现在，让我们谈谈现代平台上不同类型的内存。在启动的早期，操作系统的 CPU 将使用虚拟内存。也就是说，CPU 看到的内存地址是映射到物理内存世界的内存视图。</p>
<p>就我们的目的而言，系统上有两种类型的物理内存：</p>
<ul>
<li>RAM - 读取或写入时从计算机上的 DRAM DIMM 存储和检索的地址。这就是大多数人在想到“内存”时所想到的。</li>
<li>Device Memory（设备内存） - 在读取或写入时与系统上的设备“对话”的地址。这里的关键词是“对话”。它不会在设备上存储内存，也不会检索设备上的内存（尽管设备可能同时能够同时检索两者）。你可能正在与之通信的地址甚至可能根本不是内存，而是一个更抽象的“device register” ，用于配置设备的内部工作。这种访问会发生什么取决于设备。你所做的只是与设备通信。你通常会看到这称为 MMIO，它全称是 Memory-Mapped I/O。</li>
</ul>
<blockquote>
<p>注意：每当设备不响应设备内存区域中访问的地址时，PCI 的设备内存将始终读取“全 1”或“所有 FF”。这是了解设备何时实际响应的便捷方法。如果你看到所有 FF，则表示你正在读取无效的设备地址。</p>
</blockquote>
<p>初学者认为所有物理内存都是 RAM，这是错误的。当软件与 PCI 区域中的 PCI 设备通信时，它不会从 RAM 读取和写入数据。相反，该设备从 RC 接收一个数据包（TLP，传输层数据包），当 PCI 区域内的地址被读/写时，你的 CPU 会立即自动生成该数据包。你无需在软件中创建这些数据包，所有这些数据包都是在访问此内存后立即完全在后台生成的。在软件中，你甚至无法查看或捕获这些数据包，而需要一个特殊的硬件测试设备来拦截和查看正在发送的数据包。稍后会详细介绍。</p>
<p>如果有帮助，请将物理内存视为设备的映射。RAM 是为你映射到物理内存中的设备。PCI 还会自动为你映射区域。尽管它们截然不同且行为也非常不同，但它们在软件中看起来是相同的。</p>
<p>在下图中，我们可以看到典型系统如何将虚拟内存映射到物理内存。请注意，有两个 RAM 区域和两个 PCI 内存区域。这是因为某些较旧的 PCI 设备只能寻址 32 位内存。因此，如果你的 RAM 不适合 4GB 以下的地址窗口，则一些 RAM 会上移到 4GB 以上。由于你的处理器支持 64 位地址，因此这不是问题。此外，在 4GB 行上方为支持 64 位地址的 PCI 设备创建第二个窗口。由于 4GB 区域可能非常有限，因此设备最好在 4GB 以上移动尽可能多的内存，以免弄乱下面的空间。</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//2024/08/31/2f1b3cf2195ea201e631c0d29929398b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/2f1b3cf2195ea201e631c0d29929398b.png" alt=""  title="如何将虚拟地址范围映射到物理地址的非常简化的视图。这忽略了物理内存中的大量 “特殊” 区域，但展示了 RAM 和设备内存是如何不同的。" 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>（Configuration Space）。</p>
<p>配置空间位于一个名为 ECAM（Extended Configuration Access Management，扩展配置访问管理）的内存部分。因为它是一种设备内存，所以要从内核（使用虚拟内存）访问这段内存，内核必须请求内存管理器将这部分物理内存映射到一个虚拟地址上。然后，软件指令可以使用映射的虚拟地址来从物理地址读取和写入。在 Windows 上，定位和映射这段内存的工作部分由<code>pci.sys</code>处理，部分由<code>acpi.sys</code>处理，还有部分由内核（具体来说是 HAL）处理。</p>
<blockquote>
<p>注意：通常，在 Windows 中映射设备内存的方式是通过 <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmmapiospaceex">MmMapIoSpaceEx</a>，这是驱动程序可用于映射物理设备内存的 API。但是，为了进行配置空间访问，软件必须使用 <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-halgetbusdatabyoffset">HalGetBusDataByOffset</a> 和 <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-halsetbusdatabyoffset">HalSetBusDataByOffset</a> 来确保 <code>pci.sys</code> 的内部状态与你正在执行的配置空间读/写保持同步。如果你尝试自己映射和更改配置空间，则可能会使 <code>pci.sys</code> 状态不同步并导致蓝屏死机。</p>
</blockquote>
<blockquote>
<p>注意：ECAM/PCI 区域在物理内存中的位置取决于平台。引导时的固件将分配系统物理内存的所有特殊区域。然后，固件会在引导期间向操作系统公布这些区域的位置。在 x86-64 系统上，ECAM 区域将使用称为 MCFG 的表（结构）通过 ACPI 从固件进行通信。现在知道使用什么特定协议来检索此信息不是很重要吗，只需了解操作系统从固件中检索这些区域的地址，固件决定了将它们放在哪里。</p>
</blockquote>
<p>因此，为了进行配置空间访问，内核必须将配置空间（ECAM）映射到虚拟内存。这是这样的事情会是什么样子：</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//2024/08/31/7bb04dca3ffe48821f8bf9f6f48bc71b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7bb04dca3ffe48821f8bf9f6f48bc71b.png" alt=""  title="ECAM 到虚拟内存的映射。可怕的是没有规模。" 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>在此之后，内核现在可以使用虚拟映射与设备的配置空间进行通信。但是这个配置空间是什么样的呢？嗯，它只是我们上面讨论的一堆配置空间结构块。设备可能具有的每个可能的 BDF 都在 ECAM 中提供了空间来对其进行配置。它的布局方式是，设备的 BDF 会告诉你其配置空间在 ECAM 中的确切位置。也就是说，给定一个 BDF，我们可以计算要添加到 ECAM 区域基数的偏移量，以便与设备通信，因为每个功能的所有 ECAM 区域的大小都相同。</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//2024/08/31/54b366fd88125e12c7b1cffd70f8f0bd.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/54b366fd88125e12c7b1cffd70f8f0bd.png" alt=""  title="如果设备不存在，系统将读回所有 FF（二进制中的所有 1）。这将表明设备当前在系统上未处于活动状态" 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>从这张图中，我们可以开始看到 PCIe 的枚举实际上是如何发生的。当我们读回有效的配置空间数据时，我们知道该 BDF 上存在设备。如果我们改为读回 FF，我们知道设备不在该插槽或功能中。当然，我们不会为了枚举所有设备而暴力破解每个地址，因为由于 MMIO 的开销，代价比较大。但是，这种蛮力的高级版本是我们如何快速枚举所有已通电并在配置空间上响应我们的设备。</p>
<h2 id="把它们放在一起---软件配置空间访问">把它们放在一起 - 软件配置空间访问</h2>
<p>现在我们了解了如何访问配置空间，我们可以将两端（层次结构和 MMIO）放在一起，以查看从内核模式读取配置空间的指令的完整路径。</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//2024/08/31/ed43867c9ad22be7633a41a8cb397d12.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/ed43867c9ad22be7633a41a8cb397d12.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>在内核模式下运行的某些代码从 ECAM 虚拟映射中读取偏移量。</li>
<li>虚拟映射由 CPU 的页表转换为 ECAM 中的物理地址。</li>
<li>读取物理地址，导致内部 CPU 互连中发生操作，以通知RC访问。</li>
<li>RC将请求的数据包化版本生成为 TLP，该 TLP 显示“读取设备 02:00.0 的偏移量 0x0 处的值”，并通过层次结构发送该请求。</li>
<li>TLP 由总线 2 上的此显示控制器接收，并看到它是一个配置空间 TLP。现在，它知道使用包含偏移量 0x0 处的值内容的配置空间响应 TLP 进行响应。</li>
</ul>
<p>现在让我们看看响应：</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//2024/08/31/7636af73b1e906b7cbf1cc44fe620dfc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/7636af73b1e906b7cbf1cc44fe620dfc.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>响应路径没那么有趣了。设备以含有偏移 0 处的值（我们知道这是供应商 ID）的特殊 TLP 进行响应。这个数据包找到回到请求者（即RC），然后互连通知 CPU 更新 rax 的值为 0x10DE，这是 NVIDIA 显卡的供应商 ID。然后，CPU 开始执行下一条指令。</p>
<p>如你所想那样，通过这种方式进行访问可能比通过全部的 TLP 生成的 RAM 慢很多。这确实是事实，并且这也是存在比这种 MMIO 方法更多的方式去与设备通信的主要原因之一。在接下来的文章中，我将详细介绍另一种方法，即 DMA，以及它对于确保软件能够尽可能快地在 CPU 和设备之间传输内存的至关重要性。</p>
<h2 id="练习通过-windbg-手动访问-ecam">练习：通过 WinDbg 手动访问 ECAM</h2>
<p>我们看了一下 config space access 理论上是如何发生的，但让我们自己用 debugger 做同样的事情。为此，我们希望：</p>
<ul>
<li>找到 ECAM 在系统上的位置。</li>
<li>计算到 ECAM 的偏移量以读取设备的供应商 ID。为此，我选择了 NVIDIA 显卡上的<code>Multimedia Device @ 02:00.1</code></li>
<li>在该地址执行物理内存读取以检索值。</li>
</ul>
<p>第一步是找到 ECAM。鉴于 ECAM 的位置来自 ACPI，特别是 ACPI 中的 MCFG 表，这部分有点棘手。这是 firmware 用来告诉操作系统 ECAM 在系统的物理内存映射中的位置的表。关于 ACPI 以及如何将其与 PCI 结合使用，有很多内容要讨论，但现在，我将快速跳到相关部分以实现我们的目标。</p>
<p>在我们的调试器中，我们可以通过使用<code>!acpicache</code>来转储所有 ACPI 表的缓存副本。要转储 MCFG，请点击链接 MCFG 来转储其内容，或手动键入<code>!acpitable MCFG</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">8: kd&gt; !acpicache
</span></span><span class="line"><span class="cl">Dumping cached ACPI tables...
</span></span><span class="line"><span class="cl">  XSDT @<span class="o">(</span>fffff7b6c0004018<span class="o">)</span> Rev: 0x1 Len: 0x0000bc TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  MCFG @<span class="o">(</span>fffff7b6c0005018<span class="o">)</span> Rev: 0x1 Len: 0x00003c TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  FACP @<span class="o">(</span>fffff7b6c0007018<span class="o">)</span> Rev: 0x4 Len: 0x0000f4 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  APIC @<span class="o">(</span>fffff7b6c0008018<span class="o">)</span> Rev: 0x2 Len: 0x000afc TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  DMAR @<span class="o">(</span>fffff7b6c000a018<span class="o">)</span> Rev: 0x1 Len: 0x0000c0 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  HPET @<span class="o">(</span>fffff7b6c015a018<span class="o">)</span> Rev: 0x1 Len: 0x000038 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  TCPA @<span class="o">(</span>ffffdc89b07209f8<span class="o">)</span> Rev: 0x2 Len: 0x000064 TableID: EDK2    
</span></span><span class="line"><span class="cl">  SSDT @<span class="o">(</span>ffffdc89b0720a88<span class="o">)</span> Rev: 0x2 Len: 0x0003b3 TableID: Tpm2Tabl
</span></span><span class="line"><span class="cl">  TPM2 @<span class="o">(</span>ffffdc89b0720e68<span class="o">)</span> Rev: 0x3 Len: 0x000034 TableID: EDK2    
</span></span><span class="line"><span class="cl">  SSDT @<span class="o">(</span>ffffdc89b07fc018<span class="o">)</span> Rev: 0x1 Len: 0x0013a1 TableID: Plat_Wmi
</span></span><span class="line"><span class="cl">  UEFI @<span class="o">(</span>ffffdc89b07fd3e8<span class="o">)</span> Rev: 0x1 Len: 0x000042 TableID: 
</span></span><span class="line"><span class="cl">  BDAT @<span class="o">(</span>ffffdc89b07fd458<span class="o">)</span> Rev: 0x1 Len: 0x000030 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  MSDM @<span class="o">(</span>ffffdc89b07fd4b8<span class="o">)</span> Rev: 0x3 Len: 0x000055 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  SLIC @<span class="o">(</span>ffffdc89b07fd538<span class="o">)</span> Rev: 0x1 Len: 0x000176 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  WSMT @<span class="o">(</span>ffffdc89b07fd6d8<span class="o">)</span> Rev: 0x1 Len: 0x000028 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  WDDT @<span class="o">(</span>ffffdc89b0721a68<span class="o">)</span> Rev: 0x1 Len: 0x000040 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  SSDT @<span class="o">(</span>ffffdc89b2580018<span class="o">)</span> Rev: 0x2 Len: 0x086372 TableID: SSDT  PM
</span></span><span class="line"><span class="cl">  NITR @<span class="o">(</span>ffffdc89b26063b8<span class="o">)</span> Rev: 0x2 Len: 0x000071 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">  ASF! @<span class="o">(</span>ffffdc89b2606548<span class="o">)</span> Rev: 0x20 Len: 0x000074 TableID:  HCG
</span></span><span class="line"><span class="cl">  BGRT @<span class="o">(</span>ffffdc89b26065e8<span class="o">)</span> Rev: 0x1 Len: 0x000038 TableID: TIANO   
</span></span><span class="line"><span class="cl">  DSDT @<span class="o">(</span>ffffdc89b0e94018<span class="o">)</span> Rev: 0x2 Len: 0x021c89 TableID: SLIC-WKS
</span></span><span class="line"><span class="cl">8: kd&gt; !acpitable MCFG
</span></span><span class="line"><span class="cl">HEADER - fffff7b6c0005018
</span></span><span class="line"><span class="cl">  Signature:               MCFG
</span></span><span class="line"><span class="cl">  Length:                  0x0000003c
</span></span><span class="line"><span class="cl">  Revision:                0x01
</span></span><span class="line"><span class="cl">  Checksum:                0x3c
</span></span><span class="line"><span class="cl">  OEMID:                   HPQOEM
</span></span><span class="line"><span class="cl">  OEMTableID:              SLIC-WKS
</span></span><span class="line"><span class="cl">  OEMRevision:             0x00000001
</span></span><span class="line"><span class="cl">  CreatorID:               INTL
</span></span><span class="line"><span class="cl">  CreatorRev:              0x20091013
</span></span><span class="line"><span class="cl">BODY - fffff7b6c000503c
</span></span><span class="line"><span class="cl">fffff7b6<span class="sb">`</span>c000503c  <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> 00-00 <span class="m">00</span> <span class="m">00</span> d0 <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span>  ................
</span></span><span class="line"><span class="cl">fffff7b6<span class="sb">`</span>c000504c  <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> ff <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span>                          ........
</span></span></code></pre></div><p>要了解如何阅读此表，遗憾的是，我们需要查看 ACPI 规范。与其让你这样做，不如省去你的痛苦，把相关部分拉到这里：</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//2024/08/31/2e26ba8d45b7e876b78500fa7392440d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/2e26ba8d45b7e876b78500fa7392440d.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>由于 <code>！acpitable</code> 命令已经解析并显示此表中 <code>Creator Revision</code> 之前的所有内容，因此 <code>BODY</code> 的前 8 个字节将是偏移量 36 处的 8 个字节的 <code>Reserved</code> 内存。因此，我们跳过这 8 个字节并找到以下结构：</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//2024/08/31/936801aafdfbd7bc6e13e82310ce925f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/936801aafdfbd7bc6e13e82310ce925f.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 个字节是 <code>Reserved</code> 后面的区域的 <code>ECAM</code> 区域的地址。这意味着 <code>ECAM</code> 基址的偏移量为偏移量 8。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">BODY - fffff7b6c000503c
</span></span><span class="line"><span class="cl">fffff7b6<span class="sb">`</span>c000503c  <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> 00-00 <span class="m">00</span> <span class="m">00</span> d0 <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span>  ................
</span></span><span class="line"><span class="cl">fffff7b6<span class="sb">`</span>c000504c  <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> ff <span class="m">00</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span>                          ........
</span></span></code></pre></div><p>对于这个系统，ECAM 位于地址：<code>0xD0000000</code>。（请别忘了以小端序来读取这个地址）</p>
<p>为了验证我们得到了正确的地址，让我们读取<code>00:00.0</code>的供应商 ID，这也是 ECAM 的前两个字节。我们将使用<code>!dw</code>命令来完成这个操作，该命令代表的<code>dump physical word</code>（感叹号代表物理）。这个命令要求你指定一个缓存类型，在我们的情况下，总是使用<code>[uc]</code>或者说未缓存。它还提供了一个长度，这是由 L1 指定要读取的 word 的数量。</p>
<blockquote>
<p>注意：请务必始终将目标设备内存的大小与我们从软件中读取的大小相匹配。这意味着，如果我们要读取的值是 16 位值（如供应商 ID），则必须执行 16 位读取。执行 32 位读取可能会更改设备响应的结果。对于配置空间，我们可以读取供应商 ID 的更大大小，但并非在所有情况下都是如此。最好养成将读取大小与目标大小匹配的习惯，以避免任何意外结果。请记住：设备内存不是 RAM。</p>
</blockquote>
<p>综上所述，我们读取 <code>00：00.0</code> 的 VendorID，如下所示：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">8: kd&gt; !dw <span class="o">[</span>uc<span class="o">]</span> D0000000 L1
</span></span><span class="line"><span class="cl"><span class="c1">#d0000000 8086</span>
</span></span></code></pre></div><p>我们读取的结果值为 <code>0x8086</code>，它恰好是 <code>Intel</code> 的供应商 ID。为了验证这是正确的，让我们使用 <code>！pci</code> 转储相同的内容。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">8: kd&gt; !pci <span class="m">100</span> <span class="m">0</span> <span class="m">0</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">PCI Configuration Space <span class="o">(</span>Segment:0000 Bus:00 Device:00 Function:00<span class="o">)</span>
</span></span><span class="line"><span class="cl">Common Header:
</span></span><span class="line"><span class="cl">    00: VendorID       <span class="m">8086</span> Intel Corporation
</span></span></code></pre></div><h3 id="从特定函数读取-vendorid">从特定函数读取 VendorID</h3>
<p>现在要计算我们希望与之通信的另一个函数（<code>02：00.1</code> 的 NVIDIA 卡）的 ECAM 地址，我们需要通过使用目标函数的 BDF 和一些位数学计算到 ECAM 的偏移量来手动执行“数组访问”。</p>
<p>计算方法存在于 PCIe 规范中，该规范为总线、器件和函数分配了一定数量的 ECAM 位来计算偏移量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="p">|</span> <span class="m">27</span> - <span class="m">20</span> <span class="p">|</span> <span class="m">19</span> - <span class="m">15</span> <span class="p">|</span> <span class="m">14</span> - <span class="m">12</span>     <span class="p">|</span>  <span class="m">11</span> - <span class="m">0</span>       <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> Bus Nr  <span class="p">|</span> Dev Nr  <span class="p">|</span> Function Nr <span class="p">|</span> Register      <span class="p">|</span>
</span></span></code></pre></div><p>通过填写 BDF 并根据每个元素的位位置对结果进行移位和 OR 运算，我们可以计算出要添加到 ECAM 的偏移量。</p>
<p>我将使用 python，但你可以使用任何你想要的计算器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;&gt;&gt; hex<span class="o">(</span>0xD0000000 + <span class="o">((</span><span class="m">2</span> <span class="s">&lt;&lt; 20) | (0 &lt;&lt; 15) | (1 &lt;&lt; 12)))
</span></span></span><span class="line"><span class="cl"><span class="s">&#39;0xd020</span>1000<span class="err">&#39;</span>
</span></span></code></pre></div><p>这意味着 <code>02：00.1</code> 的 ECAM 区域位于 <code>0xD0201000</code>。</p>
<p>现在，要从函数中读取 VendorID 的值：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">8: kd&gt; !dw <span class="o">[</span>uc<span class="o">]</span> D0201000 L1
</span></span><span class="line"><span class="cl"><span class="c1">#d0201000 10de</span>
</span></span></code></pre></div><p>结果是 <code>0x10de</code>，我们从上面知道它是 NVIDIA Corporation！这意味着我们成功地从 ECAM 中读取了此函数的第一个值。</p>
<h2 id="总结">总结</h2>
<p>这篇帖子最终比我预期的要长得多！我不会继续这篇文章，而是将其拆分并随着时间的推移充实该系列。关于 PCIe，我想介绍的主题太多了，但空闲时间却很少，但在下一篇文章中，我将更详细地介绍设备 BAR（一种特定于设备的 MMIO 形式）和 DMA（直接内存访问）。本系列将继续使用与以前相同的租户，更侧重于理解而不是具体细节。</p>
<p>希望你喜欢这个对 PCIe 世界的小小了解！期待更多精彩。</p>
<p><a href="https://lifeislife.cn/posts/%E8%AF%91%E6%96%87-pcie-part2/">单击此处查看第 2 部分！</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>GearAutomator生成Strava运动热图和天气卡片</title>
      <link>https://lifeislife.cn/posts/gearautomator%E7%94%9F%E6%88%90strava%E8%BF%90%E5%8A%A8%E7%83%AD%E5%9B%BE%E5%92%8C%E5%A4%A9%E6%B0%94%E5%8D%A1%E7%89%87/</link>
      <pubDate>Sat, 24 Aug 2024 13:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/gearautomator%E7%94%9F%E6%88%90strava%E8%BF%90%E5%8A%A8%E7%83%AD%E5%9B%BE%E5%92%8C%E5%A4%A9%E6%B0%94%E5%8D%A1%E7%89%87/</guid>
      <description>&lt;h1 id=&#34;同步-strava-数据&#34;&gt;同步 Strava 数据&lt;/h1&gt;
&lt;p&gt;GearAutomator 是一个可以生成 Strava 运动热图和天气卡片的工具，访问 &lt;a href=&#34;https://www.gearaut.com&#34;&gt;GearAutomator&lt;/a&gt;，点击的&lt;code&gt;Connect with Strava&lt;/code&gt;按钮，然后输入 Strava 的用户名和密码登录。&lt;/p&gt;
&lt;p&gt;根据运动数据量不同，生成时间会有所不同，请勿关闭页面，等待生成完成。Strava的API限制很严格，同步数据很慢，连接上Strava之后，建议干其他事情，等待数据同步完成，不用频繁刷新页面。我第一次同步数据不知道花了多久一直没有显示热图，以为这个工具失效了，后来某一天再次访问，发现热图已经生成了。&lt;/p&gt;
&lt;h1 id=&#34;查看热图&#34;&gt;查看热图&lt;/h1&gt;
&lt;p&gt;Zwift虚拟骑行也会使用真实的GPX数据，所以在热力地图中也能看到有轨迹，虚拟骑行会用紫色轨迹表示，真实骑行会用绿色轨迹表示。图中紫色轨迹就是巴黎香街冲刺。绿色轨迹是一次路骑。&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//2024/08/24/fed51344e1e22419ca0ef77be2175cd5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/fed51344e1e22419ca0ef77be2175cd5.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//2024/08/24/a4ec56ea0604b336d80ff0fe8369dd2c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/a4ec56ea0604b336d80ff0fe8369dd2c.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;h1 id=&#34;配置天气卡片&#34;&gt;配置天气卡片&lt;/h1&gt;
&lt;p&gt;Strava 订阅用户可以生成每次运动的天气卡片，可以在运动简介中看到这次运动时的天气情况，如图所示：&lt;/p&gt;
&lt;p&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//2024/08/24/b9732209d84fd4164a684bb7a66eaab6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/b9732209d84fd4164a684bb7a66eaab6.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;当下次有新的数据同步到Strava时，会自动在标题下面生成天气信息，如图所示：&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//2024/08/24/1d2987ccad88c1736c7251b0bb35d299.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/1d2987ccad88c1736c7251b0bb35d299.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;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//2024/08/24/42dc1ed8021b3f224c2e7c61513ca19b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/42dc1ed8021b3f224c2e7c61513ca19b.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;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;温度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${temperature}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;体感温度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${apparentTemperature}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;天气状况&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${skyCon}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;湿度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${humidity}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;气压&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${pressure}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;云量&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${cloudRate}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;风速&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${windSpeed}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;能见度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${visibility}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;空气质量&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;空气质量指数(AQI)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${aqi}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;细颗粒物(PM2.5)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${pm25}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;可吸入颗粒物(PM10)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${pm10}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;臭氧(O3)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${o3}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;二氧化硫(SO2)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${so2}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;二氧化氮(NO2)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${no2}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;一氧化碳(CO)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${co}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;紫外线强度(UV)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${uv}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;地面接收的太阳辐射(DSWRF)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${dswrf}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;温度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startTemperature} - ${endTemperature}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;体感温度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startApparentTemperature} - ${endApparentTemperature}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;天气状况&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startSkyCon} - ${endSkyCon}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;湿度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startHumidity} - ${endHumidity}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;气压&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startPressure} - ${endPressure}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;云量&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startCloudRate} - ${endCloudRate}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;风速&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startWindSpeed} - ${endWindSpeed}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;能见度&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startVisibility} - ${endVisibility}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;空气质量&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;空气质量指数(AQI)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startAqi} - ${endAqi}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;细颗粒物(PM2.5)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startPM25} - ${endPM25}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;可吸入颗粒物(PM10)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startPM10} - ${endPM10}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;臭氧(O3)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startO3} - ${endO3}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;二氧化硫(SO2)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startSO2} - ${endSO2}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;二氧化氮(NO2)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startNO2} - ${endNO2}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;一氧化碳(CO)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startCO} - ${endCO}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;紫外线强度(UV)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startUV} - ${endUV}  &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;地面接收的太阳辐射(DSWRF)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${startDswrf} - ${endDswrf}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="同步-strava-数据">同步 Strava 数据</h1>
<p>GearAutomator 是一个可以生成 Strava 运动热图和天气卡片的工具，访问 <a href="https://www.gearaut.com">GearAutomator</a>，点击的<code>Connect with Strava</code>按钮，然后输入 Strava 的用户名和密码登录。</p>
<p>根据运动数据量不同，生成时间会有所不同，请勿关闭页面，等待生成完成。Strava的API限制很严格，同步数据很慢，连接上Strava之后，建议干其他事情，等待数据同步完成，不用频繁刷新页面。我第一次同步数据不知道花了多久一直没有显示热图，以为这个工具失效了，后来某一天再次访问，发现热图已经生成了。</p>
<h1 id="查看热图">查看热图</h1>
<p>Zwift虚拟骑行也会使用真实的GPX数据，所以在热力地图中也能看到有轨迹，虚拟骑行会用紫色轨迹表示，真实骑行会用绿色轨迹表示。图中紫色轨迹就是巴黎香街冲刺。绿色轨迹是一次路骑。</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//2024/08/24/fed51344e1e22419ca0ef77be2175cd5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/fed51344e1e22419ca0ef77be2175cd5.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//2024/08/24/a4ec56ea0604b336d80ff0fe8369dd2c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/a4ec56ea0604b336d80ff0fe8369dd2c.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>
<h1 id="配置天气卡片">配置天气卡片</h1>
<p>Strava 订阅用户可以生成每次运动的天气卡片，可以在运动简介中看到这次运动时的天气情况，如图所示：</p>
<p>没有订阅的用户也可以通过这个工具来生成，在天气配置页面，开启即可：</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//2024/08/24/b9732209d84fd4164a684bb7a66eaab6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/b9732209d84fd4164a684bb7a66eaab6.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>当下次有新的数据同步到Strava时，会自动在标题下面生成天气信息，如图所示：</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//2024/08/24/1d2987ccad88c1736c7251b0bb35d299.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/1d2987ccad88c1736c7251b0bb35d299.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>
<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//2024/08/24/42dc1ed8021b3f224c2e7c61513ca19b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/24/42dc1ed8021b3f224c2e7c61513ca19b.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>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">温度</span><span class="p">:</span><span class="w"> </span><span class="l">${temperature}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">体感温度</span><span class="p">:</span><span class="w"> </span><span class="l">${apparentTemperature}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">天气状况</span><span class="p">:</span><span class="w"> </span><span class="l">${skyCon}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">湿度</span><span class="p">:</span><span class="w"> </span><span class="l">${humidity}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">气压</span><span class="p">:</span><span class="w"> </span><span class="l">${pressure}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">云量</span><span class="p">:</span><span class="w"> </span><span class="l">${cloudRate}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">风速</span><span class="p">:</span><span class="w"> </span><span class="l">${windSpeed}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">能见度</span><span class="p">:</span><span class="w"> </span><span class="l">${visibility}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">空气质量</span><span class="p">:</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">空气质量指数(AQI)</span><span class="p">:</span><span class="w"> </span><span class="l">${aqi}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">细颗粒物(PM2.5)</span><span class="p">:</span><span class="w"> </span><span class="l">${pm25}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">可吸入颗粒物(PM10)</span><span class="p">:</span><span class="w"> </span><span class="l">${pm10}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">臭氧(O3)</span><span class="p">:</span><span class="w"> </span><span class="l">${o3}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">二氧化硫(SO2)</span><span class="p">:</span><span class="w"> </span><span class="l">${so2}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">二氧化氮(NO2)</span><span class="p">:</span><span class="w"> </span><span class="l">${no2}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">一氧化碳(CO)</span><span class="p">:</span><span class="w"> </span><span class="l">${co}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">紫外线强度(UV)</span><span class="p">:</span><span class="w"> </span><span class="l">${uv}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">地面接收的太阳辐射(DSWRF)</span><span class="p">:</span><span class="w"> </span><span class="l">${dswrf}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">温度</span><span class="p">:</span><span class="w"> </span><span class="l">${startTemperature} - ${endTemperature}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">体感温度</span><span class="p">:</span><span class="w"> </span><span class="l">${startApparentTemperature} - ${endApparentTemperature}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">天气状况</span><span class="p">:</span><span class="w"> </span><span class="l">${startSkyCon} - ${endSkyCon}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">湿度</span><span class="p">:</span><span class="w"> </span><span class="l">${startHumidity} - ${endHumidity}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">气压</span><span class="p">:</span><span class="w"> </span><span class="l">${startPressure} - ${endPressure}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">云量</span><span class="p">:</span><span class="w"> </span><span class="l">${startCloudRate} - ${endCloudRate}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">风速</span><span class="p">:</span><span class="w"> </span><span class="l">${startWindSpeed} - ${endWindSpeed}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">能见度</span><span class="p">:</span><span class="w"> </span><span class="l">${startVisibility} - ${endVisibility}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">空气质量</span><span class="p">:</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">空气质量指数(AQI)</span><span class="p">:</span><span class="w"> </span><span class="l">${startAqi} - ${endAqi}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">细颗粒物(PM2.5)</span><span class="p">:</span><span class="w"> </span><span class="l">${startPM25} - ${endPM25}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">可吸入颗粒物(PM10)</span><span class="p">:</span><span class="w"> </span><span class="l">${startPM10} - ${endPM10}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">臭氧(O3)</span><span class="p">:</span><span class="w"> </span><span class="l">${startO3} - ${endO3}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">二氧化硫(SO2)</span><span class="p">:</span><span class="w"> </span><span class="l">${startSO2} - ${endSO2}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">二氧化氮(NO2)</span><span class="p">:</span><span class="w"> </span><span class="l">${startNO2} - ${endNO2}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">一氧化碳(CO)</span><span class="p">:</span><span class="w"> </span><span class="l">${startCO} - ${endCO}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">紫外线强度(UV)</span><span class="p">:</span><span class="w"> </span><span class="l">${startUV} - ${endUV}  </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">地面接收的太阳辐射(DSWRF)</span><span class="p">:</span><span class="w"> </span><span class="l">${startDswrf} - ${endDswrf}</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>SCP-firmware 源码分析-module 间 api 调用分析</title>
      <link>https://lifeislife.cn/posts/scp-firmware%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-module%E9%97%B4api%E8%B0%83%E7%94%A8%E5%88%86%E6%9E%90/</link>
      <pubDate>Thu, 15 Aug 2024 15:42:13 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/scp-firmware%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-module%E9%97%B4api%E8%B0%83%E7%94%A8%E5%88%86%E6%9E%90/</guid>
      <description>&lt;p&gt;想了解如何提供 API，我们先看看 CMN 模块是如何使用 API 的，在文件&lt;code&gt;module/cmn700/src/mod_cmn700.c&lt;/code&gt;我们可以看到如下代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// module/cmn700/src/mod_cmn700.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod_system_info_get_info_api&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_info_api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cmn700_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;system_info_api&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;get_system_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;chip_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;system_info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chip_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;multi_chip_mode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;system_info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;multi_chip_mode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;他调用了&lt;code&gt;system_info_api-&amp;gt;get_system_info&lt;/code&gt;这个函数，实际这就是 mod_system_info 暴露给 mod_cmn700 的一个 API。用于获取系统信息。&lt;/p&gt;
&lt;p&gt;为何这个静态变量&lt;code&gt;system_info_api&lt;/code&gt;就能调用到&lt;code&gt;mod_system_info&lt;/code&gt;的函数呢？我们继续搜索代码，可以看到如下代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// module/cmn700/src/mod_cmn700.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cmn700_bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;round&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;fwk_id_is_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_ID_TYPE_MODULE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;cm&#34;&gt;/* Bind to system info module to obtain multi-chip info */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fwk_module_bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;FWK_ID_MODULE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_SYSTEM_INFO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;FWK_ID_API&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_SYSTEM_INFO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MOD_SYSTEM_INFO_GET_API_IDX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_info_api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在&lt;code&gt;cmn700_bind&lt;/code&gt;函数中，调用了&lt;code&gt;fwk_module_bind&lt;/code&gt;函数，这个函数的作用是绑定一个模块的 API，这样就可以通过这个 API 调用模块的函数。&lt;code&gt;fwk_module_bind&lt;/code&gt;函数的第一个参数是提供 API 的模块的 ID，比如当前是模块&lt;code&gt;system_info&lt;/code&gt;的 ID，如果是要用 USB 的 API，那么就是 USB 模块的 ID。第二个参数是 API 的 ID，这个 ID 是在模块的头文件中定义的，比如&lt;code&gt;mod_system_info.h&lt;/code&gt;中定义了&lt;code&gt;MOD_SYSTEM_INFO_GET_API_IDX&lt;/code&gt;，这个宏定义的值就是 API 的 ID。第三个参数是 API 的指针，这个指针就是开头提到的静态变量的地址，它是定义在使用 API 的模块中的。这样就可以通过 API 的指针调用模块的函数。对于 USB 模块，我们就需要在使用到 USB 的模块中定义一个静态变量。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fwk_module_bind&lt;/code&gt;做了什么呢？进入该函数，可以看到它回调了&lt;code&gt;mod_system_info&lt;/code&gt;的&lt;code&gt;process_bind_request&lt;/code&gt;函数，也就是&lt;code&gt;system_info_process_bind_request&lt;/code&gt;函数。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// framework/src/fwk_module.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fwk_module_bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;api_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_mod_ctx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;desc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;process_bind_request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;fwk_module_ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bind_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;api_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;system_info_process_bind_request&lt;/code&gt;函数的作用是根据 API 的 ID 返回 API 的指针。它把&lt;code&gt;get_system_info_api&lt;/code&gt;的地址赋值给了&lt;code&gt;api&lt;/code&gt;。这就实现了 API 的暴露。经过这个函数，&lt;code&gt;system_info_api&lt;/code&gt;就指向了&lt;code&gt;get_system_info_api&lt;/code&gt;。实际就是我们在&lt;code&gt;cmn700_bind&lt;/code&gt;函数中使用&lt;code&gt;system_info_api&lt;/code&gt;就是在调用&lt;code&gt;get_system_info_api&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// module/system_info/src/mod_system_info.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;system_info_process_bind_request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requester_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;targer_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;api_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;fwk_id_get_api_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;MOD_SYSTEM_INFO_GET_API_IDX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_system_info_api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_E_PARAM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;解释完这个 bind 函数，它是在哪被调用的？它在框架初始化时就会被调用，分析&lt;code&gt;fwk_arch_init&lt;/code&gt;函数，可以看到在&lt;code&gt;fwk_module_start&lt;/code&gt;函数中会对每一个模块调用&lt;code&gt;bind&lt;/code&gt;函数。函数调用流程参考图片文件。在此就不再赘述了。&lt;/p&gt;
&lt;p&gt;经过以上分析，提供 API 的模块需要做的事情就是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在模块的头文件中定义 API 的 ID，它有一定的格式，具体可以参考 MSCP 文档&lt;code&gt;doc/framework.md&lt;/code&gt;的&lt;code&gt;APIs&lt;/code&gt;章节。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod_modulename_api&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MOD_MODULENAME_API_A&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MOD_MODULENAME_API_B&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在模块的源文件中定义 API 结构体，用于在其他模块中使用。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod_system_info_get_info_api&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_system_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod_system_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sys_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在模块原文件中提供&lt;code&gt;process_bind_request&lt;/code&gt;函数，用于根据 API 的 ID 返回 API 的指针。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;system_info_process_bind_request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requester_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;targer_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;api_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;fwk_id_get_api_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;MOD_SYSTEM_INFO_GET_API_IDX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_system_info_api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_E_PARAM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在模块的源文件中定义 API 的指针，用于提供 API 的地址。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod_system_info_get_info_api&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_system_info_api&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_system_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;system_info_get_system_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实现需要提供的 API 函数。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;system_info_get_system_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod_system_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sys_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sys_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&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//2024/08/10/5b3d6d12e4e9e9b2b7aa5cc5fc6c8738.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/5b3d6d12e4e9e9b2b7aa5cc5fc6c8738.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;
</description>
      <content:encoded><![CDATA[<p>想了解如何提供 API，我们先看看 CMN 模块是如何使用 API 的，在文件<code>module/cmn700/src/mod_cmn700.c</code>我们可以看到如下代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// module/cmn700/src/mod_cmn700.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">struct</span> <span class="n">mod_system_info_get_info_api</span> <span class="o">*</span><span class="n">system_info_api</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">cmn700_start</span><span class="p">(</span><span class="kt">fwk_id_t</span> <span class="n">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="n">status</span> <span class="o">=</span> <span class="n">system_info_api</span><span class="o">-&gt;</span><span class="nf">get_system_info</span><span class="p">(</span><span class="o">&amp;</span><span class="n">system_info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">status</span> <span class="o">==</span> <span class="n">FWK_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">chip_id</span> <span class="o">=</span> <span class="n">system_info</span><span class="o">-&gt;</span><span class="n">chip_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">multi_chip_mode</span> <span class="o">=</span> <span class="n">system_info</span><span class="o">-&gt;</span><span class="n">multi_chip_mode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>他调用了<code>system_info_api-&gt;get_system_info</code>这个函数，实际这就是 mod_system_info 暴露给 mod_cmn700 的一个 API。用于获取系统信息。</p>
<p>为何这个静态变量<code>system_info_api</code>就能调用到<code>mod_system_info</code>的函数呢？我们继续搜索代码，可以看到如下代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// module/cmn700/src/mod_cmn700.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">cmn700_bind</span><span class="p">(</span><span class="kt">fwk_id_t</span> <span class="n">id</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">round</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">fwk_id_is_type</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">FWK_ID_TYPE_MODULE</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Bind to system info module to obtain multi-chip info */</span>
</span></span><span class="line"><span class="cl">        <span class="n">status</span> <span class="o">=</span> <span class="nf">fwk_module_bind</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="nf">FWK_ID_MODULE</span><span class="p">(</span><span class="n">FWK_MODULE_IDX_SYSTEM_INFO</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nf">FWK_ID_API</span><span class="p">(</span><span class="n">FWK_MODULE_IDX_SYSTEM_INFO</span><span class="p">,</span> <span class="n">MOD_SYSTEM_INFO_GET_API_IDX</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="o">&amp;</span><span class="n">system_info_api</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在<code>cmn700_bind</code>函数中，调用了<code>fwk_module_bind</code>函数，这个函数的作用是绑定一个模块的 API，这样就可以通过这个 API 调用模块的函数。<code>fwk_module_bind</code>函数的第一个参数是提供 API 的模块的 ID，比如当前是模块<code>system_info</code>的 ID，如果是要用 USB 的 API，那么就是 USB 模块的 ID。第二个参数是 API 的 ID，这个 ID 是在模块的头文件中定义的，比如<code>mod_system_info.h</code>中定义了<code>MOD_SYSTEM_INFO_GET_API_IDX</code>，这个宏定义的值就是 API 的 ID。第三个参数是 API 的指针，这个指针就是开头提到的静态变量的地址，它是定义在使用 API 的模块中的。这样就可以通过 API 的指针调用模块的函数。对于 USB 模块，我们就需要在使用到 USB 的模块中定义一个静态变量。</p>
<p><code>fwk_module_bind</code>做了什么呢？进入该函数，可以看到它回调了<code>mod_system_info</code>的<code>process_bind_request</code>函数，也就是<code>system_info_process_bind_request</code>函数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// framework/src/fwk_module.c
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fwk_module_bind</span><span class="p">(</span><span class="kt">fwk_id_t</span> <span class="n">target_id</span><span class="p">,</span> <span class="kt">fwk_id_t</span> <span class="n">api_id</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">api</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="n">status</span> <span class="o">=</span> <span class="n">fwk_mod_ctx</span><span class="o">-&gt;</span><span class="n">desc</span><span class="o">-&gt;</span><span class="nf">process_bind_request</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">fwk_module_ctx</span><span class="p">.</span><span class="n">bind_id</span><span class="p">,</span> <span class="n">target_id</span><span class="p">,</span> <span class="n">api_id</span><span class="p">,</span> <span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">**</span><span class="p">)</span><span class="n">api</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>system_info_process_bind_request</code>函数的作用是根据 API 的 ID 返回 API 的指针。它把<code>get_system_info_api</code>的地址赋值给了<code>api</code>。这就实现了 API 的暴露。经过这个函数，<code>system_info_api</code>就指向了<code>get_system_info_api</code>。实际就是我们在<code>cmn700_bind</code>函数中使用<code>system_info_api</code>就是在调用<code>get_system_info_api</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// module/system_info/src/mod_system_info.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">system_info_process_bind_request</span><span class="p">(</span><span class="kt">fwk_id_t</span> <span class="n">requester_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="kt">fwk_id_t</span> <span class="n">targer_id</span><span class="p">,</span> <span class="kt">fwk_id_t</span> <span class="n">api_id</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">**</span><span class="n">api</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="nf">fwk_id_get_api_idx</span><span class="p">(</span><span class="n">api_id</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">MOD_SYSTEM_INFO_GET_API_IDX</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">api</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">get_system_info_api</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">FWK_E_PARAM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">FWK_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>解释完这个 bind 函数，它是在哪被调用的？它在框架初始化时就会被调用，分析<code>fwk_arch_init</code>函数，可以看到在<code>fwk_module_start</code>函数中会对每一个模块调用<code>bind</code>函数。函数调用流程参考图片文件。在此就不再赘述了。</p>
<p>经过以上分析，提供 API 的模块需要做的事情就是：</p>
<ol>
<li>
<p>在模块的头文件中定义 API 的 ID，它有一定的格式，具体可以参考 MSCP 文档<code>doc/framework.md</code>的<code>APIs</code>章节。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">enum</span> <span class="n">mod_modulename_api</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MOD_MODULENAME_API_A</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">MOD_MODULENAME_API_B</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div></li>
<li>
<p>在模块的源文件中定义 API 结构体，用于在其他模块中使用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">mod_system_info_get_info_api</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">get_system_info</span><span class="p">)(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">mod_system_info</span> <span class="o">**</span><span class="n">sys_info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div></li>
<li>
<p>在模块原文件中提供<code>process_bind_request</code>函数，用于根据 API 的 ID 返回 API 的指针。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">system_info_process_bind_request</span><span class="p">(</span><span class="kt">fwk_id_t</span> <span class="n">requester_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="kt">fwk_id_t</span> <span class="n">targer_id</span><span class="p">,</span> <span class="kt">fwk_id_t</span> <span class="n">api_id</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">**</span><span class="n">api</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="nf">fwk_id_get_api_idx</span><span class="p">(</span><span class="n">api_id</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">MOD_SYSTEM_INFO_GET_API_IDX</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">api</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">get_system_info_api</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">FWK_E_PARAM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">FWK_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p>在模块的源文件中定义 API 的指针，用于提供 API 的地址。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">struct</span> <span class="n">mod_system_info_get_info_api</span> <span class="n">get_system_info_api</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">get_system_info</span> <span class="o">=</span> <span class="n">system_info_get_system_info</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div></li>
<li>
<p>实现需要提供的 API 函数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">system_info_get_system_info</span><span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">mod_system_info</span> <span class="o">**</span><span class="n">sys_info</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">sys_info</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">system_info</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">FWK_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
<p>以上就是大致的移植流程了。</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//2024/08/10/5b3d6d12e4e9e9b2b7aa5cc5fc6c8738.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/5b3d6d12e4e9e9b2b7aa5cc5fc6c8738.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>
]]></content:encoded>
    </item>
    <item>
      <title>Elevate for Strava浏览器插件更全面的骑行数据分析</title>
      <link>https://lifeislife.cn/posts/elevate-for-strava%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8F%92%E4%BB%B6%E6%9B%B4%E5%85%A8%E9%9D%A2%E7%9A%84%E9%AA%91%E8%A1%8C%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/</link>
      <pubDate>Sun, 11 Aug 2024 20:00:13 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/elevate-for-strava%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8F%92%E4%BB%B6%E6%9B%B4%E5%85%A8%E9%9D%A2%E7%9A%84%E9%AA%91%E8%A1%8C%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/</guid>
      <description>&lt;p&gt;推荐一个开源工具，&lt;a href=&#34;https://github.com/thomaschampagne/elevate&#34;&gt;Elevate for Strava&lt;/a&gt;，可以为 Strava 提供丰富的数据分析功能，帮助骑行爱好者和运动员更好地了解自己的运动表现。目前有Web插件、Windows APP和MacOS APP。开发者表示未来主要是提供应用软件，插件开发可能投入会减少，如果想要更快更全面体验软件功能，推荐使用Windows APP或MacOS APP。因为Web插件使用更加方便，所以我就以Web插件为例进行介绍。&lt;/p&gt;
&lt;h2 id=&#34;安装工具&#34;&gt;安装工具&lt;/h2&gt;
&lt;p&gt;以下是简单的安装步骤，App版本可以前往&lt;a href=&#34;https://github.com/thomaschampagne/elevate&#34;&gt;Elevate for Strava&lt;/a&gt;的github主页Release页面下载。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安装扩展程序&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户可以通过 Chrome 或 Firefox 浏览器的扩展商店搜索“Elevate for Strava”，然后点击“安装”按钮。如果你是Chrome用户，可以点击&lt;a href=&#34;https://chromewebstore.google.com/detail/elevate-for-strava/dhiaggccakkgdfcadnklkbljcgicpckn&#34;&gt;此链接&lt;/a&gt;直达。其他浏览器可以&lt;a href=&#34;https://thomaschampagne.github.io/elevate/#/landing&#34;&gt;访问官网查看&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;同步 Strava 数据&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安装完成后，用户需要登录他们的 Strava 账户，并授权 Elevate 访问其运动数据。之后，Elevate 将自动同步 Strava 数据，并开始生成各种分析报告。


&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//2024/08/11/a7e7fcb06615b3c2f248176404c6a657.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/a7e7fcb06615b3c2f248176404c6a657.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;配置和使用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户可以根据自己的需求调整 Elevate 的设置，包括单位、分析参数等选项。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;使用介绍&#34;&gt;使用介绍&lt;/h2&gt;
&lt;p&gt;第一次导入成功后先别着急查看数据，先配置一下个人信息，参考下图。切记基本信息和Strava保持一致，不然重新导入真的很费时间，因为Strava接口请求有限制，你可以在Strava申请导出所有数据为文件，这样会快很多，后面再详细介绍。&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//2024/08/11/277c631d2d6ccfd6f773c597acfd2f22.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/277c631d2d6ccfd6f773c597acfd2f22.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;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//2024/08/11/c02f376a56f92c647d3568ce55a790ca.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/c02f376a56f92c647d3568ce55a790ca.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;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//2024/08/11/6d40e1a8a47cc775db4017d2dbaf6ad6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/6d40e1a8a47cc775db4017d2dbaf6ad6.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//2024/08/11/1619fb87264a40ff328bbcee8a1a5ae5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/1619fb87264a40ff328bbcee8a1a5ae5.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;你是不是也想知道自己关注的大佬功率是多少？嘿嘿，Elevate会根据大佬公开的数据，预估一个功率和工体比。以下就是范二特的一次骑行数据。&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//2024/08/12/6fffdb98932e9b8b1a39c9aeb08da701.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/12/6fffdb98932e9b8b1a39c9aeb08da701.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;More Data！点击橙色的按钮，可以显示更多的数据信息，当然这些信息对于普通骑行者作用不大。对于业余车手可以通过这些数据来学习一下职业车手的骑行习惯。比如功率分配，踏频分配等等。&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//2024/08/12/f0259d2d859d146985934a3c86815920.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/12/f0259d2d859d146985934a3c86815920.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;
</description>
      <content:encoded><![CDATA[<p>推荐一个开源工具，<a href="https://github.com/thomaschampagne/elevate">Elevate for Strava</a>，可以为 Strava 提供丰富的数据分析功能，帮助骑行爱好者和运动员更好地了解自己的运动表现。目前有Web插件、Windows APP和MacOS APP。开发者表示未来主要是提供应用软件，插件开发可能投入会减少，如果想要更快更全面体验软件功能，推荐使用Windows APP或MacOS APP。因为Web插件使用更加方便，所以我就以Web插件为例进行介绍。</p>
<h2 id="安装工具">安装工具</h2>
<p>以下是简单的安装步骤，App版本可以前往<a href="https://github.com/thomaschampagne/elevate">Elevate for Strava</a>的github主页Release页面下载。</p>
<ol>
<li>
<p><strong>安装扩展程序</strong></p>
<ul>
<li>用户可以通过 Chrome 或 Firefox 浏览器的扩展商店搜索“Elevate for Strava”，然后点击“安装”按钮。如果你是Chrome用户，可以点击<a href="https://chromewebstore.google.com/detail/elevate-for-strava/dhiaggccakkgdfcadnklkbljcgicpckn">此链接</a>直达。其他浏览器可以<a href="https://thomaschampagne.github.io/elevate/#/landing">访问官网查看</a>。</li>
</ul>
</li>
<li>
<p><strong>同步 Strava 数据</strong></p>
<ul>
<li>安装完成后，用户需要登录他们的 Strava 账户，并授权 Elevate 访问其运动数据。之后，Elevate 将自动同步 Strava 数据，并开始生成各种分析报告。


<!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//2024/08/11/a7e7fcb06615b3c2f248176404c6a657.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/a7e7fcb06615b3c2f248176404c6a657.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></li>
</ul>
</li>
<li>
<p><strong>配置和使用</strong></p>
<ul>
<li>用户可以根据自己的需求调整 Elevate 的设置，包括单位、分析参数等选项。</li>
</ul>
</li>
</ol>
<h2 id="使用介绍">使用介绍</h2>
<p>第一次导入成功后先别着急查看数据，先配置一下个人信息，参考下图。切记基本信息和Strava保持一致，不然重新导入真的很费时间，因为Strava接口请求有限制，你可以在Strava申请导出所有数据为文件，这样会快很多，后面再详细介绍。</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//2024/08/11/277c631d2d6ccfd6f773c597acfd2f22.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/277c631d2d6ccfd6f773c597acfd2f22.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>
<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//2024/08/11/c02f376a56f92c647d3568ce55a790ca.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/c02f376a56f92c647d3568ce55a790ca.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>
<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//2024/08/11/6d40e1a8a47cc775db4017d2dbaf6ad6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/6d40e1a8a47cc775db4017d2dbaf6ad6.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//2024/08/11/1619fb87264a40ff328bbcee8a1a5ae5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/11/1619fb87264a40ff328bbcee8a1a5ae5.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>你是不是也想知道自己关注的大佬功率是多少？嘿嘿，Elevate会根据大佬公开的数据，预估一个功率和工体比。以下就是范二特的一次骑行数据。</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//2024/08/12/6fffdb98932e9b8b1a39c9aeb08da701.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/12/6fffdb98932e9b8b1a39c9aeb08da701.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>More Data！点击橙色的按钮，可以显示更多的数据信息，当然这些信息对于普通骑行者作用不大。对于业余车手可以通过这些数据来学习一下职业车手的骑行习惯。比如功率分配，踏频分配等等。</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//2024/08/12/f0259d2d859d146985934a3c86815920.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/12/f0259d2d859d146985934a3c86815920.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>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU Decodetree详解</title>
      <link>https://lifeislife.cn/posts/qemu-decodetree%E8%AF%A6%E8%A7%A3/</link>
      <pubDate>Sat, 10 Aug 2024 21:08:03 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu-decodetree%E8%AF%A6%E8%A7%A3/</guid>
      <description>&lt;p&gt;QEMU 在 decode 指令的时候，需要调用各平台所定义的 instruction decoders 来解析指令。如在 ARM 平台下，就定义了：&lt;code&gt;disas_arm_insn()&lt;/code&gt;、&lt;code&gt;disas_thumb_insn()&lt;/code&gt; 及 &lt;code&gt;disas_thumb2_insn()&lt;/code&gt; 等来分别负责 ARM 32-bits 指令、ARM Thumb 指令及 ARM Thumb2 指令的解析。&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;Decodetree&lt;/code&gt; 则是由 &lt;code&gt;Bastian Koppelmann&lt;/code&gt; 于 2017 年在 移植 RISC-V QEMU 的时候所提出来的机制 (详见：&lt;a href=&#34;https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07735.html&#34;&gt;讨论邮件1&lt;/a&gt;、&lt;a href=&#34;https://lists.gnu.org/archive/html/qemu-devel/2017-10/msg05046.html&#34;&gt;讨论邮件2&lt;/a&gt;)。提出该机制主要是因为过往的 instruction decoders (如：ARM) 都是采用一堆 &lt;code&gt;switch-case&lt;/code&gt; 来做判断。不仅难阅读，也难以维护。&lt;/p&gt;
&lt;p&gt;因此 &lt;code&gt;Bastian Koppelmann&lt;/code&gt; 就提出了 &lt;code&gt;Decodetree&lt;/code&gt; 的机制，开发者只需要通过 &lt;code&gt;Decodetree&lt;/code&gt; 的语法定义各个指令的格式，便可交由 &lt;code&gt;Decodetree&lt;/code&gt; 来动态生成对应包含 &lt;code&gt;switch-case&lt;/code&gt; 的 instruction decoder &lt;code&gt;.c&lt;/code&gt; 文档。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 特别适合像 RISC-V 这种具有&lt;strong&gt;固定指令格式&lt;/strong&gt;的 ISA。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;因为各字段都在固定的位置，(如 RISC-V 的 &lt;code&gt;opcode&lt;/code&gt; 都是固定在 &lt;code&gt;bits[6..0]&lt;/code&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/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.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;code&gt;Decodetree&lt;/code&gt; 其实是由 Python script (&lt;code&gt;./scripts/decodetree.py&lt;/code&gt;) 所生成的。使用文档可以参考：&lt;code&gt;./docs/devel/decodetree.rst&lt;/code&gt;，里面有详细定义了其语法的格式。QEMU 在编译时，会调用 &lt;code&gt;Decodetree&lt;/code&gt;，根据各平台所定义的 decode 文档，动态生成对应的 decoder。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如 RISC-V 的 instruction decoders 就是被定义在：&lt;code&gt;./target/riscv/*.decode&lt;/code&gt; 中。其 &lt;code&gt;Makefile.obj&lt;/code&gt; 就有如下的声明：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;DECODETREE = $(SRC_PATH)/scripts/decodetree.py
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;decode32-y = $(SRC_PATH)/target/riscv/insn32.decode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;decode32-$(TARGET_RISCV64) += $(SRC_PATH)/target/riscv/insn32-64.decode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;target/riscv/decode_insn32.inc.c: $(decode32-y) $(DECODETREE)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	$(call quiet-command, \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	  $(PYTHON) $(DECODETREE) -o $@ --static-decode decode_insn32 \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          $(decode32-y), &amp;#34;GEN&amp;#34;, $(TARGET_DIR)$@)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 的语法共分为：Fields、Argument Sets、Formats、Patterns 五部分。本文将介绍如何通过 &lt;code&gt;Decodetree&lt;/code&gt; 的语法，来动态生成一个指令的 decoder。&lt;/p&gt;
&lt;h2 id=&#34;field&#34;&gt;Field&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Field&lt;/code&gt; 定义如何取出一指令中，各&lt;strong&gt;字段&lt;/strong&gt; (eg: &lt;code&gt;rd&lt;/code&gt;, &lt;code&gt;rs1&lt;/code&gt;, &lt;code&gt;rs2&lt;/code&gt;, &lt;code&gt;imm&lt;/code&gt;) 的值。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;field_def     := &amp;#39;%&amp;#39; identifier ( unnamed_field )* ( !function=identifier )?
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;unnamed_field := number &amp;#39;:&amp;#39; ( &amp;#39;s&amp;#39; ) number
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由 &lt;code&gt;%&lt;/code&gt; 开头，随后紧接着一个 &lt;code&gt;identifier&lt;/code&gt; 及零个或多个 &lt;code&gt;unamed_field&lt;/code&gt;，并可再加上可选的 &lt;code&gt;!function&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自定，如：&lt;code&gt;rd&lt;/code&gt;、&lt;code&gt;imm&lt;/code&gt;… 等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unamed_field&lt;/code&gt; 定义了该字段的所在比特。第一个数字定义了该字段的 &lt;code&gt;least-significant bit position&lt;/code&gt;，第二个数字则定义了该字段的&lt;code&gt;比特长度&lt;/code&gt;。另外可加上可选的 &lt;code&gt;s&lt;/code&gt; 字符来标明在取出该字段后，是否需要做 符号扩展。
&lt;ul&gt;
&lt;li&gt;Eg：&lt;code&gt;%rd 7:5&lt;/code&gt; 代表 &lt;code&gt;rd&lt;/code&gt; 占了指令中 bits 7 ~ bits 11 的位置 (insn[11:7])，共 5 bits。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!function&lt;/code&gt; 定义在截取出该字段的值后，所会再调用的 function。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Field&lt;/code&gt; (32-bits 指令) 最后会生成对应的 &lt;code&gt;extract32()&lt;/code&gt; 及 &lt;code&gt;sextract32()&lt;/code&gt; 代码，以用来取得指令中各字段的值：&lt;/p&gt;
&lt;h3 id=&#34;field-示例&#34;&gt;Field 示例&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Input&lt;/th&gt;
          &lt;th&gt;Generated code&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;%disp 0:s16&lt;/td&gt;
          &lt;td&gt;sextract(i, 0, 16)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;%imm9 16:6 10:3&lt;/td&gt;
          &lt;td&gt;extract(i, 16, 6) &amp;laquo; 3&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;%disp12 0:s1 1:1 2:10&lt;/td&gt;
          &lt;td&gt;sextract(i, 0, 1) &amp;laquo; 11&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;%shimm8 5:s8 13:1 !function=expand_shimm8&lt;/td&gt;
          &lt;td&gt;expand_shimm8(sextract(i, 5, 8)) &amp;laquo; 1&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;以 RISC-V 的 &lt;code&gt;U-type&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/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.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;code&gt;imm&lt;/code&gt; 占 &lt;code&gt;insn[31:12]&lt;/code&gt;，共20位，&lt;code&gt;rd&lt;/code&gt; 占 &lt;code&gt;insn[11:7]&lt;/code&gt;，且 &lt;code&gt;imm&lt;/code&gt; 需要做 符号扩展 后 &lt;code&gt;左移 12 位&lt;/code&gt; (&lt;code&gt;20-bit immediate is shifted left by 12 bits to form U immediates&lt;/code&gt;)。因此，如果我们要定义 RISC-V 的 &lt;code&gt;U-type&lt;/code&gt; 指令，则可以声明成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd       7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_u    12:s20                 !function=ex_shift_12
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;20 表示占 20 bits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后会生成如下的代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;static void decode_insn32_extract_u()&lt;/code&gt; 是由下文 Format 定义所生成的，而 &lt;code&gt;arg_u *a&lt;/code&gt; 则是由 Argument Set 定义所生成的，将会在后面的部分再做说明。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以看到：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;a-&amp;gt;imm&lt;/code&gt; 是由 &lt;code&gt;insn[31:12]&lt;/code&gt; 所取得并做符号扩展，且会再调用 &lt;code&gt;ex_shift_12()&lt;/code&gt; 来 &lt;code&gt;左移 12 个 bits&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;P.S. RISC-V 的 &lt;code&gt;ex_shift_12()&lt;/code&gt; 是通过定义在&lt;code&gt;./target/riscv/translate.c&lt;/code&gt; 中 &lt;code&gt;EX_SH&lt;/code&gt; 这个 macro 所展开的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EX_SH(amount) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    static int ex_shift_##amount(DisasContext *ctx, int imm) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    {                                         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        return imm &amp;lt;&amp;lt; amount;                 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;a-&amp;gt;rd&lt;/code&gt; 是由 &lt;code&gt;insn[11:7]&lt;/code&gt; 所取得。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，在 &lt;code&gt;Decodetree&lt;/code&gt; 的 spec 中也有提到，我们可以通过只定义 &lt;code&gt;!function&lt;/code&gt; 来直接调用该 function。在这种情况下，只有 &lt;code&gt;DisasContext&lt;/code&gt; 会被传入该 function。&lt;/p&gt;
&lt;p&gt;如 ARM Thumb &lt;code&gt;./target/arm/t16.decode&lt;/code&gt; 就有定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Set S if the instruction is outside of an IT block.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%s               !function=t16_setflags
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;disas_t16_extract_addsub_2i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_s_rri_rot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;t16_setflags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意，未包含任何 &lt;code&gt;unnamed_fields&lt;/code&gt; 或 &lt;code&gt;!function&lt;/code&gt; 的 &lt;code&gt;Field&lt;/code&gt; 会被视为错误。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;argument-set&#34;&gt;Argument Set&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Argument Set&lt;/code&gt; 定义用来保存从指令中所截取出来各字段的值。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;args_def    := &amp;#39;&amp;amp;&amp;#39; identifier ( args_elt )+ ( !extern )?
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;args_elt    := identifier
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由 &lt;code&gt;&amp;amp;&lt;/code&gt; 开头，随后紧接着一个或多个的 &lt;code&gt;identifier&lt;/code&gt; ，并可再加上可选的 &lt;code&gt;!extern&lt;/code&gt; 。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自订，如：&lt;code&gt;regs&lt;/code&gt;、&lt;code&gt;loadstore&lt;/code&gt;… 等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!extern&lt;/code&gt; 则表示是否在其他地方已经由其他的 decoder 定义过。如果有该字段，就&lt;strong&gt;不会&lt;/strong&gt;再次生成对应的 &lt;code&gt;argument set struct&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;argument-set-示例&#34;&gt;Argument Set 示例&lt;/h3&gt;
&lt;p&gt;例1：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;ampreg3 ra rb rc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会生成以下的 &lt;code&gt;argument set struct&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_reg3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例2：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;loadstore reg base offset
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;则会生成以下的 &lt;code&gt;argument set struct&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_loadstore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因此，以刚刚的 RISC-V &lt;code&gt;U-type&lt;/code&gt; 指令为例，我们需要从指令中截取 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 字段的值，可以声明其 &lt;code&gt;argument set&lt;/code&gt; 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;u    imm rd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后会生成以下的 &lt;code&gt;argument set struct&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此 &lt;code&gt;argument set struct&lt;/code&gt; 会被传入由 &lt;code&gt;Format&lt;/code&gt; 定义所生成的 extract function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所传入的&lt;code&gt;arg_u&lt;/code&gt; 会保存从指令中截取出的 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 字段的值，待后续使用。&lt;/p&gt;
&lt;h2 id=&#34;format&#34;&gt;Format&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Format&lt;/code&gt; 定义了指令的格式 (如 RISC-V 中的 &lt;code&gt;R&lt;/code&gt;、&lt;code&gt;I&lt;/code&gt;、&lt;code&gt;S&lt;/code&gt;、&lt;code&gt;B&lt;/code&gt;、&lt;code&gt;U&lt;/code&gt;、&lt;code&gt;J-type&lt;/code&gt;)，并会生成对应的 decode function。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fmt_def      := &amp;#39;@&amp;#39; identifier ( fmt_elt )+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fmt_elt      := fixedbit_elt | field_elt | field_ref | args_ref
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fixedbit_elt := [01.-]+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;field_elt    := identifier &amp;#39;:&amp;#39; &amp;#39;s&amp;#39;? number
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;field_ref    := &amp;#39;%&amp;#39; identifier | identifier &amp;#39;=&amp;#39; &amp;#39;%&amp;#39; identifier
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;args_ref     := &amp;#39;&amp;amp;&amp;#39; identifier
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由 &lt;code&gt;@&lt;/code&gt; 开头，随后紧接着一个 &lt;code&gt;identifier&lt;/code&gt; 及一个以上的 &lt;code&gt;fmt_elt&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自订，如：&lt;code&gt;opr&lt;/code&gt;、&lt;code&gt;opi&lt;/code&gt;… 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fmt_elt&lt;/code&gt; 则可以采用以下不同的语法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fixedbit_elt&lt;/code&gt; 包含一个或多个 &lt;code&gt;0&lt;/code&gt;、&lt;code&gt;1&lt;/code&gt;、&lt;code&gt;.&lt;/code&gt;、&lt;code&gt;-&lt;/code&gt;，每一个代表指令中的 1 个 bit。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt; 代表该 bit 可以用 &lt;code&gt;0&lt;/code&gt; 或是 &lt;code&gt;1&lt;/code&gt; 来表示。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-&lt;/code&gt; 代表该 bit 完全被忽略。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;field_elt&lt;/code&gt; 可以用 Field 的语法来声明。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eg：&lt;code&gt;ra:5&lt;/code&gt;、&lt;code&gt;rb:5&lt;/code&gt;、&lt;code&gt;lit:8&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;field_ref&lt;/code&gt; 有下列两种格式 (以下范例参考上文所定义之 Field)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&#39;%&#39; identifier&lt;/code&gt;：直接参考一个被定义过的 &lt;code&gt;Field&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如：&lt;code&gt;%rd&lt;/code&gt;，会生成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;identifier &#39;=&#39; &#39;%&#39; identifier&lt;/code&gt;：直接参考一个被定义过的 &lt;code&gt;Field&lt;/code&gt;，但通过第一个 &lt;code&gt;identifier&lt;/code&gt; 来重命名其所对应的 &lt;code&gt;argument&lt;/code&gt; 名称。此方式可以用来指定不同的 &lt;code&gt;argument&lt;/code&gt; 名称来参考至同一个 &lt;code&gt;Field&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如：&lt;code&gt;my_rd=%rd&lt;/code&gt;，会生成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;args_ref&lt;/code&gt; 指定所传入 decode function 的 &lt;code&gt;Argument Set&lt;/code&gt;。若没有指定 &lt;code&gt;args_ref&lt;/code&gt; 的话，&lt;code&gt;Decodetree&lt;/code&gt; 会根据 &lt;code&gt;field_elt&lt;/code&gt; 或 &lt;code&gt;field_ref&lt;/code&gt; 自动生成一个 &lt;code&gt;Argument Set&lt;/code&gt;。此外，一个 &lt;code&gt;Format&lt;/code&gt; 最多只能包含一个 &lt;code&gt;args_ref&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 &lt;code&gt;fixedbit_elt&lt;/code&gt; 或 &lt;code&gt;field_ref&lt;/code&gt; 被定义时，该 &lt;code&gt;Foramt&lt;/code&gt; 的所有的 bits 都必须被定义 (可通过 &lt;code&gt;fixedbit_elt&lt;/code&gt; 或 &lt;code&gt;.&lt;/code&gt; 来定义各个 bits，&lt;code&gt;空格&lt;/code&gt;会被忽略)。&lt;/p&gt;
&lt;h3 id=&#34;format-示例&#34;&gt;Format 示例&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@opi    ...... ra:5 lit:8    1 ....... rc:5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;op1&lt;/code&gt; 这个 &lt;code&gt;Format&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:26] 可为 &lt;code&gt;0&lt;/code&gt; 或 &lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[25:21] 为 &lt;code&gt;ra&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[20:13] 为 &lt;code&gt;lit&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[12] 固定为 &lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[11:5] 可为 &lt;code&gt;0&lt;/code&gt; 或 &lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[4:0] 为 &lt;code&gt;rc&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此 &lt;code&gt;Format&lt;/code&gt; 会生成以下的 decode function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_opi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ra&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于我们没有指定 &lt;code&gt;args_ref&lt;/code&gt;，因此 &lt;code&gt;Decodetree&lt;/code&gt; 根据了 &lt;code&gt;field_elt&lt;/code&gt; 的定义，自动生成了 &lt;code&gt;arg_decode_insn320&lt;/code&gt; 这个 &lt;code&gt;Argument Set&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;以 RISC-V &lt;code&gt;I-type&lt;/code&gt; 指令为例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rs1       15:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# immediates:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_i    20:s12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argment sets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;i    imm rs1 rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@i       ........ ........ ........ ........ &amp;amp;i      imm=%imm_i     %rs1 %rd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;i&lt;/code&gt; 这个 &lt;code&gt;Format&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:20] 为 &lt;code&gt;imm&lt;/code&gt;，且为 符号扩展。&lt;/li&gt;
&lt;li&gt;insn[19:5] 为 &lt;code&gt;rs1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[11:7] 为 &lt;code&gt;rd&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，我们可以看到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;此 &lt;code&gt;Format&lt;/code&gt; 指定了 &lt;code&gt;Argument Set&lt;/code&gt;：&lt;code&gt;&amp;amp;i&lt;/code&gt;。 &lt;code&gt;&amp;amp;i&lt;/code&gt; 中必须包含所有有用到的 &lt;code&gt;arguments&lt;/code&gt; (也就是：&lt;code&gt;imm&lt;/code&gt;、&lt;code&gt;rs1&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imm&lt;/code&gt; 是通过重命名的方式来参考 &lt;code&gt;%imm_i&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此范例会生成以下的 decode function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32extract_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;相比于第一个范例，由于这次我们有指定 &lt;code&gt;args_ref&lt;/code&gt;：&lt;code&gt;&amp;amp;i&lt;/code&gt;，因此对应的 &lt;code&gt;arg_i&lt;/code&gt; 会被传入 decode function。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;回到先前的 RISC-V &lt;code&gt;U-type&lt;/code&gt; 指令，我们可以如同 &lt;code&gt;I-type&lt;/code&gt; 指令定义其格式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# immediates:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_u    12:s20                 !function=ex_shift_12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argument sets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;u    imm rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@u       ....................      ..... ....... &amp;amp;u      imm=%imm_u          %rd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;u&lt;/code&gt; 这个 &lt;code&gt;Format&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:12] 为 &lt;code&gt;imm&lt;/code&gt;，且为 符号扩展。&lt;/li&gt;
&lt;li&gt;insn[11:7] 为 &lt;code&gt;rd&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;会生成以下的 decode function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们可以看到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;此 &lt;code&gt;Format&lt;/code&gt; 指定了 &lt;code&gt;Argument Set&lt;/code&gt;：&lt;code&gt;&amp;amp;u&lt;/code&gt;。 &lt;code&gt;&amp;amp;u&lt;/code&gt; 中必须包含所有有用到的 &lt;code&gt;arguments&lt;/code&gt; (也就是：&lt;code&gt;imm&lt;/code&gt;、&lt;code&gt;rd&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imm&lt;/code&gt; 是通过重命名的方式来参考 &lt;code&gt;%imm_u&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;pattern&#34;&gt;Pattern&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Pattern&lt;/code&gt; 实际定义了一个指令的 decode 方式。&lt;code&gt;Decodetree&lt;/code&gt; 会根据 &lt;code&gt;Patterns&lt;/code&gt; 的定义，来动态产生出对应的 &lt;code&gt;switch-case&lt;/code&gt; decode 判断分支。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pat_def      := identifier ( pat_elt )+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pat_elt      := fixedbit_elt | field_elt | field_ref | args_ref | fmt_ref | const_elt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fmt_ref      := &amp;#39;@&amp;#39; identifier
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const_elt    := identifier &amp;#39;=&amp;#39; number
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由用户所定义的 &lt;code&gt;identifier&lt;/code&gt;，随后紧接着一个以上的 &lt;code&gt;pat_elt&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自订，如：&lt;code&gt;addl_r&lt;/code&gt;、&lt;code&gt;addli&lt;/code&gt; … 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pat_elt&lt;/code&gt; 则可以采用以下不同的语法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fixedbit_elt&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;fixedbit_elt&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;field_elt&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;field_elt&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;field_ref&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;field_ref&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;args_ref&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;args_ref&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fmt_ref&lt;/code&gt; 直接参考一个被定义过的Format。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;const_elt&lt;/code&gt; 可以直接指定某一个 &lt;code&gt;argument&lt;/code&gt; 的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于 &lt;code&gt;Pattern&lt;/code&gt; 实际定义了一个指令的 decode 方式，因此&lt;strong&gt;所有的 bits&lt;/strong&gt; 及 &lt;strong&gt;arguments (如果有参考 args_ref 的话)&lt;/strong&gt;  都必须明确的被定义，如果在搭配了所有的 &lt;code&gt;pat_elt&lt;/code&gt; 后还有未定义的 bits 或是 arguments 的话，&lt;code&gt;Decodetree&lt;/code&gt; 便会报错。&lt;/p&gt;
&lt;p&gt;此外，&lt;code&gt;Pattern&lt;/code&gt; 所产生出来的 decoder，最后还会调用对应的 &lt;code&gt;translator function&lt;/code&gt;。&lt;code&gt;translator function&lt;/code&gt; 需开发者自行定义。&lt;/p&gt;
&lt;h3 id=&#34;pattern-示例&#34;&gt;Pattern 示例&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;addl_i   010000 ..... ..... .... 0000000 ..... @opi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;addl_i&lt;/code&gt; 这个指令的 &lt;code&gt;Pattern&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:26] 为 &lt;code&gt;010000&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[11:5] 为 &lt;code&gt;0000000&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;参考了 Format 示例中 定义的 &lt;code&gt;@opi&lt;/code&gt; &lt;code&gt;Format&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;由于 &lt;code&gt;Pattern&lt;/code&gt; 的&lt;strong&gt;所有 bits&lt;/strong&gt; 都必须明确的被定义，因此 &lt;code&gt;@opi&lt;/code&gt; 必须包含其余 &lt;code&gt;insn[25:12]&lt;/code&gt; 及 &lt;code&gt;insn[4:0]&lt;/code&gt; 的格式定义，否则 &lt;code&gt;Decodetree&lt;/code&gt; 便会报错。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后 &lt;code&gt;addl_i&lt;/code&gt; 的 decoder 还会调用 &lt;code&gt;trans_addl_i()&lt;/code&gt; 这个 &lt;code&gt;translator function&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;搭配之前介绍的 Fields、Argument Sets 及 Formats，让我们再看几个完整的例子应该会更清楚 &lt;code&gt;Decodetree&lt;/code&gt; 是怎产生一个指令的 decoder 的。&lt;/p&gt;
&lt;p&gt;首先是 RISC-V 的 &lt;code&gt;lui&lt;/code&gt; 及 &lt;code&gt;auipc&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/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.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 class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# immediates:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_u    12:s20                 !function=ex_shift_12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argument sets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;u    imm rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Formats:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@u       ....................      ..... ....... &amp;amp;u      imm=%imm_u          %rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Patterns
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lui      ....................       ..... 0110111 @u
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;auipc    ....................       ..... 0010111 @u
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会产生以下 &lt;code&gt;lui&lt;/code&gt; 及 &lt;code&gt;auipc&lt;/code&gt; 的 decoder：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000007f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000017&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_auipc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000037&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_lui&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;回顾到目前为止所介绍的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Argument Sets&lt;/code&gt;：&lt;code&gt;&amp;amp;u&lt;/code&gt; 这个 &lt;code&gt;argument set&lt;/code&gt; 包含了 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 这两个 &lt;code&gt;arguments&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Fields&lt;/code&gt;： &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 分别位在 insn[31:12] 及 insn[11:7]，且 &lt;code&gt;imm&lt;/code&gt; 为 符号扩展。最后在截取出 &lt;code&gt;imm&lt;/code&gt; 的值后，还会调用 &lt;code&gt;ex_shift_12()&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Formats&lt;/code&gt;：&lt;code&gt;@u&lt;/code&gt; 定义了 RISC-V &lt;code&gt;U-type&lt;/code&gt; 指令的格式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;参考了 &lt;code&gt;&amp;amp;u&lt;/code&gt; 这个 &lt;code&gt;Argument Set&lt;/code&gt;，因此 decode function 会传入 &lt;code&gt;arg_u&lt;/code&gt; 作为参数。&lt;/li&gt;
&lt;li&gt;insn[31:12] 参考了 &lt;code&gt;imm_u&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt; (并重命名为 &lt;code&gt;imm&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;insn[11:7] 参考了 &lt;code&gt;rd&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Patterns&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lui&lt;/code&gt; 的 &lt;code&gt;opcode&lt;/code&gt; (insn[6:0]) 为 &lt;code&gt;0010111&lt;/code&gt;，也就是 &lt;code&gt;0x17&lt;/code&gt;，在产生出来的 &lt;code&gt;switch-case&lt;/code&gt; 中可以看到其对应的 &lt;code&gt;case&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lui&lt;/code&gt; 的 decoder 最后调用了 &lt;code&gt;trans_lui()&lt;/code&gt;，并传入 &lt;code&gt;DisasContext&lt;/code&gt; 及经由 &lt;code&gt;decode_insn32_extract_u()&lt;/code&gt; 所解析出来的 &lt;code&gt;arg_u&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auipc&lt;/code&gt; 的 &lt;code&gt;opcode&lt;/code&gt; (insn[6:0]) 为 &lt;code&gt;0110111&lt;/code&gt;，也就是 &lt;code&gt;0x37&lt;/code&gt;，在产生出来的 &lt;code&gt;switch-case&lt;/code&gt; 中可以看到其对应的 &lt;code&gt;case&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auipc&lt;/code&gt; 的 decoder 最后调用了 &lt;code&gt;trans_auipc()&lt;/code&gt;，并传入 &lt;code&gt;DisasContext&lt;/code&gt; 及经由 &lt;code&gt;decode_insn32_extract_u()&lt;/code&gt; 所解析出来的 &lt;code&gt;arg_u&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;P.S. 这边由于 &lt;code&gt;Decodetree&lt;/code&gt; 发现 &lt;code&gt;lui&lt;/code&gt; 及 &lt;code&gt;auipc&lt;/code&gt; 可以共用 &lt;code&gt;decode_insn32_extract_u()&lt;/code&gt;，因此将其提到了 &lt;code&gt;switch-case&lt;/code&gt; 之外。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000007f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000017&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_auipc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000037&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_lui&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们另外可以发现，&lt;code&gt;Pattern&lt;/code&gt; + &lt;code&gt;Format&lt;/code&gt; 把所有的 32-bits 都给了明确的定义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Pattern&lt;/code&gt; 定义了 &lt;code&gt;opcode&lt;/code&gt; (insn[6:0])。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Format&lt;/code&gt; 参考了 &lt;code&gt;imm&lt;/code&gt; (insn[31:12]) 及 &lt;code&gt;rd&lt;/code&gt; (insn[11:7])。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果有任何未明确定义的 bits 的话，&lt;code&gt;Decodetree&lt;/code&gt; 便会报错，例如如果我们将 &lt;code&gt;lui&lt;/code&gt; 的 &lt;code&gt;opcode&lt;/code&gt; 最高 2 个 bits (insn[6:5]) 由 &lt;code&gt;01&lt;/code&gt; 改成 &lt;code&gt;..&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lui      ....................       ..... ..10111 @u
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 在解析时，便会报错：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;./insn32.decode:17: error: (‘bits left unspecified (0x00000060)’,)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 提醒我们，insn[6:5] (&lt;code&gt;0x00000060&lt;/code&gt;) 尚未给出明确定义，并会显示出其错误的行数。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;trans_lui()&lt;/code&gt; 和 &lt;code&gt;trans_auipc()&lt;/code&gt; 被定义在 &lt;code&gt;target/riscv/insn_trans/trans_rvi.inc.c&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;trans_lui&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_lui&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;tcg_gen_movi_tl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu_gpr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;trans_auipc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_auipc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;tcg_gen_movi_tl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu_gpr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pc_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到 &lt;code&gt;trans_*()&lt;/code&gt; 负责实际指令的业务逻辑及产生对应的 &lt;code&gt;TCG codes&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如同先前所介绍，&lt;code&gt;Patterns&lt;/code&gt; 的 &lt;code&gt;pat_elt&lt;/code&gt; 也可以采用 &lt;code&gt;field_elt&lt;/code&gt; 语法，如 RISC-V 的 &lt;code&gt;fence&lt;/code&gt; 指令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fence    ---- pred:4 succ:4 ----- 000 ----- 0001111
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;insn[27:24] 为 &lt;code&gt;pred&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[23:20] 为 &lt;code&gt;succ&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[14:12] 固定为 &lt;code&gt;000&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[6:0] 为 &lt;code&gt;opcode&lt;/code&gt; (&lt;code&gt;0001111&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;没有参考任何的 &lt;code&gt;Format&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;剩下的 insn[31:28]、insn[19:15]、insn[11:7] 被声明为 &lt;code&gt;-&lt;/code&gt;，因此就算没有被明确定义也没有关系。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所生成 &lt;code&gt;fence&lt;/code&gt; 的 decoder 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;succ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_decode_insn32_Fmt_0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;succ&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_decode_insn32_Fmt_0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000707f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000000f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_fence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;值得注意的是，虽然这次我们没有参考任何的 &lt;code&gt;Argument Set&lt;/code&gt;，但 &lt;code&gt;Decodetree&lt;/code&gt; 还是替我们生成了一个包含 &lt;code&gt;pred&lt;/code&gt; 和 &lt;code&gt;succ&lt;/code&gt; 的 &lt;code&gt;arg_decode_insn320&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;trans_fence()&lt;/code&gt; 同样是被定义在 &lt;code&gt;./target/riscv/insn_trans/trans_rvi.inc.c&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;trans_fence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_fence&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;tcg_gen_mb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TCG_MO_ALL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TCG_BAR_SC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;pattern-groups&#34;&gt;Pattern Groups&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Pattern Groups&lt;/code&gt; 由一个以上的 &lt;code&gt;Patterns&lt;/code&gt; 所组成，其主要差别是不同 &lt;code&gt;Patterns&lt;/code&gt; 之间的 bits 可以 overlap。当同组中有多个 &lt;code&gt;Patterns&lt;/code&gt; 时，会依据该组中各 &lt;code&gt;Pattern&lt;/code&gt; 的声明顺序依序判断目前的指令是否符合其定义。除此之外，当符合的 &lt;code&gt;Pattern&lt;/code&gt; 其 &lt;code&gt;trans_*()&lt;/code&gt; 回传值为 &lt;code&gt;false&lt;/code&gt; 时，也会被视为&lt;strong&gt;不相符&lt;/strong&gt;，而继续判断该组中的下一个 &lt;code&gt;Pattern&lt;/code&gt;。因此 &lt;code&gt;Pattern Groups&lt;/code&gt; 非常适合将多个相似格式的指令给组成同一个 &lt;code&gt;Pattern Group&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;group    := &amp;#39;{&amp;#39; ( pat_def | group )+ &amp;#39;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;各 &lt;code&gt;Pattern Group&lt;/code&gt; 以 &lt;code&gt;{&lt;/code&gt; 开头，并以 &lt;code&gt;}&lt;/code&gt; 结尾，且允许 &lt;code&gt;nested pattern groups&lt;/code&gt; 的存在，其他语法皆与 &lt;code&gt;Pattern&lt;/code&gt; 相同。&lt;/p&gt;
&lt;h3 id=&#34;pattern-group-示例&#34;&gt;Pattern Group 示例&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    nop   000010 ----- ----- 0000 001001 0 00000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    copy  000010 00000 r1:5  0000 001001 0 rt:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  or      000010 rt2:5 r1:5  cf:4 001001 0 rt:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会产生以下的 decoder：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0xfc000fe0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x08000240&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000f000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000001f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;extract_decode_Fmt_0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_nop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x03e00000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nf&#34;&gt;extract_decode_Fmt_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				　
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;extract_decode_Fmt_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_or&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当指令的值符合 &lt;code&gt;nop&lt;/code&gt; 及 &lt;code&gt;copy&lt;/code&gt; 这个内层 &lt;code&gt;Pattern Group&lt;/code&gt; 时，会先判断该指令是否符合 &lt;code&gt;nop&lt;/code&gt; 指令的定义，且 &lt;code&gt;trans_nop()&lt;/code&gt; 的回传值为 &lt;code&gt;true&lt;/code&gt;。否则的话，就会继续判断是否符合同组中的 &lt;code&gt;copy&lt;/code&gt; 指令。若都不符，就会再判断是否符合外层 &lt;code&gt;Pattern Group&lt;/code&gt; 的 &lt;code&gt;or&lt;/code&gt; 指令。若仍不符，才会回传 &lt;code&gt;false&lt;/code&gt; 表示 decode 失败。&lt;/p&gt;
&lt;p&gt;与单纯使用 &lt;code&gt;Pattern&lt;/code&gt; 最大不同的是，当一 &lt;code&gt;Pattern&lt;/code&gt; 的 &lt;code&gt;trans_*()&lt;/code&gt; 回传值为 &lt;code&gt;false&lt;/code&gt; 时，不会直接回传 &lt;code&gt;false&lt;/code&gt; (代表 decode 失败)，而是会接续着判断后续的 &lt;code&gt;Patterns&lt;/code&gt; 是否相符。&lt;/p&gt;
&lt;p&gt;RISC-V Compressed-Extension 中的 &lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、及 &lt;code&gt;c.add&lt;/code&gt; 指令，由于这三个指令的格式非常相似，因此非常适合使用 &lt;code&gt;Pattern Group&lt;/code&gt; 来定义：&lt;/p&gt;
&lt;p&gt;RISC-V spec. 中定义：&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/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.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;ul&gt;
&lt;li&gt;&lt;code&gt;C.EBREAK&lt;/code&gt;指令与&lt;code&gt;C.ADD&lt;/code&gt;指令共享相同的&lt;code&gt;opcode&lt;/code&gt;，但是&lt;code&gt;rd&lt;/code&gt;和&lt;code&gt;rs2&lt;/code&gt;都为&lt;code&gt;zero&lt;/code&gt;，因此也可以使用&lt;code&gt;CR&lt;/code&gt;格式。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C.JALR&lt;/code&gt;指令只有在&lt;code&gt;rs1≠x0&lt;/code&gt;时才有效；当&lt;code&gt;rs1=x0&lt;/code&gt;时，对应的代码点是&lt;code&gt;C.EBREAK&lt;/code&gt;指令。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C.ADD&lt;/code&gt;指令只有在&lt;code&gt;rs2≠x0&lt;/code&gt;时才有效；当&lt;code&gt;rs2=x0&lt;/code&gt;时，对应的代码点是&lt;code&gt;C.JALR&lt;/code&gt;和&lt;code&gt;C.EBREAK&lt;/code&gt;指令。具有&lt;code&gt;rs2̸=x0&lt;/code&gt;和&lt;code&gt;rd=x0&lt;/code&gt;的代码点是&lt;code&gt;HINTs&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、&lt;code&gt;c.add&lt;/code&gt; 三个指令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[15:13]、insn[12]、insn[1:0] 的值皆相同。&lt;/li&gt;
&lt;li&gt;当 insn[11:7] 且 insn[6:2] 的值皆为 &lt;code&gt;0&lt;/code&gt; (&lt;code&gt;rs1=0&lt;/code&gt; 且 &lt;code&gt;rs2=0&lt;/code&gt;) 时为 &lt;code&gt;c.ebreak&lt;/code&gt; 指令。&lt;/li&gt;
&lt;li&gt;当只有 insn[11:7] 的值为 &lt;code&gt;0&lt;/code&gt; (&lt;code&gt;rs1=0&lt;/code&gt; 且 &lt;code&gt;rs2≠0&lt;/code&gt;) 时为 &lt;code&gt;c.jalr&lt;/code&gt; 指令。&lt;/li&gt;
&lt;li&gt;否则为 &lt;code&gt;c.add&lt;/code&gt; 指令 (&lt;code&gt;rs1≠x0&lt;/code&gt; 且 &lt;code&gt;rs2≠0&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rs2_5     2:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argument Sets
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;r         rd rs1 rs2   !extern
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;i         imm rs1 rd   !extern
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Formats
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@cr        ....  ..... .....  .. &amp;amp;r      rs2=%rs2_5       rs1=%rd     %rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@c_jalr    ... . .....  ..... .. &amp;amp;i      imm=0 rs1=%rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Pattern Groups
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ebreak          100 1  00000  00000 10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  jalr            100 1  .....  00000 10 @c_jalr rd=1  # C.JALR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  add             100 1  .....  ..... 10 @cr
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所生成的 decoder 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_c_jalr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_cr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_decode_insn16_Fmt_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn162&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_decode_insn162&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_decode_insn162&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_r&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000f003&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00009002&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000ffc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_decode_insn16_Fmt_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn162&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_ebreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn162&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000007c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_c_jalr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_jalr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_cr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当指令格式符合 &lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、&lt;code&gt;c.add&lt;/code&gt; 的 &lt;code&gt;Pattern Group&lt;/code&gt; 时，会依序判断该指令是否符合 &lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、&lt;code&gt;c.add&lt;/code&gt; 的定义以及其对应的 &lt;code&gt;trans_*()&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;另外值得一提的是，在 &lt;code&gt;c_jalr&lt;/code&gt; &lt;code&gt;Format&lt;/code&gt; 和 &lt;code&gt;jalr&lt;/code&gt; &lt;code&gt;Pattern&lt;/code&gt; 中有分别指定其 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 的值为 &lt;code&gt;0&lt;/code&gt;，所生成的 codes 也会分别在对应的地方将该值设为 &lt;code&gt;0&lt;/code&gt; (见 codes 注解说明)。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;以上就是 &lt;code&gt;Decodetree&lt;/code&gt; 的语法说明。通过 &lt;code&gt;Decodetree&lt;/code&gt;，我们不用再像以前以样写一大包的 &lt;code&gt;switch-case&lt;/code&gt; 来 decode 指令。将不同类型的指令写至不同的 decode 档，不仅方便维护，阅读起来也更为容易。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--translate&lt;/code&gt;：translator function 的 prefix，默认为 &lt;code&gt;trans&lt;/code&gt;。一旦指定后，translator function 的 scope 就不会再是 &lt;code&gt;static&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--decode&lt;/code&gt;：decode function 的 prefix，默认为 &lt;code&gt;decode&lt;/code&gt;，且 scope 为 &lt;code&gt;static&lt;/code&gt;。一旦指定后，decode function 的 scope 就不会再是 &lt;code&gt;static&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--static-decode&lt;/code&gt;：如同 &lt;code&gt;--decode&lt;/code&gt;，不过 decode function 的 scope 仍维持为 &lt;code&gt;static&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o&lt;/code&gt; / &lt;code&gt;--output&lt;/code&gt;：指定生成的 decoder &lt;code&gt;.c&lt;/code&gt; 档路径。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-w&lt;/code&gt; / &lt;code&gt;--insnwidth&lt;/code&gt;：指令长度，eg：&lt;code&gt;32&lt;/code&gt; or &lt;code&gt;16&lt;/code&gt;，默认为 &lt;code&gt;32&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--varinsnwidth&lt;/code&gt;：指令为不定长度。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;最后一个参数&lt;/code&gt;为输入的 decode 档路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;运行范例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./decodetree.py -o target/riscv/decode_insn16.inc.c --static-decode decode_insn16 \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -w 16 ./insn16.decode
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;static inline int32_t sextract32(uint32_t value, int start, int length){    assert(start &amp;gt;= 0 &amp;amp;&amp;amp; length &amp;gt; 0 &amp;amp;&amp;amp; length &amp;lt;= 32 - start);        return ((int32_t)(value &amp;lt;&amp;lt; (32 - length - start))) &amp;gt;&amp;gt; (32 - length);}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>QEMU 在 decode 指令的时候，需要调用各平台所定义的 instruction decoders 来解析指令。如在 ARM 平台下，就定义了：<code>disas_arm_insn()</code>、<code>disas_thumb_insn()</code> 及 <code>disas_thumb2_insn()</code> 等来分别负责 ARM 32-bits 指令、ARM Thumb 指令及 ARM Thumb2 指令的解析。</p>
<p>而 <code>Decodetree</code> 则是由 <code>Bastian Koppelmann</code> 于 2017 年在 移植 RISC-V QEMU 的时候所提出来的机制 (详见：<a href="https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07735.html">讨论邮件1</a>、<a href="https://lists.gnu.org/archive/html/qemu-devel/2017-10/msg05046.html">讨论邮件2</a>)。提出该机制主要是因为过往的 instruction decoders (如：ARM) 都是采用一堆 <code>switch-case</code> 来做判断。不仅难阅读，也难以维护。</p>
<p>因此 <code>Bastian Koppelmann</code> 就提出了 <code>Decodetree</code> 的机制，开发者只需要通过 <code>Decodetree</code> 的语法定义各个指令的格式，便可交由 <code>Decodetree</code> 来动态生成对应包含 <code>switch-case</code> 的 instruction decoder <code>.c</code> 文档。</p>
<p><code>Decodetree</code> 特别适合像 RISC-V 这种具有<strong>固定指令格式</strong>的 ISA。</p>
<ul>
<li>因为各字段都在固定的位置，(如 RISC-V 的 <code>opcode</code> 都是固定在 <code>bits[6..0]</code> 的位置)。</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/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.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><code>Decodetree</code> 其实是由 Python script (<code>./scripts/decodetree.py</code>) 所生成的。使用文档可以参考：<code>./docs/devel/decodetree.rst</code>，里面有详细定义了其语法的格式。QEMU 在编译时，会调用 <code>Decodetree</code>，根据各平台所定义的 decode 文档，动态生成对应的 decoder。</p>
<ul>
<li>
<p>如 RISC-V 的 instruction decoders 就是被定义在：<code>./target/riscv/*.decode</code> 中。其 <code>Makefile.obj</code> 就有如下的声明：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">DECODETREE = $(SRC_PATH)/scripts/decodetree.py
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">decode32-y = $(SRC_PATH)/target/riscv/insn32.decode
</span></span><span class="line"><span class="cl">decode32-$(TARGET_RISCV64) += $(SRC_PATH)/target/riscv/insn32-64.decode
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">target/riscv/decode_insn32.inc.c: $(decode32-y) $(DECODETREE)
</span></span><span class="line"><span class="cl">	$(call quiet-command, \
</span></span><span class="line"><span class="cl">	  $(PYTHON) $(DECODETREE) -o $@ --static-decode decode_insn32 \
</span></span><span class="line"><span class="cl">          $(decode32-y), &#34;GEN&#34;, $(TARGET_DIR)$@)
</span></span></code></pre></div></li>
</ul>
<p><code>Decodetree</code> 的语法共分为：Fields、Argument Sets、Formats、Patterns 五部分。本文将介绍如何通过 <code>Decodetree</code> 的语法，来动态生成一个指令的 decoder。</p>
<h2 id="field">Field</h2>
<p><code>Field</code> 定义如何取出一指令中，各<strong>字段</strong> (eg: <code>rd</code>, <code>rs1</code>, <code>rs2</code>, <code>imm</code>) 的值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">field_def     := &#39;%&#39; identifier ( unnamed_field )* ( !function=identifier )?
</span></span><span class="line"><span class="cl">unnamed_field := number &#39;:&#39; ( &#39;s&#39; ) number
</span></span></code></pre></div><p>其语法由 <code>%</code> 开头，随后紧接着一个 <code>identifier</code> 及零个或多个 <code>unamed_field</code>，并可再加上可选的 <code>!function</code>。</p>
<ul>
<li><code>identifier</code> 可由开发者自定，如：<code>rd</code>、<code>imm</code>… 等。</li>
<li><code>unamed_field</code> 定义了该字段的所在比特。第一个数字定义了该字段的 <code>least-significant bit position</code>，第二个数字则定义了该字段的<code>比特长度</code>。另外可加上可选的 <code>s</code> 字符来标明在取出该字段后，是否需要做 符号扩展。
<ul>
<li>Eg：<code>%rd 7:5</code> 代表 <code>rd</code> 占了指令中 bits 7 ~ bits 11 的位置 (insn[11:7])，共 5 bits。</li>
</ul>
</li>
<li><code>!function</code> 定义在截取出该字段的值后，所会再调用的 function。</li>
</ul>
<p><code>Field</code> (32-bits 指令) 最后会生成对应的 <code>extract32()</code> 及 <code>sextract32()</code> 代码，以用来取得指令中各字段的值：</p>
<h3 id="field-示例">Field 示例</h3>
<table>
  <thead>
      <tr>
          <th>Input</th>
          <th>Generated code</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>%disp 0:s16</td>
          <td>sextract(i, 0, 16)</td>
      </tr>
      <tr>
          <td>%imm9 16:6 10:3</td>
          <td>extract(i, 16, 6) &laquo; 3</td>
      </tr>
      <tr>
          <td>%disp12 0:s1 1:1 2:10</td>
          <td>sextract(i, 0, 1) &laquo; 11</td>
      </tr>
      <tr>
          <td>%shimm8 5:s8 13:1 !function=expand_shimm8</td>
          <td>expand_shimm8(sextract(i, 5, 8)) &laquo; 1</td>
      </tr>
  </tbody>
</table>
<p>以 RISC-V 的 <code>U-type</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/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.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>其中，<code>imm</code> 占 <code>insn[31:12]</code>，共20位，<code>rd</code> 占 <code>insn[11:7]</code>，且 <code>imm</code> 需要做 符号扩展 后 <code>左移 12 位</code> (<code>20-bit immediate is shifted left by 12 bits to form U immediates</code>)。因此，如果我们要定义 RISC-V 的 <code>U-type</code> 指令，则可以声明成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">%rd       7:5
</span></span><span class="line"><span class="cl">%imm_u    12:s20                 !function=ex_shift_12
</span></span></code></pre></div><blockquote>
<p>20 表示占 20 bits</p>
</blockquote>
<p>最后会生成如下的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p><code>static void decode_insn32_extract_u()</code> 是由下文 Format 定义所生成的，而 <code>arg_u *a</code> 则是由 Argument Set 定义所生成的，将会在后面的部分再做说明。</p>
</blockquote>
<p>可以看到：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span></code></pre></div><ul>
<li>
<p><code>a-&gt;imm</code> 是由 <code>insn[31:12]</code> 所取得并做符号扩展，且会再调用 <code>ex_shift_12()</code> 来 <code>左移 12 个 bits</code>。</p>
<ul>
<li>
<p>P.S. RISC-V 的 <code>ex_shift_12()</code> 是通过定义在<code>./target/riscv/translate.c</code> 中 <code>EX_SH</code> 这个 macro 所展开的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define EX_SH(amount) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    static int ex_shift_##amount(DisasContext *ctx, int imm) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">        return imm &lt;&lt; amount;                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p><code>a-&gt;rd</code> 是由 <code>insn[11:7]</code> 所取得。</p>
</li>
</ul>
<p>此外，在 <code>Decodetree</code> 的 spec 中也有提到，我们可以通过只定义 <code>!function</code> 来直接调用该 function。在这种情况下，只有 <code>DisasContext</code> 会被传入该 function。</p>
<p>如 ARM Thumb <code>./target/arm/t16.decode</code> 就有定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Set S if the instruction is outside of an IT block.
</span></span><span class="line"><span class="cl">%s               !function=t16_setflags
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">disas_t16_extract_addsub_2i</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_s_rri_rot</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rn</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">s</span> <span class="o">=</span> <span class="nf">t16_setflags</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> 
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>请注意，未包含任何 <code>unnamed_fields</code> 或 <code>!function</code> 的 <code>Field</code> 会被视为错误。</p>
<hr>
<h2 id="argument-set">Argument Set</h2>
<p><code>Argument Set</code> 定义用来保存从指令中所截取出来各字段的值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">args_def    := &#39;&amp;&#39; identifier ( args_elt )+ ( !extern )?
</span></span><span class="line"><span class="cl">args_elt    := identifier
</span></span></code></pre></div><p>其语法由 <code>&amp;</code> 开头，随后紧接着一个或多个的 <code>identifier</code> ，并可再加上可选的 <code>!extern</code> 。</p>
<ul>
<li><code>identifier</code> 可由开发者自订，如：<code>regs</code>、<code>loadstore</code>… 等。</li>
<li><code>!extern</code> 则表示是否在其他地方已经由其他的 decoder 定义过。如果有该字段，就<strong>不会</strong>再次生成对应的 <code>argument set struct</code>。</li>
</ul>
<h3 id="argument-set-示例">Argument Set 示例</h3>
<p>例1：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">&amp;ampreg3 ra rb rc
</span></span></code></pre></div><p>会生成以下的 <code>argument set struct</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">ra</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_reg3</span><span class="p">;</span>
</span></span></code></pre></div><p>例2：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">&amp;loadstore reg base offset
</span></span></code></pre></div><p>则会生成以下的 <code>argument set struct</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">reg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_loadstore</span><span class="p">;</span>
</span></span></code></pre></div><p>因此，以刚刚的 RISC-V <code>U-type</code> 指令为例，我们需要从指令中截取 <code>imm</code> 及 <code>rd</code> 字段的值，可以声明其 <code>argument set</code> 如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">&amp;u    imm rd
</span></span></code></pre></div><p>最后会生成以下的 <code>argument set struct</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span></code></pre></div><p>此 <code>argument set struct</code> 会被传入由 <code>Format</code> 定义所生成的 extract function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>所传入的<code>arg_u</code> 会保存从指令中截取出的 <code>imm</code> 及 <code>rd</code> 字段的值，待后续使用。</p>
<h2 id="format">Format</h2>
<p><code>Format</code> 定义了指令的格式 (如 RISC-V 中的 <code>R</code>、<code>I</code>、<code>S</code>、<code>B</code>、<code>U</code>、<code>J-type</code>)，并会生成对应的 decode function。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">fmt_def      := &#39;@&#39; identifier ( fmt_elt )+
</span></span><span class="line"><span class="cl">fmt_elt      := fixedbit_elt | field_elt | field_ref | args_ref
</span></span><span class="line"><span class="cl">fixedbit_elt := [01.-]+
</span></span><span class="line"><span class="cl">field_elt    := identifier &#39;:&#39; &#39;s&#39;? number
</span></span><span class="line"><span class="cl">field_ref    := &#39;%&#39; identifier | identifier &#39;=&#39; &#39;%&#39; identifier
</span></span><span class="line"><span class="cl">args_ref     := &#39;&amp;&#39; identifier
</span></span></code></pre></div><p>其语法由 <code>@</code> 开头，随后紧接着一个 <code>identifier</code> 及一个以上的 <code>fmt_elt</code>。</p>
<ul>
<li>
<p><code>identifier</code> 可由开发者自订，如：<code>opr</code>、<code>opi</code>… 等。</p>
</li>
<li>
<p><code>fmt_elt</code> 则可以采用以下不同的语法：</p>
<ul>
<li>
<p><code>fixedbit_elt</code> 包含一个或多个 <code>0</code>、<code>1</code>、<code>.</code>、<code>-</code>，每一个代表指令中的 1 个 bit。</p>
<ul>
<li><code>.</code> 代表该 bit 可以用 <code>0</code> 或是 <code>1</code> 来表示。</li>
<li><code>-</code> 代表该 bit 完全被忽略。</li>
</ul>
</li>
<li>
<p><code>field_elt</code> 可以用 Field 的语法来声明。</p>
<ul>
<li>Eg：<code>ra:5</code>、<code>rb:5</code>、<code>lit:8</code></li>
</ul>
</li>
<li>
<p><code>field_ref</code> 有下列两种格式 (以下范例参考上文所定义之 Field)：</p>
<ul>
<li>
<p><code>'%' identifier</code>：直接参考一个被定义过的 <code>Field</code>。</p>
<ul>
<li>
<p>如：<code>%rd</code>，会生成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p><code>identifier '=' '%' identifier</code>：直接参考一个被定义过的 <code>Field</code>，但通过第一个 <code>identifier</code> 来重命名其所对应的 <code>argument</code> 名称。此方式可以用来指定不同的 <code>argument</code> 名称来参考至同一个 <code>Field</code>。</p>
<ul>
<li>
<p>如：<code>my_rd=%rd</code>，会生成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">my_rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span> 
</span></span></code></pre></div></li>
</ul>
</li>
</ul>
</li>
<li>
<p><code>args_ref</code> 指定所传入 decode function 的 <code>Argument Set</code>。若没有指定 <code>args_ref</code> 的话，<code>Decodetree</code> 会根据 <code>field_elt</code> 或 <code>field_ref</code> 自动生成一个 <code>Argument Set</code>。此外，一个 <code>Format</code> 最多只能包含一个 <code>args_ref</code>。</p>
</li>
</ul>
</li>
</ul>
<p>当 <code>fixedbit_elt</code> 或 <code>field_ref</code> 被定义时，该 <code>Foramt</code> 的所有的 bits 都必须被定义 (可通过 <code>fixedbit_elt</code> 或 <code>.</code> 来定义各个 bits，<code>空格</code>会被忽略)。</p>
<h3 id="format-示例">Format 示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">@opi    ...... ra:5 lit:8    1 ....... rc:5
</span></span></code></pre></div><p>定义了 <code>op1</code> 这个 <code>Format</code>，其中：</p>
<ul>
<li>insn[31:26] 可为 <code>0</code> 或 <code>1</code>。</li>
<li>insn[25:21] 为 <code>ra</code>。</li>
<li>insn[20:13] 为 <code>lit</code>。</li>
<li>insn[12] 固定为 <code>1</code>。</li>
<li>insn[11:5] 可为 <code>0</code> 或 <code>1</code>。</li>
<li>insn[4:0] 为 <code>rc</code>。</li>
</ul>
<p>此 <code>Format</code> 会生成以下的 decode function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">lit</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">ra</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_decode_insn320</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_opi</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_decode_insn320</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">ra</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">lit</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rc</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>由于我们没有指定 <code>args_ref</code>，因此 <code>Decodetree</code> 根据了 <code>field_elt</code> 的定义，自动生成了 <code>arg_decode_insn320</code> 这个 <code>Argument Set</code>。</p>
<p>以 RISC-V <code>I-type</code> 指令为例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields:
</span></span><span class="line"><span class="cl">%rs1       15:5
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># immediates:
</span></span><span class="line"><span class="cl">%imm_i    20:s12
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argment sets:
</span></span><span class="line"><span class="cl">&amp;i    imm rs1 rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@i       ........ ........ ........ ........ &amp;i      imm=%imm_i     %rs1 %rd
</span></span></code></pre></div><p>定义了 <code>i</code> 这个 <code>Format</code>，其中：</p>
<ul>
<li>insn[31:20] 为 <code>imm</code>，且为 符号扩展。</li>
<li>insn[19:5] 为 <code>rs1</code>。</li>
<li>insn[11:7] 为 <code>rd</code>。</li>
</ul>
<p>此外，我们可以看到：</p>
<ul>
<li>此 <code>Format</code> 指定了 <code>Argument Set</code>：<code>&amp;i</code>。 <code>&amp;i</code> 中必须包含所有有用到的 <code>arguments</code> (也就是：<code>imm</code>、<code>rs1</code> 及 <code>rd</code>)</li>
<li><code>imm</code> 是通过重命名的方式来参考 <code>%imm_i</code> 这个 <code>Field</code>。</li>
</ul>
<p>此范例会生成以下的 decode function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rs1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32extract_i</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_i</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">12</span><span class="p">);</span> 
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs1</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>相比于第一个范例，由于这次我们有指定 <code>args_ref</code>：<code>&amp;i</code>，因此对应的 <code>arg_i</code> 会被传入 decode function。</p>
<hr>
<p>回到先前的 RISC-V <code>U-type</code> 指令，我们可以如同 <code>I-type</code> 指令定义其格式：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields:
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># immediates:
</span></span><span class="line"><span class="cl">%imm_u    12:s20                 !function=ex_shift_12
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argument sets:
</span></span><span class="line"><span class="cl">&amp;u    imm rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@u       ....................      ..... ....... &amp;u      imm=%imm_u          %rd
</span></span></code></pre></div><p>定义了 <code>u</code> 这个 <code>Format</code>，其中：</p>
<ul>
<li>insn[31:12] 为 <code>imm</code>，且为 符号扩展。</li>
<li>insn[11:7] 为 <code>rd</code>。</li>
</ul>
<p>会生成以下的 decode function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我们可以看到：</p>
<ul>
<li>此 <code>Format</code> 指定了 <code>Argument Set</code>：<code>&amp;u</code>。 <code>&amp;u</code> 中必须包含所有有用到的 <code>arguments</code> (也就是：<code>imm</code>、<code>rd</code>)</li>
<li><code>imm</code> 是通过重命名的方式来参考 <code>%imm_u</code> 这个 <code>Field</code>。</li>
</ul>
<h2 id="pattern">Pattern</h2>
<p><code>Pattern</code> 实际定义了一个指令的 decode 方式。<code>Decodetree</code> 会根据 <code>Patterns</code> 的定义，来动态产生出对应的 <code>switch-case</code> decode 判断分支。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">pat_def      := identifier ( pat_elt )+
</span></span><span class="line"><span class="cl">pat_elt      := fixedbit_elt | field_elt | field_ref | args_ref | fmt_ref | const_elt
</span></span><span class="line"><span class="cl">fmt_ref      := &#39;@&#39; identifier
</span></span><span class="line"><span class="cl">const_elt    := identifier &#39;=&#39; number
</span></span></code></pre></div><p>其语法由用户所定义的 <code>identifier</code>，随后紧接着一个以上的 <code>pat_elt</code>。</p>
<ul>
<li>
<p><code>identifier</code> 可由开发者自订，如：<code>addl_r</code>、<code>addli</code> … 等。</p>
</li>
<li>
<p><code>pat_elt</code> 则可以采用以下不同的语法：</p>
<ul>
<li><code>fixedbit_elt</code> 与在 <code>Format</code> 中 <code>fixedbit_elt</code> 的定义相同。</li>
<li><code>field_elt</code> 与在 <code>Format</code> 中 <code>field_elt</code> 的定义相同。</li>
<li><code>field_ref</code> 与在 <code>Format</code> 中 <code>field_ref</code> 的定义相同。</li>
<li><code>args_ref</code> 与在 <code>Format</code> 中 <code>args_ref</code> 的定义相同。</li>
<li><code>fmt_ref</code> 直接参考一个被定义过的Format。</li>
<li><code>const_elt</code> 可以直接指定某一个 <code>argument</code> 的值。</li>
</ul>
</li>
</ul>
<p>由于 <code>Pattern</code> 实际定义了一个指令的 decode 方式，因此<strong>所有的 bits</strong> 及 <strong>arguments (如果有参考 args_ref 的话)</strong>  都必须明确的被定义，如果在搭配了所有的 <code>pat_elt</code> 后还有未定义的 bits 或是 arguments 的话，<code>Decodetree</code> 便会报错。</p>
<p>此外，<code>Pattern</code> 所产生出来的 decoder，最后还会调用对应的 <code>translator function</code>。<code>translator function</code> 需开发者自行定义。</p>
<h3 id="pattern-示例">Pattern 示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">addl_i   010000 ..... ..... .... 0000000 ..... @opi
</span></span></code></pre></div><p>定义了 <code>addl_i</code> 这个指令的 <code>Pattern</code>，其中：</p>
<ul>
<li>insn[31:26] 为 <code>010000</code>。</li>
<li>insn[11:5] 为 <code>0000000</code>。</li>
<li>参考了 Format 示例中 定义的 <code>@opi</code> <code>Format</code>。</li>
<li>由于 <code>Pattern</code> 的<strong>所有 bits</strong> 都必须明确的被定义，因此 <code>@opi</code> 必须包含其余 <code>insn[25:12]</code> 及 <code>insn[4:0]</code> 的格式定义，否则 <code>Decodetree</code> 便会报错。</li>
</ul>
<p>最后 <code>addl_i</code> 的 decoder 还会调用 <code>trans_addl_i()</code> 这个 <code>translator function</code>。</p>
<p>搭配之前介绍的 Fields、Argument Sets 及 Formats，让我们再看几个完整的例子应该会更清楚 <code>Decodetree</code> 是怎产生一个指令的 decoder 的。</p>
<p>首先是 RISC-V 的 <code>lui</code> 及 <code>auipc</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/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.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 class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields:
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># immediates:
</span></span><span class="line"><span class="cl">%imm_u    12:s20                 !function=ex_shift_12
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argument sets:
</span></span><span class="line"><span class="cl">&amp;u    imm rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Formats:
</span></span><span class="line"><span class="cl">@u       ....................      ..... ....... &amp;u      imm=%imm_u          %rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Patterns
</span></span><span class="line"><span class="cl">lui      ....................       ..... 0110111 @u
</span></span><span class="line"><span class="cl">auipc    ....................       ..... 0010111 @u
</span></span></code></pre></div><p>会产生以下 <code>lui</code> 及 <code>auipc</code> 的 decoder：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn32</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_u</span> <span class="n">f_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000007f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000017</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_auipc</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000037</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_lui</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>回顾到目前为止所介绍的：</p>
<ul>
<li>
<p><code>Argument Sets</code>：<code>&amp;u</code> 这个 <code>argument set</code> 包含了 <code>imm</code> 及 <code>rd</code> 这两个 <code>arguments</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span></code></pre></div></li>
<li>
<p><code>Fields</code>： <code>imm</code> 及 <code>rd</code> 分别位在 insn[31:12] 及 insn[11:7]，且 <code>imm</code> 为 符号扩展。最后在截取出 <code>imm</code> 的值后，还会调用 <code>ex_shift_12()</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span></code></pre></div></li>
<li>
<p><code>Formats</code>：<code>@u</code> 定义了 RISC-V <code>U-type</code> 指令的格式</p>
<ul>
<li>参考了 <code>&amp;u</code> 这个 <code>Argument Set</code>，因此 decode function 会传入 <code>arg_u</code> 作为参数。</li>
<li>insn[31:12] 参考了 <code>imm_u</code> 这个 <code>Field</code> (并重命名为 <code>imm</code>)</li>
<li>insn[11:7] 参考了 <code>rd</code> 这个 <code>Field</code>。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p><code>Patterns</code>：</p>
<ul>
<li><code>lui</code> 的 <code>opcode</code> (insn[6:0]) 为 <code>0010111</code>，也就是 <code>0x17</code>，在产生出来的 <code>switch-case</code> 中可以看到其对应的 <code>case</code>。</li>
<li><code>lui</code> 的 decoder 最后调用了 <code>trans_lui()</code>，并传入 <code>DisasContext</code> 及经由 <code>decode_insn32_extract_u()</code> 所解析出来的 <code>arg_u</code>。</li>
<li><code>auipc</code> 的 <code>opcode</code> (insn[6:0]) 为 <code>0110111</code>，也就是 <code>0x37</code>，在产生出来的 <code>switch-case</code> 中可以看到其对应的 <code>case</code>。</li>
<li><code>auipc</code> 的 decoder 最后调用了 <code>trans_auipc()</code>，并传入 <code>DisasContext</code> 及经由 <code>decode_insn32_extract_u()</code> 所解析出来的 <code>arg_u</code>。</li>
<li>P.S. 这边由于 <code>Decodetree</code> 发现 <code>lui</code> 及 <code>auipc</code> 可以共用 <code>decode_insn32_extract_u()</code>，因此将其提到了 <code>switch-case</code> 之外。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn32</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_u</span> <span class="n">f_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000007f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000017</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_auipc</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000037</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_lui</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我们另外可以发现，<code>Pattern</code> + <code>Format</code> 把所有的 32-bits 都给了明确的定义：</p>
<ul>
<li><code>Pattern</code> 定义了 <code>opcode</code> (insn[6:0])。</li>
<li><code>Format</code> 参考了 <code>imm</code> (insn[31:12]) 及 <code>rd</code> (insn[11:7])。</li>
</ul>
<p>如果有任何未明确定义的 bits 的话，<code>Decodetree</code> 便会报错，例如如果我们将 <code>lui</code> 的 <code>opcode</code> 最高 2 个 bits (insn[6:5]) 由 <code>01</code> 改成 <code>..</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">lui      ....................       ..... ..10111 @u
</span></span></code></pre></div><p><code>Decodetree</code> 在解析时，便会报错：</p>
<blockquote>
<p>./insn32.decode:17: error: (‘bits left unspecified (0x00000060)’,)</p>
</blockquote>
<p><code>Decodetree</code> 提醒我们，insn[6:5] (<code>0x00000060</code>) 尚未给出明确定义，并会显示出其错误的行数。</p>
<p><code>trans_lui()</code> 和 <code>trans_auipc()</code> 被定义在 <code>target/riscv/insn_trans/trans_rvi.inc.c</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">trans_lui</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_lui</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tcg_gen_movi_tl</span><span class="p">(</span><span class="n">cpu_gpr</span><span class="p">[</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span><span class="p">],</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">trans_auipc</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_auipc</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tcg_gen_movi_tl</span><span class="p">(</span><span class="n">cpu_gpr</span><span class="p">[</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span><span class="p">],</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">+</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">pc_next</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以看到 <code>trans_*()</code> 负责实际指令的业务逻辑及产生对应的 <code>TCG codes</code>。</p>
</li>
</ul>
<p>如同先前所介绍，<code>Patterns</code> 的 <code>pat_elt</code> 也可以采用 <code>field_elt</code> 语法，如 RISC-V 的 <code>fence</code> 指令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">fence    ---- pred:4 succ:4 ----- 000 ----- 0001111
</span></span></code></pre></div><ul>
<li>insn[27:24] 为 <code>pred</code>。</li>
<li>insn[23:20] 为 <code>succ</code>。</li>
<li>insn[14:12] 固定为 <code>000</code>。</li>
<li>insn[6:0] 为 <code>opcode</code> (<code>0001111</code>)。</li>
<li>没有参考任何的 <code>Format</code>。</li>
<li>剩下的 insn[31:28]、insn[19:15]、insn[11:7] 被声明为 <code>-</code>，因此就算没有被明确定义也没有关系。</li>
</ul>
<p>所生成 <code>fence</code> 的 decoder 如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">pred</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">succ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_decode_insn320</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_decode_insn32_Fmt_0</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_decode_insn320</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">pred</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">succ</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn32</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_decode_insn320</span> <span class="n">f_decode_insn320</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">decode_insn32_extract_decode_insn32_Fmt_0</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn320</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000707f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x0000000f</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_fence</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn320</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>值得注意的是，虽然这次我们没有参考任何的 <code>Argument Set</code>，但 <code>Decodetree</code> 还是替我们生成了一个包含 <code>pred</code> 和 <code>succ</code> 的 <code>arg_decode_insn320</code> 。</p>
<p><code>trans_fence()</code> 同样是被定义在 <code>./target/riscv/insn_trans/trans_rvi.inc.c</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">trans_fence</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_fence</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="nf">tcg_gen_mb</span><span class="p">(</span><span class="n">TCG_MO_ALL</span> <span class="o">|</span> <span class="n">TCG_BAR_SC</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="pattern-groups">Pattern Groups</h2>
<p><code>Pattern Groups</code> 由一个以上的 <code>Patterns</code> 所组成，其主要差别是不同 <code>Patterns</code> 之间的 bits 可以 overlap。当同组中有多个 <code>Patterns</code> 时，会依据该组中各 <code>Pattern</code> 的声明顺序依序判断目前的指令是否符合其定义。除此之外，当符合的 <code>Pattern</code> 其 <code>trans_*()</code> 回传值为 <code>false</code> 时，也会被视为<strong>不相符</strong>，而继续判断该组中的下一个 <code>Pattern</code>。因此 <code>Pattern Groups</code> 非常适合将多个相似格式的指令给组成同一个 <code>Pattern Group</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">group    := &#39;{&#39; ( pat_def | group )+ &#39;}&#39;
</span></span></code></pre></div><p>各 <code>Pattern Group</code> 以 <code>{</code> 开头，并以 <code>}</code> 结尾，且允许 <code>nested pattern groups</code> 的存在，其他语法皆与 <code>Pattern</code> 相同。</p>
<h3 id="pattern-group-示例">Pattern Group 示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  {
</span></span><span class="line"><span class="cl">    nop   000010 ----- ----- 0000 001001 0 00000
</span></span><span class="line"><span class="cl">    copy  000010 00000 r1:5  0000 001001 0 rt:5
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">  or      000010 rt2:5 r1:5  cf:4 001001 0 rt:5
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>会产生以下的 decoder：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0xfc000fe0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mh">0x08000240</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000f000</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000001f</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">            <span class="nf">extract_decode_Fmt_0</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode0</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nf">trans_nop</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode0</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">				
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x03e00000</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">          <span class="nf">extract_decode_Fmt_1</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode1</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nf">trans_copy</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode1</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">				　
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nf">extract_decode_Fmt_2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode2</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nf">trans_or</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode2</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>当指令的值符合 <code>nop</code> 及 <code>copy</code> 这个内层 <code>Pattern Group</code> 时，会先判断该指令是否符合 <code>nop</code> 指令的定义，且 <code>trans_nop()</code> 的回传值为 <code>true</code>。否则的话，就会继续判断是否符合同组中的 <code>copy</code> 指令。若都不符，就会再判断是否符合外层 <code>Pattern Group</code> 的 <code>or</code> 指令。若仍不符，才会回传 <code>false</code> 表示 decode 失败。</p>
<p>与单纯使用 <code>Pattern</code> 最大不同的是，当一 <code>Pattern</code> 的 <code>trans_*()</code> 回传值为 <code>false</code> 时，不会直接回传 <code>false</code> (代表 decode 失败)，而是会接续着判断后续的 <code>Patterns</code> 是否相符。</p>
<p>RISC-V Compressed-Extension 中的 <code>c.ebreak</code>、<code>c.jalr</code>、及 <code>c.add</code> 指令，由于这三个指令的格式非常相似，因此非常适合使用 <code>Pattern Group</code> 来定义：</p>
<p>RISC-V spec. 中定义：</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/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.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>
<ul>
<li><code>C.EBREAK</code>指令与<code>C.ADD</code>指令共享相同的<code>opcode</code>，但是<code>rd</code>和<code>rs2</code>都为<code>zero</code>，因此也可以使用<code>CR</code>格式。</li>
<li><code>C.JALR</code>指令只有在<code>rs1≠x0</code>时才有效；当<code>rs1=x0</code>时，对应的代码点是<code>C.EBREAK</code>指令。</li>
<li><code>C.ADD</code>指令只有在<code>rs2≠x0</code>时才有效；当<code>rs2=x0</code>时，对应的代码点是<code>C.JALR</code>和<code>C.EBREAK</code>指令。具有<code>rs2̸=x0</code>和<code>rd=x0</code>的代码点是<code>HINTs</code>。</li>
</ul>
<p><code>c.ebreak</code>、<code>c.jalr</code>、<code>c.add</code> 三个指令：</p>
<ul>
<li>insn[15:13]、insn[12]、insn[1:0] 的值皆相同。</li>
<li>当 insn[11:7] 且 insn[6:2] 的值皆为 <code>0</code> (<code>rs1=0</code> 且 <code>rs2=0</code>) 时为 <code>c.ebreak</code> 指令。</li>
<li>当只有 insn[11:7] 的值为 <code>0</code> (<code>rs1=0</code> 且 <code>rs2≠0</code>) 时为 <code>c.jalr</code> 指令。</li>
<li>否则为 <code>c.add</code> 指令 (<code>rs1≠x0</code> 且 <code>rs2≠0</code>)。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">%rs2_5     2:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argument Sets
</span></span><span class="line"><span class="cl">&amp;r         rd rs1 rs2   !extern
</span></span><span class="line"><span class="cl">&amp;i         imm rs1 rd   !extern
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Formats
</span></span><span class="line"><span class="cl">@cr        ....  ..... .....  .. &amp;r      rs2=%rs2_5       rs1=%rd     %rd
</span></span><span class="line"><span class="cl">@c_jalr    ... . .....  ..... .. &amp;i      imm=0 rs1=%rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Pattern Groups
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  ebreak          100 1  00000  00000 10
</span></span><span class="line"><span class="cl">  jalr            100 1  .....  00000 10 @c_jalr rd=1  # C.JALR
</span></span><span class="line"><span class="cl">  add             100 1  .....  ..... 10 @cr
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>所生成的 decoder 如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn16_extract_c_jalr</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_i</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs1</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn16_extract_cr</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_r</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs2</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs1</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn16_extract_decode_insn16_Fmt_2</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_decode_insn162</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn16</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_decode_insn162</span> <span class="n">f_decode_insn162</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_i</span> <span class="n">f_i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_r</span> <span class="n">f_r</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000f003</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00009002</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x00000ffc</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">            <span class="nf">decode_insn16_extract_decode_insn16_Fmt_2</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn162</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nf">trans_ebreak</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn162</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000007c</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">            <span class="nf">decode_insn16_extract_c_jalr</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_i</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">u</span><span class="p">.</span><span class="n">f_i</span><span class="p">.</span><span class="n">rd</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nf">trans_jalr</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_i</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="nf">decode_insn16_extract_cr</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_r</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_add</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_r</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>当指令格式符合 <code>c.ebreak</code>、<code>c.jalr</code>、<code>c.add</code> 的 <code>Pattern Group</code> 时，会依序判断该指令是否符合 <code>c.ebreak</code>、<code>c.jalr</code>、<code>c.add</code> 的定义以及其对应的 <code>trans_*()</code>。</p>
<p>另外值得一提的是，在 <code>c_jalr</code> <code>Format</code> 和 <code>jalr</code> <code>Pattern</code> 中有分别指定其 <code>imm</code> 及 <code>rd</code> 的值为 <code>0</code>，所生成的 codes 也会分别在对应的地方将该值设为 <code>0</code> (见 codes 注解说明)。</p>
<h2 id="总结">总结</h2>
<p>以上就是 <code>Decodetree</code> 的语法说明。通过 <code>Decodetree</code>，我们不用再像以前以样写一大包的 <code>switch-case</code> 来 decode 指令。将不同类型的指令写至不同的 decode 档，不仅方便维护，阅读起来也更为容易。</p>
<hr>
<ul>
<li><code>--translate</code>：translator function 的 prefix，默认为 <code>trans</code>。一旦指定后，translator function 的 scope 就不会再是 <code>static</code>。</li>
<li><code>--decode</code>：decode function 的 prefix，默认为 <code>decode</code>，且 scope 为 <code>static</code>。一旦指定后，decode function 的 scope 就不会再是 <code>static</code>。</li>
<li><code>--static-decode</code>：如同 <code>--decode</code>，不过 decode function 的 scope 仍维持为 <code>static</code>。</li>
<li><code>-o</code> / <code>--output</code>：指定生成的 decoder <code>.c</code> 档路径。</li>
<li><code>-w</code> / <code>--insnwidth</code>：指令长度，eg：<code>32</code> or <code>16</code>，默认为 <code>32</code>。</li>
<li><code>--varinsnwidth</code>：指令为不定长度。</li>
<li><code>最后一个参数</code>为输入的 decode 档路径。</li>
</ul>
<p>运行范例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">./decodetree.py -o target/riscv/decode_insn16.inc.c --static-decode decode_insn16 \
</span></span><span class="line"><span class="cl">    -w 16 ./insn16.decode
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">static inline int32_t sextract32(uint32_t value, int start, int length){    assert(start &gt;= 0 &amp;&amp; length &gt; 0 &amp;&amp; length &lt;= 32 - start);        return ((int32_t)(value &lt;&lt; (32 - length - start))) &gt;&gt; (32 - length);}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>qBittorrent 设置单独的固态硬盘作为临时下载盘</title>
      <link>https://lifeislife.cn/posts/qbittorrent%E8%AE%BE%E7%BD%AE%E5%8D%95%E7%8B%AC%E7%9A%84%E5%9B%BA%E6%80%81%E7%A1%AC%E7%9B%98%E4%BD%9C%E4%B8%BA%E4%B8%B4%E6%97%B6%E4%B8%8B%E8%BD%BD%E7%9B%98/</link>
      <pubDate>Sat, 10 Aug 2024 20:00:13 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/qbittorrent%E8%AE%BE%E7%BD%AE%E5%8D%95%E7%8B%AC%E7%9A%84%E5%9B%BA%E6%80%81%E7%A1%AC%E7%9B%98%E4%BD%9C%E4%B8%BA%E4%B8%B4%E6%97%B6%E4%B8%8B%E8%BD%BD%E7%9B%98/</guid>
      <description>&lt;p&gt;为了避免qBittorrnet在下载文件时对机械硬盘频繁读写，可以将固态硬盘作为临时下载盘，在下载完成后再将文件移动到机械硬盘。qBittorrentk可以通过简单的设置就能实现这一目的。&lt;/p&gt;
&lt;p&gt;首先需要将固态硬盘映射到qBittorrent的容器中。我是使用docker-compose部署的qBittorrent，所以需要在&lt;code&gt;docker-compose.yml&lt;/code&gt;中添加一个volume：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;qbittorrent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;linuxserver/qbittorrent:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;qbittorrent&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;TZ=Asia/Shanghai&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/root/sharedfolder/appdata/qbittorrent:/config&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/root/sharedfolder/downloads/qbittorrent:/downloads&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/srv/download-disk/downloads/qbittorrent:/download-disk&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;/root/sharedfolder/downloads/qbittorrent:/downloads&lt;/code&gt;是原来机械硬盘的目录，我们不需要动他。&lt;code&gt;/srv/download-disk/downloads/qbittorrent:/download-disk&lt;/code&gt;是新的固态硬盘的目录，我在&lt;code&gt;download-disk&lt;/code&gt;目录下新建了和之前一样的目录结构。将它映射到容器的&lt;code&gt;/download-disk&lt;/code&gt;目录下。&lt;/p&gt;
&lt;p&gt;进入qBittorrent的web页面，&lt;strong&gt;先全选所有的任务，将任务暂停&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;点击工具-&amp;gt;选项-&amp;gt;下载，设置保存未完成的文件到固态硬盘。路径就是我们在&lt;code&gt;docker-compose.yml&lt;/code&gt;中设置的&lt;code&gt;/download-disk&lt;/code&gt;目录下的&lt;code&gt;qbincomplete&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//2024/08/10/436fd7ea3757be4efe21a7050c22db57.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/436fd7ea3757be4efe21a7050c22db57.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;重启qBittorrent容器，这样就设置好了。&lt;/p&gt;
&lt;p&gt;再次登录qBittorrent的web页面，qBittorrent会自动将之前暂停的任务都移动到固态硬盘&lt;code&gt;/download-disk&lt;/code&gt;中。你在宿主机的&lt;code&gt;/srv/download-disk/downloads/qbittorrent/qbincomplete&lt;/code&gt;目录下就能看到这些任务。你可以重新启动所有任务，当他们移动完成后就会继续下载。下载完成后就会自动移动到机械硬盘中。也就是你设置的默认下载位置，比如我的下载位置为&lt;code&gt;/root/sharedfolder/downloads/qbittorrent/qbcomplete&lt;/code&gt;。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>为了避免qBittorrnet在下载文件时对机械硬盘频繁读写，可以将固态硬盘作为临时下载盘，在下载完成后再将文件移动到机械硬盘。qBittorrentk可以通过简单的设置就能实现这一目的。</p>
<p>首先需要将固态硬盘映射到qBittorrent的容器中。我是使用docker-compose部署的qBittorrent，所以需要在<code>docker-compose.yml</code>中添加一个volume：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">qbittorrent</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">linuxserver/qbittorrent:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">qbittorrent</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">8080</span><span class="p">:</span><span class="m">8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TZ=Asia/Shanghai</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/root/sharedfolder/appdata/qbittorrent:/config</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/root/sharedfolder/downloads/qbittorrent:/downloads</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/srv/download-disk/downloads/qbittorrent:/download-disk</span><span class="w">
</span></span></span></code></pre></div><p>其中<code>/root/sharedfolder/downloads/qbittorrent:/downloads</code>是原来机械硬盘的目录，我们不需要动他。<code>/srv/download-disk/downloads/qbittorrent:/download-disk</code>是新的固态硬盘的目录，我在<code>download-disk</code>目录下新建了和之前一样的目录结构。将它映射到容器的<code>/download-disk</code>目录下。</p>
<p>进入qBittorrent的web页面，<strong>先全选所有的任务，将任务暂停</strong>。</p>
<p>点击工具-&gt;选项-&gt;下载，设置保存未完成的文件到固态硬盘。路径就是我们在<code>docker-compose.yml</code>中设置的<code>/download-disk</code>目录下的<code>qbincomplete</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//2024/08/10/436fd7ea3757be4efe21a7050c22db57.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/436fd7ea3757be4efe21a7050c22db57.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>重启qBittorrent容器，这样就设置好了。</p>
<p>再次登录qBittorrent的web页面，qBittorrent会自动将之前暂停的任务都移动到固态硬盘<code>/download-disk</code>中。你在宿主机的<code>/srv/download-disk/downloads/qbittorrent/qbincomplete</code>目录下就能看到这些任务。你可以重新启动所有任务，当他们移动完成后就会继续下载。下载完成后就会自动移动到机械硬盘中。也就是你设置的默认下载位置，比如我的下载位置为<code>/root/sharedfolder/downloads/qbittorrent/qbcomplete</code>。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Linux挂载新硬盘</title>
      <link>https://lifeislife.cn/posts/linux%E6%8C%82%E8%BD%BD%E6%96%B0%E7%A1%AC%E7%9B%98/</link>
      <pubDate>Sat, 10 Aug 2024 15:42:13 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/linux%E6%8C%82%E8%BD%BD%E6%96%B0%E7%A1%AC%E7%9B%98/</guid>
      <description>&lt;p&gt;用qBittorrent下载文件有些资源上传率很低，下载很慢，就会对机械硬盘频繁读写，这样会导致硬盘寿命缩短。虽然组了RAID5，但是还是希望硬盘能活得久一点吧。所以买了一块新固态专门用来下载文件，作为临时存放，等文件下载完了再整块拷贝到RAID5里。OMV本来挂载新硬盘很简单的，UI页面点点就可以了，不知道是不是因为我这块硬盘是通过PCIe转接卡接在主板上的，挂载一直报错。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OMV&lt;span class=&#34;se&#34;&gt;\E&lt;/span&gt;xecException: Failed to execute &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin; export LANG=C.UTF-8; omv-salt deploy run --no-color fstab 2&amp;gt;&amp;amp;1&amp;#39;&lt;/span&gt; with &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; code &lt;span class=&#34;s1&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;: /usr/lib/python3/dist-packages/requests/__init__.py:91: RequestsDependencyWarning: urllib3 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.26.18&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; or chardet &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;3.0.4&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; doesn&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;t match a supported version!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  RequestsDependencyWarning&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;网上找了一圈也没找到解决办法，只好自己手动挂载了。&lt;/p&gt;
&lt;p&gt;首先查看新硬盘的设备名：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lsblk
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvme0n1     259:0    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; 931.5G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用&lt;code&gt;fdisk&lt;/code&gt;命令对硬盘进行分区：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fdisk /dev/nvme0n1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Welcome to fdisk &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;util-linux 2.39.1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Changes will remain in memory only, &lt;span class=&#34;k&#34;&gt;until&lt;/span&gt; you decide to write them.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Be careful before using the write command.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入n表示新建分区&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Command &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;m &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: n
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   p   primary &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; primary, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; extended, &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; free&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   e   extended &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;container &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; logical partitions&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入p表示主分区&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Select &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;default p&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: p
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 分区号直接回车使用默认的，新硬盘应该为1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition number &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2-128, default 2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入两次回车使用默认的，从第一个扇区开始，到最后一个扇区结束，即整个硬盘&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 我因为已经分区完了，所以找了以前的笔记，所以扇区号和实际的大小，你们只需要两次回车就行了&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;First sector &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1001472-452984798, default 1001472&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Last sector, +/-sectors or +/-size&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;K,M,G,T,P&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1001472-452984798, default 452982783&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入w保存退出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Command &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;m &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: w
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The partition table has been altered.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Calling ioctl&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; to re-read partition table.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Syncing disks.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 分区完成了&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;格式化分区：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkfs.ext4 /dev/nvme0n1p1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mke2fs 1.44.5 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;15-Dec-2018&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Creating filesystem with &lt;span class=&#34;m&#34;&gt;244189696&lt;/span&gt; 4k blocks and &lt;span class=&#34;m&#34;&gt;61054976&lt;/span&gt; inodes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Filesystem UUID: 1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Superblock backups stored on blocks: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    102400000, &lt;span class=&#34;m&#34;&gt;214990848&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建挂载点：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在NAS上创建一个目录，用来挂载新硬盘，我的硬盘都挂载在了srv目录下，所以这里也挂载在srv下&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /srv/download-disk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;挂载硬盘：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount /dev/nvme0n1p1 /srv/download-disk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看挂载情况：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ df -h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Filesystem      Size  Used Avail Use% Mounted on
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;udev            7.6G     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  7.6G   0% /dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tmpfs           1.6G   13M  1.5G   1% /run
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda1       116G   97G   14G  88% /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tmpfs           7.6G     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  7.6G   0% /dev/shm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tmpfs           5.0M     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  5.0M   0% /run/lock
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tmpfs           7.6G     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  7.6G   0% /sys/fs/cgroup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/nvme0n1p1  916G   28K  870G   1% /srv/download-disk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为文件系统打上标签，为了能在NAS中能方便区分硬盘，我就用了硬盘的品牌和容量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;e2label /dev/nvme0n1p1 Kingston1tb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看标签：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ blkid
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;设置自动挂载，编辑&lt;code&gt;/etc/fstab&lt;/code&gt;文件，每次重启后都会自动挂载：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/dev/nvme0n1p1 /srv/download-disk ext4 defaults 0 0&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/fstab
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>用qBittorrent下载文件有些资源上传率很低，下载很慢，就会对机械硬盘频繁读写，这样会导致硬盘寿命缩短。虽然组了RAID5，但是还是希望硬盘能活得久一点吧。所以买了一块新固态专门用来下载文件，作为临时存放，等文件下载完了再整块拷贝到RAID5里。OMV本来挂载新硬盘很简单的，UI页面点点就可以了，不知道是不是因为我这块硬盘是通过PCIe转接卡接在主板上的，挂载一直报错。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">OMV<span class="se">\E</span>xecException: Failed to execute <span class="nb">command</span> <span class="s1">&#39;export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin; export LANG=C.UTF-8; omv-salt deploy run --no-color fstab 2&gt;&amp;1&#39;</span> with <span class="nb">exit</span> code <span class="s1">&#39;1&#39;</span>: /usr/lib/python3/dist-packages/requests/__init__.py:91: RequestsDependencyWarning: urllib3 <span class="o">(</span>1.26.18<span class="o">)</span> or chardet <span class="o">(</span>3.0.4<span class="o">)</span> doesn<span class="err">&#39;</span>t match a supported version!
</span></span><span class="line"><span class="cl">  RequestsDependencyWarning<span class="o">)</span>
</span></span></code></pre></div><p>网上找了一圈也没找到解决办法，只好自己手动挂载了。</p>
<p>首先查看新硬盘的设备名：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">lsblk
</span></span><span class="line"><span class="cl">NAME        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
</span></span><span class="line"><span class="cl">nvme0n1     259:0    <span class="m">0</span> 931.5G  <span class="m">0</span> disk  
</span></span></code></pre></div><p>使用<code>fdisk</code>命令对硬盘进行分区：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">fdisk /dev/nvme0n1
</span></span><span class="line"><span class="cl">Welcome to fdisk <span class="o">(</span>util-linux 2.39.1<span class="o">)</span>.
</span></span><span class="line"><span class="cl">Changes will remain in memory only, <span class="k">until</span> you decide to write them.
</span></span><span class="line"><span class="cl">Be careful before using the write command.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 输入n表示新建分区</span>
</span></span><span class="line"><span class="cl">Command <span class="o">(</span>m <span class="k">for</span> <span class="nb">help</span><span class="o">)</span>: n
</span></span><span class="line"><span class="cl">Partition <span class="nb">type</span>
</span></span><span class="line"><span class="cl">   p   primary <span class="o">(</span><span class="m">2</span> primary, <span class="m">0</span> extended, <span class="m">2</span> free<span class="o">)</span>
</span></span><span class="line"><span class="cl">   e   extended <span class="o">(</span>container <span class="k">for</span> logical partitions<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 输入p表示主分区</span>
</span></span><span class="line"><span class="cl">Select <span class="o">(</span>default p<span class="o">)</span>: p
</span></span><span class="line"><span class="cl"><span class="c1"># 分区号直接回车使用默认的，新硬盘应该为1</span>
</span></span><span class="line"><span class="cl">Partition number <span class="o">(</span>2-128, default 2<span class="o">)</span>:
</span></span><span class="line"><span class="cl"><span class="c1"># 输入两次回车使用默认的，从第一个扇区开始，到最后一个扇区结束，即整个硬盘</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 我因为已经分区完了，所以找了以前的笔记，所以扇区号和实际的大小，你们只需要两次回车就行了</span>
</span></span><span class="line"><span class="cl">First sector <span class="o">(</span>1001472-452984798, default 1001472<span class="o">)</span>:
</span></span><span class="line"><span class="cl">Last sector, +/-sectors or +/-size<span class="o">{</span>K,M,G,T,P<span class="o">}</span> <span class="o">(</span>1001472-452984798, default 452982783<span class="o">)</span>:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 输入w保存退出</span>
</span></span><span class="line"><span class="cl">Command <span class="o">(</span>m <span class="k">for</span> <span class="nb">help</span><span class="o">)</span>: w
</span></span><span class="line"><span class="cl">The partition table has been altered.
</span></span><span class="line"><span class="cl">Calling ioctl<span class="o">()</span> to re-read partition table.
</span></span><span class="line"><span class="cl">Syncing disks.
</span></span><span class="line"><span class="cl"><span class="c1"># 分区完成了</span>
</span></span></code></pre></div><p>格式化分区：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkfs.ext4 /dev/nvme0n1p1
</span></span><span class="line"><span class="cl">mke2fs 1.44.5 <span class="o">(</span>15-Dec-2018<span class="o">)</span>
</span></span><span class="line"><span class="cl">Creating filesystem with <span class="m">244189696</span> 4k blocks and <span class="m">61054976</span> inodes
</span></span><span class="line"><span class="cl">Filesystem UUID: 1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b
</span></span><span class="line"><span class="cl">Superblock backups stored on blocks: 
</span></span><span class="line"><span class="cl">    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
</span></span><span class="line"><span class="cl">    4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 
</span></span><span class="line"><span class="cl">    102400000, <span class="m">214990848</span>
</span></span></code></pre></div><p>创建挂载点：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 在NAS上创建一个目录，用来挂载新硬盘，我的硬盘都挂载在了srv目录下，所以这里也挂载在srv下</span>
</span></span><span class="line"><span class="cl">mkdir /srv/download-disk
</span></span></code></pre></div><p>挂载硬盘：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mount /dev/nvme0n1p1 /srv/download-disk
</span></span></code></pre></div><p>查看挂载情况：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ df -h
</span></span><span class="line"><span class="cl">Filesystem      Size  Used Avail Use% Mounted on
</span></span><span class="line"><span class="cl">udev            7.6G     <span class="m">0</span>  7.6G   0% /dev
</span></span><span class="line"><span class="cl">tmpfs           1.6G   13M  1.5G   1% /run
</span></span><span class="line"><span class="cl">/dev/sda1       116G   97G   14G  88% /
</span></span><span class="line"><span class="cl">tmpfs           7.6G     <span class="m">0</span>  7.6G   0% /dev/shm
</span></span><span class="line"><span class="cl">tmpfs           5.0M     <span class="m">0</span>  5.0M   0% /run/lock
</span></span><span class="line"><span class="cl">tmpfs           7.6G     <span class="m">0</span>  7.6G   0% /sys/fs/cgroup
</span></span><span class="line"><span class="cl">/dev/nvme0n1p1  916G   28K  870G   1% /srv/download-disk
</span></span></code></pre></div><p>为文件系统打上标签，为了能在NAS中能方便区分硬盘，我就用了硬盘的品牌和容量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">e2label /dev/nvme0n1p1 Kingston1tb
</span></span></code></pre></div><p>查看标签：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ blkid
</span></span></code></pre></div><p>设置自动挂载，编辑<code>/etc/fstab</code>文件，每次重启后都会自动挂载：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;/dev/nvme0n1p1 /srv/download-disk ext4 defaults 0 0&#34;</span> &gt;&gt; /etc/fstab
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>SCP-firmware 源码分析 - 源码编译以及模块初始化流程</title>
      <link>https://lifeislife.cn/posts/scp-firmware%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E4%BB%A5%E5%8F%8A%E6%A8%A1%E5%9D%97%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B/</link>
      <pubDate>Sat, 10 Aug 2024 15:42:13 +0800</pubDate>
      <guid>https://lifeislife.cn/posts/scp-firmware%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E4%BB%A5%E5%8F%8A%E6%A8%A1%E5%9D%97%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B/</guid>
      <description>&lt;h2 id=&#34;编译源码&#34;&gt;编译源码&lt;/h2&gt;
&lt;h3 id=&#34;下载配置工具链&#34;&gt;下载配置工具链&lt;/h3&gt;
&lt;p&gt;访问&lt;a href=&#34;https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads&#34;&gt;ARM 官网下载工具链&lt;/a&gt;下载自己系统的版本的工具链。如果你也是 x86_64 的 Linux 系统，直接点击&lt;a href=&#34;https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz&#34;&gt;该链接下载 arm-gnu-toolchain-13.3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;解压工具链：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;xz -d arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xvf arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将工具链路径添加到环境变量中。&lt;/p&gt;
&lt;h3 id=&#34;编译源码-1&#34;&gt;编译源码&lt;/h3&gt;
&lt;p&gt;源码使用 CMake 进行编译，CMake 版本需要 3.18.4 以上。如果你是 Ubuntu 20.04，直接安装即可。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install cmake
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你是 Ubuntu 18.04，需要升级 CMAKE。可以通过下面的方式下载源码编译安装 CMAKE。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://github.com/Kitware/CMake/releases/download/v3.22.0/cmake-3.22.0-linux-x86_64.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -zxvf cmake-3.22.0-linux-x86_64.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv cmake-3.22.0-linux-x86_64 /usr/local/cmake
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/local/cmake/bin:&lt;span class=&#34;nv&#34;&gt;$PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;源码编译：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -f Makefile.cmake &lt;span class=&#34;nv&#34;&gt;PRODUCT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;totalcompute/tc2 &lt;span class=&#34;nv&#34;&gt;MODE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;debug firmware-scp_ramfw
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;源码编译成功后，会在&lt;code&gt;build/tc2/GNU/debug/firmware-scp_ramfw/bin&lt;/code&gt;目录下生成二进制文件。具体编译参数，可以参考源码根目录下的&lt;code&gt;user_guide&lt;/code&gt;文档。&lt;/p&gt;
&lt;h2 id=&#34;初始化流程分析&#34;&gt;初始化流程分析&lt;/h2&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//2024/08/15/9489caa086aadd2299d862a9267ff947.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/15/9489caa086aadd2299d862a9267ff947.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;fwk_module_init-初始时module_config_table-是在什么时候被初始化的&#34;&gt;fwk_module_init 初始时，module_config_table 是在什么时候被初始化的？&lt;/h3&gt;
&lt;p&gt;module_config_table 无法在源码中直接搜索到，因为它是在编译过程中生成的，当你编译一次后，就会在 output/build/mpw/GNU/debug/firmware-scp_romfw/framework/src/fwk_module_list.c 找到这个变量。具体它是如何生成的，注意通过 &lt;code&gt;framework/CMakeLists.txt&lt;/code&gt; 文件中的这段代码完成，我们逐行分析这段代码。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;LENGTH&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_IDX_MAX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;获取模块列表的长度，保存在 &lt;code&gt;SCP_MODULE_IDX_MAX&lt;/code&gt; 变量中。在每个 &lt;code&gt;Firmware.cmake&lt;/code&gt; 文件中，都会有一个 &lt;code&gt;SCP_MODULES&lt;/code&gt; 变量，这个变量保存了该产品所包含的模块。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# product/juno/scp_romfw/Firmware.cmake
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;uart&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当前我们加入了两个模块，所以 &lt;code&gt;SCP_MODULE_IDX_MAX&lt;/code&gt; 的值为 2。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;foreach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;RANGE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;SCP_MODULE_IDX_MAX&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;EQUAL&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_IDX_MAX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_IDX_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;    FWK_MODULE_IDX_COUNT = ${idx},\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;endif&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;遍历模块列表，生成模块索引的定义，如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_idx&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_COUNT&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;GET&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将 &lt;code&gt;SCP_MODULES&lt;/code&gt; 列表中索引为 &lt;code&gt;idx&lt;/code&gt; 的元素赋值给变量 &lt;code&gt;SCP_MODULE&lt;/code&gt;，以便在后续的代码中使用。当前 &lt;code&gt;idx&lt;/code&gt; 为 0，所以 &lt;code&gt;SCP_MODULE&lt;/code&gt; 为 &lt;code&gt;test&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;MAKE_C_IDENTIFIER&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;SCP_MODULE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;TOUPPER&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;SCP_MODULE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_UPPER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将 &lt;code&gt;SCP_MODULE&lt;/code&gt; 转换为 C 标识符。将 &lt;code&gt;SCP_MODULE&lt;/code&gt; 转为大写并保存在 &lt;code&gt;SCP_MODULE_UPPER&lt;/code&gt; 中，即 &lt;code&gt;SCP_MODULE_UPPER=&amp;quot;TEST&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_IDX_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;    FWK_MODULE_IDX_${SCP_MODULE_UPPER} = ${idx},\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_ID_INIT_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;#define FWK_MODULE_ID_${SCP_MODULE_UPPER}_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_${SCP_MODULE_UPPER})\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_ID_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;#define FWK_MODULE_ID_${SCP_MODULE_UPPER} FWK_ID_MODULE(FWK_MODULE_IDX_${SCP_MODULE_UPPER})\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_ID_CONST_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;static const fwk_id_t fwk_module_id_${SCP_MODULE} = FWK_MODULE_ID_${SCP_MODULE_UPPER}_INIT;\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_EXTERN_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;extern const struct fwk_module module_${SCP_MODULE};\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_EXTERN_CONFIG_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;extern const struct fwk_module_config config_${SCP_MODULE};\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;    &amp;amp;module_${SCP_MODULE},\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULE_CONFIG_GEN&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;    &amp;amp;config_${SCP_MODULE},\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;进行一些宏替换，替换后的结果如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_IDX_GEN: &amp;#34;   FWK_MODULE_IDX_TEST = 0,&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_ID_INIT_GEN: &amp;#34;#define FWK_MODULE_ID_TEST_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_ID_GEN: &amp;#34;#define FWK_MODULE_ID_TEST FWK_ID_MODULE(FWK_MODULE_IDX_TEST)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_ID_CONST_GEN: &amp;#34;static const fwk_id_t fwk_module_id_test = FWK_MODULE_ID_TEST_INIT;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_EXTERN_GEN: &amp;#34;extern const struct fwk_module module_test;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_EXTERN_CONFIG_GEN: &amp;#34;extern const struct fwk_module_config config_test;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_GEN: &amp;#34;&amp;amp;module_test,&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SCP_MODULE_CONFIG_GEN: &amp;#34;&amp;amp;config_test,&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;target_compile_definitions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;framework&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                               &lt;span class=&#34;s&#34;&gt;PUBLIC&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;BUILD_HAS_MOD_${SCP_MODULE_UPPER}=1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将 &lt;code&gt;BUILD_HAS_MOD_TEST&lt;/code&gt; 定义为 1，以便在后续的代码中使用。&lt;/p&gt;
&lt;p&gt;在源码文件中有两个模板文件，分别为 &lt;code&gt;fwk_module_list.c.in&lt;/code&gt; 和 &lt;code&gt;fwk_module_idx.h.in&lt;/code&gt;，这两个文件中包含了一些宏定义，这些宏定义在编译过程中会被替换为上面生成的宏定义。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;stddef.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_EXTERN_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_COUNT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_EXTERN_CONFIG_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_config_table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_COUNT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_CONFIG_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;fwk_id.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_ID_INIT_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_ID_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_idx&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_IDX_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SCP_MODULE_ID_CONST_GEN&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在编译过程中就会生成下面两个文件，当使用&lt;code&gt;module_config_table&lt;/code&gt;时，就会引用这两个文件，找到对应的配置。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// build/mpw/GNU/debug/firmware-scp_romfw/framework/include/fwk_module_idx.h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FWK_MODULE_ID_TEST_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FWK_MODULE_ID_UART_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_UART)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FWK_MODULE_ID_TEST FWK_ID_MODULE(FWK_MODULE_IDX_TEST)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define FWK_MODULE_ID_UART FWK_ID_MODULE(FWK_MODULE_IDX_UART)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_idx&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_TEST&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_UART&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_COUNT&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_id_test&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_MODULE_ID_TEST_INIT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;fwk_id_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_id_uart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FWK_MODULE_ID_UART_INIT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// build/mpw/GNU/debug/firmware-scp_romfw/framework/src/fwk_module_list.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module_test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module_uart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_COUNT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_uart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_config&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;config_test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_config&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;config_uart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fwk_module_config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_config_table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FWK_MODULE_IDX_COUNT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config_test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config_uart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;如何确定各个模块在初始化过程中的执行顺序&#34;&gt;如何确定各个模块在初始化过程中的执行顺序？&lt;/h3&gt;
&lt;p&gt;在每个产品目录 mscp/product/juno/scp_romfw/Firmware.cmake 中，都会有一个 Firmware.cmake 文件，这个文件中会包含该产品所包含的模块：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;juno-ppu&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;juno-rom&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;gtimer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sds&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;bootloader&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;APPEND&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCP_MODULES&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;juno-soc-clock&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编译后会在 output 文件夹中生成&lt;code&gt;fwk_module_idx.h&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/14-48-59-f81a00af1d7e54bd428cf18f77a3465e-20240729144858-cdaa85.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-48-59-f81a00af1d7e54bd428cf18f77a3465e-20240729144858-cdaa85.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;
</description>
      <content:encoded><![CDATA[<h2 id="编译源码">编译源码</h2>
<h3 id="下载配置工具链">下载配置工具链</h3>
<p>访问<a href="https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads">ARM 官网下载工具链</a>下载自己系统的版本的工具链。如果你也是 x86_64 的 Linux 系统，直接点击<a href="https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz">该链接下载 arm-gnu-toolchain-13.3</a></p>
<p>解压工具链：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">xz -d arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
</span></span><span class="line"><span class="cl">tar -xvf arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar
</span></span></code></pre></div><p>将工具链路径添加到环境变量中。</p>
<h3 id="编译源码-1">编译源码</h3>
<p>源码使用 CMake 进行编译，CMake 版本需要 3.18.4 以上。如果你是 Ubuntu 20.04，直接安装即可。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt install cmake
</span></span></code></pre></div><p>如果你是 Ubuntu 18.04，需要升级 CMAKE。可以通过下面的方式下载源码编译安装 CMAKE。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">wget https://github.com/Kitware/CMake/releases/download/v3.22.0/cmake-3.22.0-linux-x86_64.tar.gz
</span></span><span class="line"><span class="cl">tar -zxvf cmake-3.22.0-linux-x86_64.tar.gz
</span></span><span class="line"><span class="cl">mv cmake-3.22.0-linux-x86_64 /usr/local/cmake
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span>/usr/local/cmake/bin:<span class="nv">$PATH</span>
</span></span></code></pre></div><p>源码编译：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">make -f Makefile.cmake <span class="nv">PRODUCT</span><span class="o">=</span>totalcompute/tc2 <span class="nv">MODE</span><span class="o">=</span>debug firmware-scp_ramfw
</span></span></code></pre></div><p>源码编译成功后，会在<code>build/tc2/GNU/debug/firmware-scp_ramfw/bin</code>目录下生成二进制文件。具体编译参数，可以参考源码根目录下的<code>user_guide</code>文档。</p>
<h2 id="初始化流程分析">初始化流程分析</h2>
<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//2024/08/15/9489caa086aadd2299d862a9267ff947.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/15/9489caa086aadd2299d862a9267ff947.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="fwk_module_init-初始时module_config_table-是在什么时候被初始化的">fwk_module_init 初始时，module_config_table 是在什么时候被初始化的？</h3>
<p>module_config_table 无法在源码中直接搜索到，因为它是在编译过程中生成的，当你编译一次后，就会在 output/build/mpw/GNU/debug/firmware-scp_romfw/framework/src/fwk_module_list.c 找到这个变量。具体它是如何生成的，注意通过 <code>framework/CMakeLists.txt</code> 文件中的这段代码完成，我们逐行分析这段代码。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">LENGTH</span> <span class="s">SCP_MODULES</span> <span class="s">SCP_MODULE_IDX_MAX</span><span class="p">)</span><span class="err">
</span></span></span></code></pre></div><p>获取模块列表的长度，保存在 <code>SCP_MODULE_IDX_MAX</code> 变量中。在每个 <code>Firmware.cmake</code> 文件中，都会有一个 <code>SCP_MODULES</code> 变量，这个变量保存了该产品所包含的模块。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl"><span class="c"># product/juno/scp_romfw/Firmware.cmake
</span></span></span><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;test&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;uart&#34;</span><span class="p">)</span><span class="err">
</span></span></span></code></pre></div><p>当前我们加入了两个模块，所以 <code>SCP_MODULE_IDX_MAX</code> 的值为 2。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl"><span class="nb">foreach</span><span class="p">(</span><span class="s">idx</span> <span class="s">RANGE</span> <span class="o">${</span><span class="nv">SCP_MODULE_IDX_MAX</span><span class="o">}</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">if</span><span class="p">(</span><span class="s">idx</span> <span class="s">EQUAL</span> <span class="s">SCP_MODULE_IDX_MAX</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">        <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_IDX_GEN</span> <span class="s2">&#34;    FWK_MODULE_IDX_COUNT = ${idx},\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl">        <span class="nb">break</span><span class="p">()</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">endif</span><span class="p">()</span><span class="err">
</span></span></span></code></pre></div><p>遍历模块列表，生成模块索引的定义，如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">enum</span> <span class="n">fwk_module_idx</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FWK_MODULE_IDX_COUNT</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl">    <span class="nb">list</span><span class="p">(</span><span class="s">GET</span> <span class="s">SCP_MODULES</span> <span class="o">${</span><span class="nv">idx</span><span class="o">}</span> <span class="s">SCP_MODULE</span><span class="p">)</span><span class="err">
</span></span></span></code></pre></div><p>将 <code>SCP_MODULES</code> 列表中索引为 <code>idx</code> 的元素赋值给变量 <code>SCP_MODULE</code>，以便在后续的代码中使用。当前 <code>idx</code> 为 0，所以 <code>SCP_MODULE</code> 为 <code>test</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">MAKE_C_IDENTIFIER</span> <span class="o">${</span><span class="nv">SCP_MODULE</span><span class="o">}</span> <span class="s">SCP_MODULE</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">TOUPPER</span> <span class="o">${</span><span class="nv">SCP_MODULE</span><span class="o">}</span> <span class="s">SCP_MODULE_UPPER</span><span class="p">)</span><span class="err">
</span></span></span></code></pre></div><p>将 <code>SCP_MODULE</code> 转换为 C 标识符。将 <code>SCP_MODULE</code> 转为大写并保存在 <code>SCP_MODULE_UPPER</code> 中，即 <code>SCP_MODULE_UPPER=&quot;TEST&quot;</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_IDX_GEN</span> <span class="s2">&#34;    FWK_MODULE_IDX_${SCP_MODULE_UPPER} = ${idx},\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_ID_INIT_GEN</span> <span class="s2">&#34;#define FWK_MODULE_ID_${SCP_MODULE_UPPER}_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_${SCP_MODULE_UPPER})\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_ID_GEN</span> <span class="s2">&#34;#define FWK_MODULE_ID_${SCP_MODULE_UPPER} FWK_ID_MODULE(FWK_MODULE_IDX_${SCP_MODULE_UPPER})\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_ID_CONST_GEN</span> <span class="s2">&#34;static const fwk_id_t fwk_module_id_${SCP_MODULE} = FWK_MODULE_ID_${SCP_MODULE_UPPER}_INIT;\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_EXTERN_GEN</span> <span class="s2">&#34;extern const struct fwk_module module_${SCP_MODULE};\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_EXTERN_CONFIG_GEN</span> <span class="s2">&#34;extern const struct fwk_module_config config_${SCP_MODULE};\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_GEN</span> <span class="s2">&#34;    &amp;module_${SCP_MODULE},\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl">    <span class="nb">string</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULE_CONFIG_GEN</span> <span class="s2">&#34;    &amp;config_${SCP_MODULE},\n&#34;</span><span class="p">)</span><span class="err">
</span></span></span></code></pre></div><p>进行一些宏替换，替换后的结果如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">SCP_MODULE_IDX_GEN: &#34;   FWK_MODULE_IDX_TEST = 0,&#34;
</span></span><span class="line"><span class="cl">SCP_MODULE_ID_INIT_GEN: &#34;#define FWK_MODULE_ID_TEST_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST)&#34;
</span></span><span class="line"><span class="cl">SCP_MODULE_ID_GEN: &#34;#define FWK_MODULE_ID_TEST FWK_ID_MODULE(FWK_MODULE_IDX_TEST)&#34;
</span></span><span class="line"><span class="cl">SCP_MODULE_ID_CONST_GEN: &#34;static const fwk_id_t fwk_module_id_test = FWK_MODULE_ID_TEST_INIT;&#34;
</span></span><span class="line"><span class="cl">SCP_MODULE_EXTERN_GEN: &#34;extern const struct fwk_module module_test;&#34;
</span></span><span class="line"><span class="cl">SCP_MODULE_EXTERN_CONFIG_GEN: &#34;extern const struct fwk_module_config config_test;&#34;
</span></span><span class="line"><span class="cl">SCP_MODULE_GEN: &#34;&amp;module_test,&#34;
</span></span><span class="line"><span class="cl">SCP_MODULE_CONFIG_GEN: &#34;&amp;config_test,&#34;
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl">    <span class="nb">target_compile_definitions</span><span class="p">(</span><span class="s">framework</span>
</span></span><span class="line"><span class="cl">                               <span class="s">PUBLIC</span> <span class="s2">&#34;BUILD_HAS_MOD_${SCP_MODULE_UPPER}=1&#34;</span><span class="p">)</span><span class="err">
</span></span></span></code></pre></div><p>将 <code>BUILD_HAS_MOD_TEST</code> 定义为 1，以便在后续的代码中使用。</p>
<p>在源码文件中有两个模板文件，分别为 <code>fwk_module_list.c.in</code> 和 <code>fwk_module_idx.h.in</code>，这两个文件中包含了一些宏定义，这些宏定义在编译过程中会被替换为上面生成的宏定义。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stddef.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_EXTERN_GEN</span><span class="err">@</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module</span> <span class="o">*</span><span class="n">module_table</span><span class="p">[</span><span class="n">FWK_MODULE_IDX_COUNT</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_GEN</span><span class="err">@</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_EXTERN_CONFIG_GEN</span><span class="err">@</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module_config</span> <span class="o">*</span><span class="n">module_config_table</span><span class="p">[</span><span class="n">FWK_MODULE_IDX_COUNT</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_CONFIG_GEN</span><span class="err">@</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;fwk_id.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_ID_INIT_GEN</span><span class="err">@</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_ID_GEN</span><span class="err">@</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="n">fwk_module_idx</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_IDX_GEN</span><span class="err">@</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">SCP_MODULE_ID_CONST_GEN</span><span class="err">@</span>
</span></span></code></pre></div><p>在编译过程中就会生成下面两个文件，当使用<code>module_config_table</code>时，就会引用这两个文件，找到对应的配置。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// build/mpw/GNU/debug/firmware-scp_romfw/framework/include/fwk_module_idx.h
</span></span></span><span class="line"><span class="cl"><span class="cp">#define FWK_MODULE_ID_TEST_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define FWK_MODULE_ID_UART_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_UART)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define FWK_MODULE_ID_TEST FWK_ID_MODULE(FWK_MODULE_IDX_TEST)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define FWK_MODULE_ID_UART FWK_ID_MODULE(FWK_MODULE_IDX_UART)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="n">fwk_module_idx</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FWK_MODULE_IDX_TEST</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">FWK_MODULE_IDX_UART</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">FWK_MODULE_IDX_COUNT</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="kt">fwk_id_t</span> <span class="n">fwk_module_id_test</span> <span class="o">=</span> <span class="n">FWK_MODULE_ID_TEST_INIT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="kt">fwk_id_t</span> <span class="n">fwk_module_id_uart</span> <span class="o">=</span> <span class="n">FWK_MODULE_ID_UART_INIT</span><span class="p">;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// build/mpw/GNU/debug/firmware-scp_romfw/framework/src/fwk_module_list.c
</span></span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module</span> <span class="n">module_test</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module</span> <span class="n">module_uart</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module</span> <span class="o">*</span><span class="n">module_table</span><span class="p">[</span><span class="n">FWK_MODULE_IDX_COUNT</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">&amp;</span><span class="n">module_test</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="o">&amp;</span><span class="n">module_uart</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module_config</span> <span class="n">config_test</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module_config</span> <span class="n">config_uart</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="k">struct</span> <span class="n">fwk_module_config</span> <span class="o">*</span><span class="n">module_config_table</span><span class="p">[</span><span class="n">FWK_MODULE_IDX_COUNT</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">&amp;</span><span class="n">config_test</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="o">&amp;</span><span class="n">config_uart</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="如何确定各个模块在初始化过程中的执行顺序">如何确定各个模块在初始化过程中的执行顺序？</h3>
<p>在每个产品目录 mscp/product/juno/scp_romfw/Firmware.cmake 中，都会有一个 Firmware.cmake 文件，这个文件中会包含该产品所包含的模块：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;juno-ppu&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;juno-rom&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;gtimer&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;sds&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;bootloader&#34;</span><span class="p">)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="nb">list</span><span class="p">(</span><span class="s">APPEND</span> <span class="s">SCP_MODULES</span> <span class="s2">&#34;juno-soc-clock&#34;</span><span class="p">)</span><span class="err">
</span></span></span></code></pre></div><p>编译后会在 output 文件夹中生成<code>fwk_module_idx.h</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/14-48-59-f81a00af1d7e54bd428cf18f77a3465e-20240729144858-cdaa85.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-48-59-f81a00af1d7e54bd428cf18f77a3465e-20240729144858-cdaa85.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>
]]></content:encoded>
    </item>
    <item>
      <title>Hugo 主题配置</title>
      <link>https://lifeislife.cn/posts/hugo%E4%B8%BB%E9%A2%98%E9%85%8D%E7%BD%AE/</link>
      <pubDate>Fri, 09 Aug 2024 23:10:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/hugo%E4%B8%BB%E9%A2%98%E9%85%8D%E7%BD%AE/</guid>
      <description>&lt;h2 id=&#34;配置页面的菜单&#34;&gt;配置页面的菜单&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;config.yaml&lt;/code&gt;中配置&lt;code&gt;menu&lt;/code&gt;字段，如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;menu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;identifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;archives&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;归档&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/archives/&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;weight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;identifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;标签&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/tags/&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;weight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;identifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;categories&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;分类&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/categories/&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;weight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;weight&lt;/code&gt;字段表示菜单的排序，数字越小越靠前。此时页面应该会显示这三个菜单，但是归档页面是 404，还需要在创建一个&lt;code&gt;content/archives.md&lt;/code&gt;文件，内容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;title: &amp;#34;Archive&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;layout: &amp;#34;archives&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;url: &amp;#34;/archives&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;summary: &amp;#34;archives&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中最关键的就是&lt;code&gt;layout&lt;/code&gt;字段，指定了这个页面使用的模板，这里使用的是&lt;code&gt;themes\PaperMod\layouts\_default\archives.html&lt;/code&gt;模板，所以我们在&lt;code&gt;content/archives.md&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//2024/08/09/7973533a1fe44d65c94813434de6782c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/09/7973533a1fe44d65c94813434de6782c.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;h2 id=&#34;修改-layout-模板&#34;&gt;修改 layout 模板&lt;/h2&gt;
&lt;p&gt;我们可以直接修改主题中的文件，但是这样会导致主题更新时丢失我们的修改，所以我们可以将主题中的文件复制到&lt;code&gt;layouts&lt;/code&gt;目录下，然后再进行修改，这样就不会丢失我们的修改了。hugo 会优先使用&lt;code&gt;layouts&lt;/code&gt;目录下的文件。&lt;/p&gt;
&lt;h2 id=&#34;文章添加标签和分类&#34;&gt;文章添加标签和分类&lt;/h2&gt;
&lt;p&gt;在文章的头部添加&lt;code&gt;tags&lt;/code&gt;和&lt;code&gt;categories&lt;/code&gt;字段，如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ELF, C]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;C 语言入门]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;文章添加封面图&#34;&gt;文章添加封面图&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;static&lt;/code&gt;目录下创建一个&lt;code&gt;img&lt;/code&gt;目录，然后将图片&lt;code&gt;a.jpg&lt;/code&gt;放到这个目录下，然后在文章的头部添加&lt;code&gt;cover&lt;/code&gt;字段，如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;cover&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;img/a.jpg&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 设置图片的小标题&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;alt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;图片&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;caption&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;图片&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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//2024/08/09/ec0963b43aceed81451a2bd8a82f0779.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/09/ec0963b43aceed81451a2bd8a82f0779.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;h2 id=&#34;点击图片显示原图&#34;&gt;点击图片显示原图&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;config.yaml&lt;/code&gt;中添加如下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;cover&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;linkFullImages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;设置面包屑导航&#34;&gt;设置面包屑导航&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;config.yaml&lt;/code&gt;中添加如下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;showBreadCrumbs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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//2024/08/09/659f8be600c857ce5d3c7f10ce7e4d65.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/09/659f8be600c857ce5d3c7f10ce7e4d65.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;h2 id=&#34;设置主页欢迎信息&#34;&gt;设置主页欢迎信息&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;config.yaml&lt;/code&gt;中添加如下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;homeInfoParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;欢迎来到我的博客&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;这里是我的个人博客，欢迎访问。&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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//2024/08/10/5c96dd4273a7f7c1a66829ea3b610b13.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/5c96dd4273a7f7c1a66829ea3b610b13.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;h2 id=&#34;主页添加社交网站信息&#34;&gt;主页添加社交网站信息&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 主页添加社交网站图标&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;socialIcons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;GitHub&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://github.com/Dunky-Z&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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//2024/08/10/d1056513367d2698342646e970de4083.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/d1056513367d2698342646e970de4083.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;h2 id=&#34;添加搜索功能&#34;&gt;添加搜索功能&lt;/h2&gt;
&lt;p&gt;添加以下配置到 config.yml 文件（上述配置文件已有，此处不用重复添加）&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;outputs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;HTML&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;RSS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# is necessary&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 content 文件夹中创建 search.md 并添加以下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;title: &amp;#34;🔍 搜索&amp;#34; # in any language you want
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;layout: &amp;#34;search&amp;#34; # is necessary
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# url: &amp;#34;/archive&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# description: &amp;#34;Description for Search&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;summary: &amp;#34;search&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;placeholder: &amp;#34;搜索框内的默认显示&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要使得某些特定的不被搜素到，可以在博客开头添加：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;searchHidden: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;更改字体&#34;&gt;更改字体&lt;/h2&gt;
&lt;p&gt;打开&lt;a href=&#34;https://fonts.google.com/&#34;&gt;谷歌字体&lt;/a&gt;，选择自己喜欢的字体，点击 Get Font-&amp;gt;Get embed code，复制 html 和 css 两部分备用。&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//2024/08/10/7cd7a9cb8e0f2182805dfede2204588c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/7cd7a9cb8e0f2182805dfede2204588c.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;code&gt;themes\PaperMod\layouts\partials\extend_head.html&lt;/code&gt;文件复制到博客主目录下的&lt;code&gt;layout\partials\extend_head.html&lt;/code&gt;目录下，一定要保持目录结构一致，没有的目录自己创建一个。将 Html 代码粘贴到&lt;code&gt;extend_head.html&lt;/code&gt;文件中。&lt;/p&gt;
&lt;p&gt;将主题中的&lt;code&gt;themes\PaperMod\assets\css\extended\blank.css&lt;/code&gt;文件复制到博客主目录下的&lt;code&gt;assets\css\extended\blank.css&lt;/code&gt;目录下，同样需要保持目录结构一致，将 css 代码粘贴到&lt;code&gt;blank.css&lt;/code&gt;文件中。注意 Google Font 提供的是个 CSS 模板，有些具体的参数需要修改一下，比如尖括号里的内容是需要自己填写的。我的修改如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;font-family&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Noto Sans SC&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;sans-serif&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;font-optical-sizing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;font-size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;rem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;line-height&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;margin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;修改代码字体过程一样，需要注意的是，Google Font 可以选择多个字体，比如我选择了两种字体，一种&lt;code&gt;JetBrains+Mono&lt;/code&gt;用于代码，一种&lt;code&gt;Noto+Sans+SC&lt;/code&gt;用于中文，在选择第二个字体时，点击 Get embed font 默认会把&lt;code&gt;html&lt;/code&gt;代码何必在了一起，也就是下面这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;preconnect&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://fonts.googleapis.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;preconnect&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://fonts.gstatic.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;crossorigin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&amp;amp;family=Noto+Sans+SC:wght@100..900&amp;amp;display=swap&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;加入-disqus-评论&#34;&gt;加入 Disqus 评论&lt;/h2&gt;
&lt;p&gt;前往&lt;a href=&#34;https://disqus.com/&#34;&gt;Disqus 官网&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//2024/08/10/64b2119d2d42c6dbfb67423a2e672869.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/64b2119d2d42c6dbfb67423a2e672869.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;code&gt;shortname&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//2024/08/10/40345b0512027a8cc19050102b2ce0e0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/40345b0512027a8cc19050102b2ce0e0.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;code&gt;config.yaml&lt;/code&gt;中添加如下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;production&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;comments&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disqus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;lifeislife-1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;回到 Disqus 官网，点击&lt;code&gt;Create a new site&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//2024/08/10/844c3c5e65779844178c610bb16ed19b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/844c3c5e65779844178c610bb16ed19b.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;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//2024/08/10/bd2d40d6921793c8b01fe3915d62383b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/bd2d40d6921793c8b01fe3915d62383b.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;code&gt;themes\PaperMod\layouts\partials\comments.html&lt;/code&gt;文件到博客主目录下的&lt;code&gt;layouts\partials\comments.html&lt;/code&gt;目录下，然后将 Disqus 提供的代码粘贴到&lt;code&gt;comments.html&lt;/code&gt;文件中。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;disqus_thread&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;    *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;    *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables    */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;    var disqus_config = function () {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;    this.page.url = PAGE_URL;  // Replace PAGE_URL with your page&amp;#39;s canonical URL variable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;    this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page&amp;#39;s unique identifier variable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;    */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// DON&amp;#39;T EDIT BELOW THIS LINE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://lifeislife-1.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setAttribute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;data-timestamp&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;head&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;appendChild&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;})();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;noscript&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Please enable JavaScript to view the &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://disqus.com/?ref_noscript&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;comments powered by Disqus.&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;noscript&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&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//2024/08/10/6ce5fe639e0155b01d05bb11068893db.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/6ce5fe639e0155b01d05bb11068893db.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;h2 id=&#34;配置网站的-favicon&#34;&gt;配置网站的 favicon&lt;/h2&gt;
&lt;p&gt;Favicon 就是网站的小图标，比如标签栏中的图标。&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//2024/08/16/1e83fb5fd94465d1b86c22a9f549d48a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/16/1e83fb5fd94465d1b86c22a9f549d48a.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;
&lt;p&gt;准备一个喜欢的 Logo，最好为 png 格式，尺寸为 1:1，但是图片内容为圆形，圆形外面为透明的，这样才不会影响显示。&lt;/p&gt;
&lt;ol&gt;
&lt;li&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//2024/08/16/e948f8f14fbefa649d76985e655e50ec.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/16/e948f8f14fbefa649d76985e655e50ec.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开&lt;a href=&#34;https://realfavicongenerator.net/&#34;&gt;Favicon Generator&lt;/a&gt;，选择自己的图标，点击 Generate Favicon，下载生成的文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把解压的文件都放到博客主目录下的&lt;code&gt;static/assets&lt;/code&gt;目录下。比如&lt;code&gt;/static/assets/favicon.ico&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修改配置文件&lt;code&gt;config.yaml&lt;/code&gt;，添加如下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;assets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disableHLJS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;favicon&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/assets/favicon.ico&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# favicon: &amp;#34;&amp;lt;link / abs url&amp;gt;&amp;#34; &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# favicon16x16: &amp;#34;&amp;lt;link / abs url&amp;gt;&amp;#34; &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# favicon32x32: &amp;#34;&amp;lt;link / abs url&amp;gt;&amp;#34; &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# apple_touch_icon: &amp;#34;&amp;lt;link / abs url&amp;gt;&amp;#34; &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# safari_pinned_tab: &amp;#34;&amp;lt;link / abs url&amp;gt;&amp;#34; &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;路径中不需要加上&lt;code&gt;static&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;刷新页面，可以看到网站图标已经修改。&lt;/p&gt;
&lt;ol&gt;
&lt;li&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//2024/08/16/a13d568c2fad1947a18b1bfb9d07b2a2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/16/a13d568c2fad1947a18b1bfb9d07b2a2.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;点击图片查看原图&#34;&gt;点击图片查看原图&lt;/h2&gt;
&lt;p&gt;博客中有一些图片比较大，无法查看原图就看不到细节，可以通过点击图片查看原图。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;修改网站根目录下的 config.yaml 文件，添加如下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;fancybox&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主题目录下创建以下路径和文件&lt;code&gt;/layouts/_default/_markup/render-image.html&lt;/code&gt;，并添加内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; {{if .Page.Site.Params.fancybox }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;post-img-view&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;data-fancybox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;gallery&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;img&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;alt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Text }}&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Title&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ . }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主题 &lt;code&gt;layouts/partials&lt;/code&gt; 目录下的 &lt;code&gt;head.html&lt;/code&gt; 或者 &lt;code&gt;footer.html&lt;/code&gt; 中添加以下代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{if .Page.Site.Params.fancybox }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;图片居中设置显示大小&#34;&gt;图片居中，设置显示大小&lt;/h2&gt;
&lt;p&gt;修改之前&lt;code&gt;/layouts/_default/_markup/render-image.html&lt;/code&gt;中的代码，在&lt;code&gt;img&lt;/code&gt;标签上添加&lt;code&gt;style=&amp;quot;display: block; margin: 0 auto; width: 60%;&amp;quot;&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;display: block - 使图片变为块级元素，这样 margin: 0 auto 才会生效。&lt;/li&gt;
&lt;li&gt;margin: 0 auto - 将图片水平居中。&lt;/li&gt;
&lt;li&gt;width: 60% - 将图片宽度设置为原图的 60%。避免原图太大占用页面空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{ if .Page.Site.Params.fancybox }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;post-img-view&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;data-fancybox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;gallery&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;img&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;alt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Text }}&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Title&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ . }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;display: block; margin: 0 auto; width: 60%;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;根据屏幕大小自适应图片高度&#34;&gt;根据屏幕大小自适应图片高度&lt;/h2&gt;
&lt;p&gt;修改之前&lt;code&gt;/layouts/_default/_markup/render-image.html&lt;/code&gt;中的代码，加入JS脚本，根据屏幕大小自适应图片高度。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;lang&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;charset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Responsive Image&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;post-img-view&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;text-align&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;responsive-image&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;margin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    {{ if .Page.Site.Params.fancybox }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;post-img-view&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;data-fancybox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;gallery&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;img&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;responsive-image&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;alt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ .Text }}&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Title&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{{ . }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;margin: 0 auto;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEventListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;DOMContentLoaded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;images&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;.responsive-image&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;maxHeight&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;innerHeight&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;images&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;forEach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;maxHeight&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;maxHeight&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;px&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&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//2024/08/31/13939de68f90687fd63eb51a3bfc62a1.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/13939de68f90687fd63eb51a3bfc62a1.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;h2 id=&#34;加入哔哩哔哩视频&#34;&gt;加入哔哩哔哩视频&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;&amp;lt; douban src=&amp;#34;https://book.douban.com/subject/30838038/&amp;#34; &amp;gt; }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 使用的时候把去掉尖括弧外侧空格，此处是为了防止被识别生效&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h2 id="配置页面的菜单">配置页面的菜单</h2>
<p>在<code>config.yaml</code>中配置<code>menu</code>字段，如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">menu</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">main</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">identifier</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;archives&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;归档&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/archives/&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">weight</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">identifier</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;tags&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;标签&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/tags/&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">weight</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">identifier</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;categories&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;分类&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/categories/&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">weight</span><span class="p">:</span><span class="w"> </span><span class="m">4</span><span class="w">
</span></span></span></code></pre></div><p>其中<code>weight</code>字段表示菜单的排序，数字越小越靠前。此时页面应该会显示这三个菜单，但是归档页面是 404，还需要在创建一个<code>content/archives.md</code>文件，内容如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: &#34;Archive&#34;
</span></span><span class="line"><span class="cl">layout: &#34;archives&#34;
</span></span><span class="line"><span class="cl">url: &#34;/archives&#34;
</span></span><span class="line"><span class="cl">summary: &#34;archives&#34;
</span></span><span class="line"><span class="cl">---
</span></span></code></pre></div><p>其中最关键的就是<code>layout</code>字段，指定了这个页面使用的模板，这里使用的是<code>themes\PaperMod\layouts\_default\archives.html</code>模板，所以我们在<code>content/archives.md</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//2024/08/09/7973533a1fe44d65c94813434de6782c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/09/7973533a1fe44d65c94813434de6782c.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>
<h2 id="修改-layout-模板">修改 layout 模板</h2>
<p>我们可以直接修改主题中的文件，但是这样会导致主题更新时丢失我们的修改，所以我们可以将主题中的文件复制到<code>layouts</code>目录下，然后再进行修改，这样就不会丢失我们的修改了。hugo 会优先使用<code>layouts</code>目录下的文件。</p>
<h2 id="文章添加标签和分类">文章添加标签和分类</h2>
<p>在文章的头部添加<code>tags</code>和<code>categories</code>字段，如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ELF, C]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">categories</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">C 语言入门]</span><span class="w">
</span></span></span></code></pre></div><h2 id="文章添加封面图">文章添加封面图</h2>
<p>在<code>static</code>目录下创建一个<code>img</code>目录，然后将图片<code>a.jpg</code>放到这个目录下，然后在文章的头部添加<code>cover</code>字段，如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">cover</span><span class="p">:</span><span class="w"> 
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">img/a.jpg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># 设置图片的小标题</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">alt</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;图片&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">caption</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;图片&#34;</span><span class="w">
</span></span></span></code></pre></div><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//2024/08/09/ec0963b43aceed81451a2bd8a82f0779.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/09/ec0963b43aceed81451a2bd8a82f0779.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>
<h2 id="点击图片显示原图">点击图片显示原图</h2>
<p>在<code>config.yaml</code>中添加如下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cover</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">linkFullImages</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><h2 id="设置面包屑导航">设置面包屑导航</h2>
<p>在<code>config.yaml</code>中添加如下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">showBreadCrumbs</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><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//2024/08/09/659f8be600c857ce5d3c7f10ce7e4d65.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/09/659f8be600c857ce5d3c7f10ce7e4d65.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>
<h2 id="设置主页欢迎信息">设置主页欢迎信息</h2>
<p>在<code>config.yaml</code>中添加如下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">homeInfoParams</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;欢迎来到我的博客&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">content</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;这里是我的个人博客，欢迎访问。&#34;</span><span class="w">
</span></span></span></code></pre></div><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//2024/08/10/5c96dd4273a7f7c1a66829ea3b610b13.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/5c96dd4273a7f7c1a66829ea3b610b13.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>
<h2 id="主页添加社交网站信息">主页添加社交网站信息</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># 主页添加社交网站图标</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">socialIcons</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">GitHub</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://github.com/Dunky-Z&#34;</span><span class="w">
</span></span></span></code></pre></div><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//2024/08/10/d1056513367d2698342646e970de4083.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/d1056513367d2698342646e970de4083.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>
<h2 id="添加搜索功能">添加搜索功能</h2>
<p>添加以下配置到 config.yml 文件（上述配置文件已有，此处不用重复添加）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">outputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">home</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">HTML</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">RSS</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">JSON</span><span class="w"> </span><span class="c"># is necessary</span><span class="w">
</span></span></span></code></pre></div><p>在 content 文件夹中创建 search.md 并添加以下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: &#34;🔍 搜索&#34; # in any language you want
</span></span><span class="line"><span class="cl">layout: &#34;search&#34; # is necessary
</span></span><span class="line"><span class="cl"># url: &#34;/archive&#34;
</span></span><span class="line"><span class="cl"># description: &#34;Description for Search&#34;
</span></span><span class="line"><span class="cl">summary: &#34;search&#34;
</span></span><span class="line"><span class="cl">placeholder: &#34;搜索框内的默认显示&#34;
</span></span><span class="line"><span class="cl">---
</span></span></code></pre></div><p>要使得某些特定的不被搜素到，可以在博客开头添加：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">searchHidden: true
</span></span></code></pre></div><h2 id="更改字体">更改字体</h2>
<p>打开<a href="https://fonts.google.com/">谷歌字体</a>，选择自己喜欢的字体，点击 Get Font-&gt;Get embed code，复制 html 和 css 两部分备用。</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//2024/08/10/7cd7a9cb8e0f2182805dfede2204588c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/7cd7a9cb8e0f2182805dfede2204588c.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>将主题中的<code>themes\PaperMod\layouts\partials\extend_head.html</code>文件复制到博客主目录下的<code>layout\partials\extend_head.html</code>目录下，一定要保持目录结构一致，没有的目录自己创建一个。将 Html 代码粘贴到<code>extend_head.html</code>文件中。</p>
<p>将主题中的<code>themes\PaperMod\assets\css\extended\blank.css</code>文件复制到博客主目录下的<code>assets\css\extended\blank.css</code>目录下，同样需要保持目录结构一致，将 css 代码粘贴到<code>blank.css</code>文件中。注意 Google Font 提供的是个 CSS 模板，有些具体的参数需要修改一下，比如尖括号里的内容是需要自己填写的。我的修改如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">body</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="s2">&#34;Noto Sans SC&#34;</span><span class="p">,</span> <span class="kc">sans-serif</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-optical-sizing</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">1</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>修改代码字体过程一样，需要注意的是，Google Font 可以选择多个字体，比如我选择了两种字体，一种<code>JetBrains+Mono</code>用于代码，一种<code>Noto+Sans+SC</code>用于中文，在选择第二个字体时，点击 Get embed font 默认会把<code>html</code>代码何必在了一起，也就是下面这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;preconnect&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.googleapis.com&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;preconnect&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.gstatic.com&#34;</span> <span class="na">crossorigin</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&amp;family=Noto+Sans+SC:wght@100..900&amp;display=swap&#34;</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="加入-disqus-评论">加入 Disqus 评论</h2>
<p>前往<a href="https://disqus.com/">Disqus 官网</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//2024/08/10/64b2119d2d42c6dbfb67423a2e672869.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/64b2119d2d42c6dbfb67423a2e672869.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>然后将站点的<code>shortname</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//2024/08/10/40345b0512027a8cc19050102b2ce0e0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/40345b0512027a8cc19050102b2ce0e0.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>然后在<code>config.yaml</code>中添加如下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">env</span><span class="p">:</span><span class="w"> </span><span class="l">production</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">comments</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">disqus</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">shortname</span><span class="p">:</span><span class="w"> </span><span class="l">lifeislife-1</span><span class="w">
</span></span></span></code></pre></div><p>回到 Disqus 官网，点击<code>Create a new site</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//2024/08/10/844c3c5e65779844178c610bb16ed19b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/844c3c5e65779844178c610bb16ed19b.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>
<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//2024/08/10/bd2d40d6921793c8b01fe3915d62383b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/bd2d40d6921793c8b01fe3915d62383b.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>复制主题中的<code>themes\PaperMod\layouts\partials\comments.html</code>文件到博客主目录下的<code>layouts\partials\comments.html</code>目录下，然后将 Disqus 提供的代码粘贴到<code>comments.html</code>文件中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;disqus_thread&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">    *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
</span></span></span><span class="line"><span class="cl"><span class="cm">    *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables    */</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">    var disqus_config = function () {
</span></span></span><span class="line"><span class="cl"><span class="cm">    this.page.url = PAGE_URL;  // Replace PAGE_URL with your page&#39;s canonical URL variable
</span></span></span><span class="line"><span class="cl"><span class="cm">    this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page&#39;s unique identifier variable
</span></span></span><span class="line"><span class="cl"><span class="cm">    };
</span></span></span><span class="line"><span class="cl"><span class="cm">    */</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// DON&#39;T EDIT BELOW THIS LINE
</span></span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">d</span> <span class="o">=</span> <span class="nb">document</span><span class="p">,</span> <span class="nx">s</span> <span class="o">=</span> <span class="nx">d</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">&#39;script&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">s</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s1">&#39;https://lifeislife-1.disqus.com/embed.js&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">s</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">&#39;data-timestamp&#39;</span><span class="p">,</span> <span class="o">+</span><span class="k">new</span> <span class="nb">Date</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nx">d</span><span class="p">.</span><span class="nx">head</span> <span class="o">||</span> <span class="nx">d</span><span class="p">.</span><span class="nx">body</span><span class="p">).</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">s</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">})();</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">noscript</span><span class="p">&gt;</span>Please enable JavaScript to view the <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://disqus.com/?ref_noscript&#34;</span><span class="p">&gt;</span>comments powered by Disqus.<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>配置完成，刷新页面可以在博客底部看到评论框。</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//2024/08/10/6ce5fe639e0155b01d05bb11068893db.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/10/6ce5fe639e0155b01d05bb11068893db.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>
<h2 id="配置网站的-favicon">配置网站的 favicon</h2>
<p>Favicon 就是网站的小图标，比如标签栏中的图标。</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//2024/08/16/1e83fb5fd94465d1b86c22a9f549d48a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/16/1e83fb5fd94465d1b86c22a9f549d48a.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>
<p>准备一个喜欢的 Logo，最好为 png 格式，尺寸为 1:1，但是图片内容为圆形，圆形外面为透明的，这样才不会影响显示。</p>
<ol>
<li>

<!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//2024/08/16/e948f8f14fbefa649d76985e655e50ec.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/16/e948f8f14fbefa649d76985e655e50ec.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></li>
</ol>
</li>
<li>
<p>打开<a href="https://realfavicongenerator.net/">Favicon Generator</a>，选择自己的图标，点击 Generate Favicon，下载生成的文件。</p>
</li>
<li>
<p>把解压的文件都放到博客主目录下的<code>static/assets</code>目录下。比如<code>/static/assets/favicon.ico</code>。</p>
</li>
<li>
<p>修改配置文件<code>config.yaml</code>，添加如下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">assets</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">disableHLJS</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">favicon</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/assets/favicon.ico&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># favicon: &#34;&lt;link / abs url&gt;&#34; </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># favicon16x16: &#34;&lt;link / abs url&gt;&#34; </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># favicon32x32: &#34;&lt;link / abs url&gt;&#34; </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># apple_touch_icon: &#34;&lt;link / abs url&gt;&#34; </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># safari_pinned_tab: &#34;&lt;link / abs url&gt;&#34; </span><span class="w">
</span></span></span></code></pre></div><p>路径中不需要加上<code>static</code>。</p>
</li>
<li>
<p>刷新页面，可以看到网站图标已经修改。</p>
<ol>
<li>

<!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//2024/08/16/a13d568c2fad1947a18b1bfb9d07b2a2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/16/a13d568c2fad1947a18b1bfb9d07b2a2.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></li>
</ol>
</li>
</ol>
<h2 id="点击图片查看原图">点击图片查看原图</h2>
<p>博客中有一些图片比较大，无法查看原图就看不到细节，可以通过点击图片查看原图。</p>
<ol>
<li>
<p>修改网站根目录下的 config.yaml 文件，添加如下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">fancybox</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p>主题目录下创建以下路径和文件<code>/layouts/_default/_markup/render-image.html</code>，并添加内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"> {{if .Page.Site.Params.fancybox }}
</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-img-view&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">a</span> <span class="na">data-fancybox</span><span class="o">=</span><span class="s">&#34;gallery&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ .Destination | safeURL }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{ .Destination | safeURL }}&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;{{ .Text }}&#34;</span> <span class="err">{{</span> <span class="na">with</span> <span class="err">.</span><span class="na">Title</span><span class="err">}}</span> <span class="na">title</span><span class="o">=</span><span class="s">&#34;{{ . }}&#34;</span><span class="err">{{</span> <span class="na">end</span> <span class="err">}}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"> {{ end }}
</span></span></code></pre></div></li>
<li>
<p>主题 <code>layouts/partials</code> 目录下的 <code>head.html</code> 或者 <code>footer.html</code> 中添加以下代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{if .Page.Site.Params.fancybox }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ end }}
</span></span></code></pre></div></li>
</ol>
<h2 id="图片居中设置显示大小">图片居中，设置显示大小</h2>
<p>修改之前<code>/layouts/_default/_markup/render-image.html</code>中的代码，在<code>img</code>标签上添加<code>style=&quot;display: block; margin: 0 auto; width: 60%;&quot;</code>。</p>
<ul>
<li>display: block - 使图片变为块级元素，这样 margin: 0 auto 才会生效。</li>
<li>margin: 0 auto - 将图片水平居中。</li>
<li>width: 60% - 将图片宽度设置为原图的 60%。避免原图太大占用页面空间。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{ if .Page.Site.Params.fancybox }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-img-view&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">data-fancybox</span><span class="o">=</span><span class="s">&#34;gallery&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ .Destination | safeURL }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{ .Destination | safeURL }}&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;{{ .Text }}&#34;</span> <span class="err">{{</span> <span class="na">with</span> <span class="err">.</span><span class="na">Title</span><span class="err">}}</span> <span class="na">title</span><span class="o">=</span><span class="s">&#34;{{ . }}&#34;</span><span class="err">{{</span> <span class="na">end</span> <span class="err">}}</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;display: block; margin: 0 auto; width: 60%;&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ end }}
</span></span></code></pre></div><h2 id="根据屏幕大小自适应图片高度">根据屏幕大小自适应图片高度</h2>
<p>修改之前<code>/layouts/_default/_markup/render-image.html</code>中的代码，加入JS脚本，根据屏幕大小自适应图片高度。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Responsive Image<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nc">post-img-view</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="nc">responsive-image</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">display</span><span class="p">:</span> <span class="kc">block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {{ if .Page.Site.Params.fancybox }}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-img-view&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">a</span> <span class="na">data-fancybox</span><span class="o">=</span><span class="s">&#34;gallery&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ .Destination | safeURL }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">img</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;responsive-image&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{ .Destination | safeURL }}&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;{{ .Text }}&#34;</span> <span class="err">{{</span> <span class="na">with</span> <span class="err">.</span><span class="na">Title</span><span class="err">}}</span> <span class="na">title</span><span class="o">=</span><span class="s">&#34;{{ . }}&#34;</span><span class="err">{{</span> <span class="na">end</span> <span class="err">}}</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;margin: 0 auto;&#34;</span><span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {{ end }}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;DOMContentLoaded&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kd">var</span> <span class="nx">images</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s2">&#34;.responsive-image&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="kd">var</span> <span class="nx">maxHeight</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">innerHeight</span> <span class="o">/</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">images</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">image</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">image</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">maxHeight</span> <span class="o">=</span> <span class="nx">maxHeight</span> <span class="o">+</span> <span class="s2">&#34;px&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>效果如下，不用担心一张图片铺满整个屏幕了。</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//2024/08/31/13939de68f90687fd63eb51a3bfc62a1.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/08/31/13939de68f90687fd63eb51a3bfc62a1.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>
<h2 id="加入哔哩哔哩视频">加入哔哩哔哩视频</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">{{<span class="w"> </span><span class="l">&lt; douban src=&#34;https://book.douban.com/subject/30838038/&#34; &gt; }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># 使用的时候把去掉尖括弧外侧空格，此处是为了防止被识别生效</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>VSCode 插件发布简易流程</title>
      <link>https://lifeislife.cn/posts/vscode-%E6%8F%92%E4%BB%B6%E5%8F%91%E5%B8%83%E7%AE%80%E6%98%93%E6%B5%81%E7%A8%8B/</link>
      <pubDate>Sat, 27 Jul 2024 22:24:55 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/vscode-%E6%8F%92%E4%BB%B6%E5%8F%91%E5%B8%83%E7%AE%80%E6%98%93%E6%B5%81%E7%A8%8B/</guid>
      <description>&lt;h1 id=&#34;vscode-插件发布简易流程&#34;&gt;VSCode 插件发布简易流程&lt;/h1&gt;
&lt;h2 id=&#34;注册微软开发者账号&#34;&gt;注册微软开发者账号&lt;/h2&gt;
&lt;p&gt;如果已经有微软账号，直接登录即可，如果没有，需要注册一个微软账号。登录：https://login.live.com/。&lt;/p&gt;
&lt;p&gt;登录https://aka.ms/SignupAzureDevOps，注册微软开发者账号。设置保存默认即可，自己设置也行，不太重要。&lt;/p&gt;
&lt;p&gt;右上角创建Personal Access Token，用于登录vsce。&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//2024/07/27/0f289f67e18686fc378dd22a047c3150.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/27/0f289f67e18686fc378dd22a047c3150.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;创建的Token名随意，但是Organization和Scopes需要选择正确的，否则会报错。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Organization：All accessible organizations&lt;/li&gt;
&lt;li&gt;Scopes：Full access&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//2024/07/27/5fd7bf5c760b5dcbe48c36733bcbf0f3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/27/5fd7bf5c760b5dcbe48c36733bcbf0f3.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;创建成功后，会生成一个Token，需要保存好，后续会用到。Token只会显示一次，如果忘记了，需要重新创建。&lt;/p&gt;
&lt;h2 id=&#34;注册vscode-publisher&#34;&gt;注册VSCode Publisher&lt;/h2&gt;
&lt;p&gt;登录：https://aka.ms/vscode-create-publisher，填写姓名即可如“Dominic Zhang”，会自动生成一个唯一的ID，ID只能是数字字母下划线，所以会生成“DominicZhang”。需要记住这个ID，后续会用到。&lt;/p&gt;
&lt;h2 id=&#34;vsce登录并发布插件&#34;&gt;vsce登录并发布插件&lt;/h2&gt;
&lt;p&gt;输入在注册微软开发者账号时生成的Token，登录vsce。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vsce login DominicZhang                        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; WARNING  Failed to open credential store. Falling back to storing secrets clear-text in: /home/nic/.vsce
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://marketplace.visualstudio.com/manage/publishers/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Personal Access Token &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; publisher &lt;span class=&#34;s1&#34;&gt;&amp;#39;DominicZhang&amp;#39;&lt;/span&gt;: ****************************************************
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The Personal Access Token verification succeeded &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; the publisher &lt;span class=&#34;s1&#34;&gt;&amp;#39;DominicZhang&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;打包插件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vsce package
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;发布插件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vsce publish
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="vscode-插件发布简易流程">VSCode 插件发布简易流程</h1>
<h2 id="注册微软开发者账号">注册微软开发者账号</h2>
<p>如果已经有微软账号，直接登录即可，如果没有，需要注册一个微软账号。登录：https://login.live.com/。</p>
<p>登录https://aka.ms/SignupAzureDevOps，注册微软开发者账号。设置保存默认即可，自己设置也行，不太重要。</p>
<p>右上角创建Personal Access Token，用于登录vsce。</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//2024/07/27/0f289f67e18686fc378dd22a047c3150.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/27/0f289f67e18686fc378dd22a047c3150.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>创建的Token名随意，但是Organization和Scopes需要选择正确的，否则会报错。</p>
<ul>
<li>Organization：All accessible organizations</li>
<li>Scopes：Full access</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//2024/07/27/5fd7bf5c760b5dcbe48c36733bcbf0f3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/27/5fd7bf5c760b5dcbe48c36733bcbf0f3.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>创建成功后，会生成一个Token，需要保存好，后续会用到。Token只会显示一次，如果忘记了，需要重新创建。</p>
<h2 id="注册vscode-publisher">注册VSCode Publisher</h2>
<p>登录：https://aka.ms/vscode-create-publisher，填写姓名即可如“Dominic Zhang”，会自动生成一个唯一的ID，ID只能是数字字母下划线，所以会生成“DominicZhang”。需要记住这个ID，后续会用到。</p>
<h2 id="vsce登录并发布插件">vsce登录并发布插件</h2>
<p>输入在注册微软开发者账号时生成的Token，登录vsce。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vsce login DominicZhang                        
</span></span><span class="line"><span class="cl"> WARNING  Failed to open credential store. Falling back to storing secrets clear-text in: /home/nic/.vsce
</span></span><span class="line"><span class="cl">https://marketplace.visualstudio.com/manage/publishers/
</span></span><span class="line"><span class="cl">Personal Access Token <span class="k">for</span> publisher <span class="s1">&#39;DominicZhang&#39;</span>: ****************************************************
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The Personal Access Token verification succeeded <span class="k">for</span> the publisher <span class="s1">&#39;DominicZhang&#39;</span>.
</span></span></code></pre></div><p>打包插件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vsce package
</span></span></code></pre></div><p>发布插件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vsce publish
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>Windows挂载NFS网络文件系统实现与开发文件传输</title>
      <link>https://lifeislife.cn/posts/windows%E6%8C%82%E8%BD%BDnfs%E7%BD%91%E7%BB%9C%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%AE%9E%E7%8E%B0%E4%B8%8E%E5%BC%80%E5%8F%91%E6%96%87%E4%BB%B6%E4%BC%A0%E8%BE%93/</link>
      <pubDate>Thu, 25 Jul 2024 21:11:05 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/windows%E6%8C%82%E8%BD%BDnfs%E7%BD%91%E7%BB%9C%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%AE%9E%E7%8E%B0%E4%B8%8E%E5%BC%80%E5%8F%91%E6%96%87%E4%BB%B6%E4%BC%A0%E8%BE%93/</guid>
      <description>&lt;h2 id=&#34;配置windows与开发板网络通信&#34;&gt;配置Windows与开发板网络通信&lt;/h2&gt;
&lt;h3 id=&#34;配置windows侧网卡&#34;&gt;配置Windows侧网卡&lt;/h3&gt;
&lt;p&gt;将Windows与开发板通过USB网卡连接，开发板的网卡插口选择eth0，也就是靠近拨码开关的网口。开发板开机，Windows的设备管理器中出现以下网卡表示网卡功能正常。&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//2024/07/21/90a28f55cd3ca3039694cb1c16ddff3e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/90a28f55cd3ca3039694cb1c16ddff3e.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;打开网络和共享中心，选择更改适配器设置，找到USB网卡，右键属性，选择Internet协议版本4（TCP/IPv4），点击属性，选择使用以下IP地址，设置IP地址为以下内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IP地址：192.168.1.111&lt;/li&gt;
&lt;li&gt;子网掩码：255.255.225.0&lt;/li&gt;
&lt;li&gt;默认网关：192.168.1.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;网卡的IP要和你Window主机自己的IP在一个网段，不能盲目设置为手册里的&lt;code&gt;192.168.5.10&lt;/code&gt;，这样会导致Windows主机无法访问开发板。可以通过在Windows命令行输入&lt;code&gt;ipconfig&lt;/code&gt;查看Windows主机的IP地址。一般来说，如果没有设置过路由器，默认都是在&lt;code&gt;192.168.1.x&lt;/code&gt;网段。那么网关通常就是这个网段的第一个地址，也就是&lt;code&gt;192.168.1.1&lt;/code&gt;，这也是路由器的地址。&lt;/p&gt;
&lt;p&gt;如果你配置完网卡以及开发板的IP后能正常相互ping通，那么说明网络配置成功。如果你很不巧和我一样，USB网卡一直无法通过DHCP自动获取IP地址，并且配置完静态IP后只能单侧ping通，那么也可以和我一样将USB网卡和主机的WIFI网卡桥接，这样就可以实现双向通信。这里有个前提条件就是你的主机原先就有两张网卡，一张用于连接路由器，一张是WIFI网卡。不能直接将USB网卡和连接路由器的网卡桥接。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;万万没想到一张整年闲置的WIFI网卡竟然派上了用场。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;选择WIFI网卡和USB网卡，右键桥接，这样就创建一个网桥，网桥会自动获取IP地址。&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//2024/07/25/07fff7094057ea29a1fd3ace9bdbb33e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/25/07fff7094057ea29a1fd3ace9bdbb33e.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;配置网卡IP，这个IP也是需要和Windows主机在一个网段的。因为路由器分配IP时会从小到大分配，为了避免和已有的设备IP冲突，我们就可以设置一个比较大的IP，比如112。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 配置网卡ip为192.169.1.112，子网掩码为255.255.255.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 192.168.1.112
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;设置默认网关：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip route add default via 192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;注意：更换网口需要重启开发板&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Match&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;Name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Network&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;Address&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;192.168.1.112/24 &lt;span class=&#34;nv&#34;&gt;Gateway&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;挂载nsf共享目录&#34;&gt;挂载NSF共享目录&lt;/h2&gt;
&lt;h3 id=&#34;windows侧配置&#34;&gt;Windows侧配置&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;安装haneWIN NFS 服务器（&lt;a href=&#34;https://pan.baidu.com/s/1y2R2aJyEExnCJ7FRqdaNdA&#34;&gt;https://pan.baidu.com/s/1y2R2aJyEExnCJ7FRqdaNdA&lt;/a&gt;  提取码：esdw）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;安装路径一般为：&lt;code&gt;C:\Program Files (x86)\nfsd&lt;/code&gt;。使用VScode打开该目录下的&lt;code&gt;exports&lt;/code&gt;文件，修改内容如下，保存时需要用管理员身份保存：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;d:\nfs -public 192.168.1.112
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;d:\nfs&lt;/code&gt;：共享目录为D盘的nfs，可以根据自己的实际情况修改。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-public&lt;/code&gt;：表示共享目录为公共目录，即所有用户都可以访问。&lt;/li&gt;
&lt;li&gt;IP：为开发板的IP地址，表示要与这个IP进行共享。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;关闭防火墙或者开启必要的通信端口&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果为了省事，可以直接关闭Windows防火墙&lt;/li&gt;
&lt;li&gt;如果不想关防火墙需要开启以下端口的通信：
&lt;ul&gt;
&lt;li&gt;TCP 111&lt;/li&gt;
&lt;li&gt;UDP 111&lt;/li&gt;
&lt;li&gt;TCP 2049&lt;/li&gt;
&lt;li&gt;UDP 2049&lt;/li&gt;
&lt;li&gt;TCP 1058&lt;/li&gt;
&lt;li&gt;UDP 1058&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;开启端口的方法为：&lt;code&gt;控制面板&lt;/code&gt; -&amp;gt; &lt;code&gt;系统和安全&lt;/code&gt; -&amp;gt; &lt;code&gt;Windows Defender 防火墙&lt;/code&gt; -&amp;gt; &lt;code&gt;高级设置&lt;/code&gt; -&amp;gt; &lt;code&gt;入站规则&lt;/code&gt; -&amp;gt; &lt;code&gt;新建规则&lt;/code&gt; -&amp;gt; &lt;code&gt;端口&lt;/code&gt; -&amp;gt; &lt;code&gt;下一步&lt;/code&gt; -&amp;gt; &lt;code&gt;TCP&lt;/code&gt; -&amp;gt; &lt;code&gt;特定本地端口&lt;/code&gt; -&amp;gt; &lt;code&gt;2049&lt;/code&gt; -&amp;gt; &lt;code&gt;下一步&lt;/code&gt; -&amp;gt; &lt;code&gt;允许连接&lt;/code&gt; -&amp;gt; &lt;code&gt;下一步&lt;/code&gt; -&amp;gt; &lt;code&gt;下一步&lt;/code&gt; -&amp;gt; &lt;code&gt;规则名称&lt;/code&gt; -&amp;gt; &lt;code&gt;完成&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;需要注意在出站规则和入站规则都需要添加。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重启Windows NFS Server服务&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;右键Windows徽标键，打开计算机管理，找到服务和应用程序，点击服务，找到&lt;code&gt;NFS Server&lt;/code&gt;，右键重启。
&lt;ol&gt;
&lt;li&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//2024/07/21/b73eea915bc0492384cdf55949db30e6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/b73eea915bc0492384cdf55949db30e6.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&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//2024/07/21/8003d546a8f10b949a43f8856d6b07d6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/8003d546a8f10b949a43f8856d6b07d6.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;开发板侧配置&#34;&gt;开发板侧配置&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;新建一个目录，用于挂载共享目录&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;mkdir /nfs&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;挂载共享目录&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -vvv  -t nfs -o nolock -o &lt;span class=&#34;nv&#34;&gt;vers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;3,port&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2049&lt;/span&gt; 192.168.1.7:/d/nfs  /nfs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-vvv：表示挂载时输出详细信息，可省略&lt;/li&gt;
&lt;li&gt;-t nfs：表示挂载的文件系统类型为nfs&lt;/li&gt;
&lt;li&gt;-o nolock：表示不使用锁定机制&lt;/li&gt;
&lt;li&gt;-o vers=3：表示使用NFSv3版本&lt;/li&gt;
&lt;li&gt;-o port=2049：表示NFS服务端口号&lt;/li&gt;
&lt;li&gt;192.168.1.7:/d/nfs：表示NFS服务器的IP地址和共享目录&lt;/li&gt;
&lt;li&gt;/nfs: 表示开发板上的共享目录&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;常见错误&#34;&gt;常见错误&lt;/h3&gt;
&lt;h4 id=&#34;挂载时报错portmap-query-failed-rpc-timed-out&#34;&gt;挂载时报错：portmap query failed: RPC: Timed out&lt;/h4&gt;
&lt;p&gt;点击NFS服务的端口映射页面，可以查看RPC使用的所有端口，需要将这些端口都开放，或者直接关闭防火墙。&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//2024/07/21/b73ecc06edeb5e50f42ee12768fd1f68.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/b73ecc06edeb5e50f42ee12768fd1f68.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;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip route add default via 192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip route del 0.0.0.0/0 via 192.168.1.1 dev eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip route del 192.168.1.0/24 dev eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vi  /etc/systemd/network/50-static.network
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://gitcode.csdn.net/65e93d3c1a836825ed78e9ea.html?dp_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTE3MDk0NywiZXhwIjoxNzIxOTEyNTYxLCJpYXQiOjE3MjEzMDc3NjEsInVzZXJuYW1lIjoiRHVua3laIn0.7EEwv32DoA7Vdm-XwdAd3_0P8hEsWarq71nSo5HWTrA&#34;&gt;如何在Windows上搭建NFS服务器实现开发板与Windows之间的文件共享_opencv_Jack_小明-GitCode 开源社区&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="配置windows与开发板网络通信">配置Windows与开发板网络通信</h2>
<h3 id="配置windows侧网卡">配置Windows侧网卡</h3>
<p>将Windows与开发板通过USB网卡连接，开发板的网卡插口选择eth0，也就是靠近拨码开关的网口。开发板开机，Windows的设备管理器中出现以下网卡表示网卡功能正常。</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//2024/07/21/90a28f55cd3ca3039694cb1c16ddff3e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/90a28f55cd3ca3039694cb1c16ddff3e.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>打开网络和共享中心，选择更改适配器设置，找到USB网卡，右键属性，选择Internet协议版本4（TCP/IPv4），点击属性，选择使用以下IP地址，设置IP地址为以下内容：</p>
<ul>
<li>IP地址：192.168.1.111</li>
<li>子网掩码：255.255.225.0</li>
<li>默认网关：192.168.1.1</li>
</ul>
<p>网卡的IP要和你Window主机自己的IP在一个网段，不能盲目设置为手册里的<code>192.168.5.10</code>，这样会导致Windows主机无法访问开发板。可以通过在Windows命令行输入<code>ipconfig</code>查看Windows主机的IP地址。一般来说，如果没有设置过路由器，默认都是在<code>192.168.1.x</code>网段。那么网关通常就是这个网段的第一个地址，也就是<code>192.168.1.1</code>，这也是路由器的地址。</p>
<p>如果你配置完网卡以及开发板的IP后能正常相互ping通，那么说明网络配置成功。如果你很不巧和我一样，USB网卡一直无法通过DHCP自动获取IP地址，并且配置完静态IP后只能单侧ping通，那么也可以和我一样将USB网卡和主机的WIFI网卡桥接，这样就可以实现双向通信。这里有个前提条件就是你的主机原先就有两张网卡，一张用于连接路由器，一张是WIFI网卡。不能直接将USB网卡和连接路由器的网卡桥接。</p>
<blockquote>
<p>万万没想到一张整年闲置的WIFI网卡竟然派上了用场。</p>
</blockquote>
<p>选择WIFI网卡和USB网卡，右键桥接，这样就创建一个网桥，网桥会自动获取IP地址。</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//2024/07/25/07fff7094057ea29a1fd3ace9bdbb33e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/25/07fff7094057ea29a1fd3ace9bdbb33e.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>配置网卡IP，这个IP也是需要和Windows主机在一个网段的。因为路由器分配IP时会从小到大分配，为了避免和已有的设备IP冲突，我们就可以设置一个比较大的IP，比如112。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 配置网卡ip为192.169.1.112，子网掩码为255.255.255.0</span>
</span></span><span class="line"><span class="cl">ifconfig eth0 192.168.1.112
</span></span></code></pre></div><p>设置默认网关：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ip route add default via 192.168.1.1
</span></span></code></pre></div><blockquote>
<p>注意：更换网口需要重启开发板</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>Match<span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Name</span><span class="o">=</span>eth0
</span></span><span class="line"><span class="cl"><span class="o">[</span>Network<span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">Address</span><span class="o">=</span>192.168.1.112/24 <span class="nv">Gateway</span><span class="o">=</span>192.168.1.1
</span></span></code></pre></div><h2 id="挂载nsf共享目录">挂载NSF共享目录</h2>
<h3 id="windows侧配置">Windows侧配置</h3>
<ol>
<li>
<p>安装haneWIN NFS 服务器（<a href="https://pan.baidu.com/s/1y2R2aJyEExnCJ7FRqdaNdA">https://pan.baidu.com/s/1y2R2aJyEExnCJ7FRqdaNdA</a>  提取码：esdw）</p>
<ol>
<li>安装路径一般为：<code>C:\Program Files (x86)\nfsd</code>。使用VScode打开该目录下的<code>exports</code>文件，修改内容如下，保存时需要用管理员身份保存：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">d:\nfs -public 192.168.1.112
</span></span></code></pre></div><ul>
<li><code>d:\nfs</code>：共享目录为D盘的nfs，可以根据自己的实际情况修改。</li>
<li><code>-public</code>：表示共享目录为公共目录，即所有用户都可以访问。</li>
<li>IP：为开发板的IP地址，表示要与这个IP进行共享。</li>
</ul>
</li>
<li>
<p>关闭防火墙或者开启必要的通信端口</p>
<ol>
<li>如果为了省事，可以直接关闭Windows防火墙</li>
<li>如果不想关防火墙需要开启以下端口的通信：
<ul>
<li>TCP 111</li>
<li>UDP 111</li>
<li>TCP 2049</li>
<li>UDP 2049</li>
<li>TCP 1058</li>
<li>UDP 1058</li>
</ul>
</li>
<li>开启端口的方法为：<code>控制面板</code> -&gt; <code>系统和安全</code> -&gt; <code>Windows Defender 防火墙</code> -&gt; <code>高级设置</code> -&gt; <code>入站规则</code> -&gt; <code>新建规则</code> -&gt; <code>端口</code> -&gt; <code>下一步</code> -&gt; <code>TCP</code> -&gt; <code>特定本地端口</code> -&gt; <code>2049</code> -&gt; <code>下一步</code> -&gt; <code>允许连接</code> -&gt; <code>下一步</code> -&gt; <code>下一步</code> -&gt; <code>规则名称</code> -&gt; <code>完成</code>。</li>
<li>需要注意在出站规则和入站规则都需要添加。</li>
</ol>
</li>
<li>
<p>重启Windows NFS Server服务</p>
<ol>
<li>右键Windows徽标键，打开计算机管理，找到服务和应用程序，点击服务，找到<code>NFS Server</code>，右键重启。
<ol>
<li>

<!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//2024/07/21/b73eea915bc0492384cdf55949db30e6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/b73eea915bc0492384cdf55949db30e6.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></li>
</ol>
</li>
<li>点击软件的输出选项卡，点击重启服务器，如果出现如图配置表示正确启动服务：


<!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//2024/07/21/8003d546a8f10b949a43f8856d6b07d6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/8003d546a8f10b949a43f8856d6b07d6.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></li>
</ol>
</li>
</ol>
<h3 id="开发板侧配置">开发板侧配置</h3>
<ol>
<li>
<p>新建一个目录，用于挂载共享目录</p>
<ol>
<li><code>mkdir /nfs</code></li>
</ol>
</li>
<li>
<p>挂载共享目录</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mount -vvv  -t nfs -o nolock -o <span class="nv">vers</span><span class="o">=</span>3,port<span class="o">=</span><span class="m">2049</span> 192.168.1.7:/d/nfs  /nfs
</span></span></code></pre></div><ul>
<li>-vvv：表示挂载时输出详细信息，可省略</li>
<li>-t nfs：表示挂载的文件系统类型为nfs</li>
<li>-o nolock：表示不使用锁定机制</li>
<li>-o vers=3：表示使用NFSv3版本</li>
<li>-o port=2049：表示NFS服务端口号</li>
<li>192.168.1.7:/d/nfs：表示NFS服务器的IP地址和共享目录</li>
<li>/nfs: 表示开发板上的共享目录</li>
</ul>
</li>
</ol>
<h3 id="常见错误">常见错误</h3>
<h4 id="挂载时报错portmap-query-failed-rpc-timed-out">挂载时报错：portmap query failed: RPC: Timed out</h4>
<p>点击NFS服务的端口映射页面，可以查看RPC使用的所有端口，需要将这些端口都开放，或者直接关闭防火墙。</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//2024/07/21/b73ecc06edeb5e50f42ee12768fd1f68.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/21/b73ecc06edeb5e50f42ee12768fd1f68.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>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ip route add default via 192.168.1.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ip route del 0.0.0.0/0 via 192.168.1.1 dev eth0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ip route del 192.168.1.0/24 dev eth0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vi  /etc/systemd/network/50-static.network
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> systemd-networkd
</span></span></code></pre></div><p><a href="https://gitcode.csdn.net/65e93d3c1a836825ed78e9ea.html?dp_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTE3MDk0NywiZXhwIjoxNzIxOTEyNTYxLCJpYXQiOjE3MjEzMDc3NjEsInVzZXJuYW1lIjoiRHVua3laIn0.7EEwv32DoA7Vdm-XwdAd3_0P8hEsWarq71nSo5HWTrA">如何在Windows上搭建NFS服务器实现开发板与Windows之间的文件共享_opencv_Jack_小明-GitCode 开源社区</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 常用命令</title>
      <link>https://lifeislife.cn/posts/qemu%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</link>
      <pubDate>Sun, 07 Jul 2024 10:22:12 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</guid>
      <description>&lt;p&gt;QEMU 是一个开源的虚拟化软件，它能够模拟不同的硬件平台，让用户在不同的操作系统之间进行切换和测试。以下是 QEMU 常用命令的总结文档，包含每个命令的功能说明。&lt;/p&gt;
&lt;h2 id=&#34;qemu-安装&#34;&gt;QEMU 安装&lt;/h2&gt;
&lt;h3 id=&#34;源码下载&#34;&gt;源码下载&lt;/h3&gt;
&lt;h4 id=&#34;压缩包方式&#34;&gt;压缩包方式&lt;/h4&gt;
&lt;p&gt;版本可以修改&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget  &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://download.qemu.org/qemu-8.0.2.tar.xz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xf qemu-8.0.2.tar.xz --strip-components&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;clone-方式&#34;&gt;Clone 方式&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;git clone https://gitlab.com/qemu-project/qemu.git&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;cd qemu&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;git submodule init&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;git submodule update --recursive&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;配置编译选项&#34;&gt;配置编译选项&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --target-list&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--enable-kvm &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--enable-debug &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--enable-sdl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/user/program/riscv64-qemu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--python&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/bin/python3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# --python=python路径，如果提示默认python版本低，可以加这个参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# --prefix 选项设置qemu的安装位置绝对路径，之后若要卸载删除qemu只要删除该文件夹即可，--enable-kvm开启kvm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# config完，可以在指定的qemu安装文件夹下面找到config-host.mak文件，&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 该文件记录着qemu配置的选项，可以和自己设置的进行对比，确保配置和自己已知&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;一些常用的编译选项&#34;&gt;一些常用的编译选项&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ndash;enable-debug：编译调试版本，调试版本的运行速度非常慢&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-werror：忽略警告，否则任何编译警告都被视为编译错误&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-plugins：开启TCG Plugin支持&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-stack-protector：关闭QEMU自身的栈保护&lt;/li&gt;
&lt;li&gt;&amp;ndash;extra-cflags=&amp;quot;-O3&amp;quot;：能让你的QEMU提速5~10%，如果编译时报错，请加上&lt;code&gt;--disable-werror&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;ndash;prefix=&amp;lt;路径&amp;gt;：指定安装目录的路径&lt;/li&gt;
&lt;li&gt;&amp;ndash;target-list=&amp;lt;架构&amp;gt;：指定要编译的目标架构列表，例如 &lt;code&gt;x86_64-softmmu,arm-softmmu&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-&amp;lt;功能&amp;gt;：启用指定的功能。例如，&lt;code&gt;--enable-kvm&lt;/code&gt; 启用 KVM 支持，&lt;code&gt;--enable-gtk&lt;/code&gt; 启用 GTK 图形界面等。&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-&amp;lt;功能&amp;gt;：禁用指定的功能。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-debug：启用调试模式，包括调试符号和调试输出。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-virtfs：启用 virtio 文件系统支持。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-modules：启用模块支持。&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-guest-agent：禁用客户机代理支持。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-trace-backend=&amp;lt;后端&amp;gt;：指定跟踪后端，例如 &lt;code&gt;simple&lt;/code&gt;、&lt;code&gt;log&lt;/code&gt; 或 &lt;code&gt;dtrace&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-vhost-net：禁用 vhost-net 支持。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;编译时输出i-文件用于查看宏定义展开&#34;&gt;编译时输出.i 文件用于查看宏定义展开&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;$ ./configure  --extra-cflags=&amp;#34;-save-temps&amp;#34;   --target-list=riscv64-linux-user,riscv64-softmmu --disable-werror  --python=/usr/bin/python3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;启动虚拟机&#34;&gt;启动虚拟机&lt;/h2&gt;
&lt;p&gt;以下命令用于启动虚拟机：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -boot d -cdrom /path/to/iso -m &lt;span class=&#34;m&#34;&gt;1024&lt;/span&gt; -hda /path/to/hda.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-boot d：从 CD/DVD 启动&lt;/li&gt;
&lt;li&gt;-cdrom /path/to/iso：指定 ISO 文件的路径&lt;/li&gt;
&lt;li&gt;-m 1024：设置虚拟机的内存大小为 1024MB&lt;/li&gt;
&lt;li&gt;-hda /path/to/hda.img：指定虚拟硬盘的路径&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;安装系统至磁盘&#34;&gt;安装系统至磁盘&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -cdrom ~/Downloads/ubuntu.iso &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -drive &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;ubuntu.qcow2 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -enable-kvm &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -cpu host &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -smp &lt;span class=&#34;nv&#34;&gt;cores&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;2,threads&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -m 2G &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -vga virtio &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -display sdl,gl&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;on 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;网络配置&#34;&gt;网络配置&lt;/h2&gt;
&lt;p&gt;以下命令用于配置虚拟机的网络：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -net nic -net user,hostfwd&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tcp::2222-:22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-net nic：启用虚拟网卡&lt;/li&gt;
&lt;li&gt;-net user：使用用户模式网络堆栈&lt;/li&gt;
&lt;li&gt;hostfwd=tcp::2222-:22：将主机的 2222 端口转发到虚拟机的 22 端口&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;调试相关命令&#34;&gt;调试相关命令&lt;/h2&gt;
&lt;h3 id=&#34;启动调试模式&#34;&gt;启动调试模式&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -s -S
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-s：启用 GDB 调试&lt;/li&gt;
&lt;li&gt;-S：在启动时暂停虚拟机，等待调试器连接&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;输出调试日志&#34;&gt;输出调试日志&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;/home/user/develop/qemu/build/qemu-riscv64 -d in_asm,exec,cpu,strace -D ./log hello&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;QEMU 的 &lt;code&gt;-d&lt;/code&gt; 参数说明&lt;/p&gt;
&lt;p&gt;日志选项（用逗号分隔）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;out_asm&lt;/code&gt;：显示每个已编译基本块生成的宿主汇编代码&lt;/li&gt;
&lt;li&gt;&lt;code&gt;in_asm&lt;/code&gt;：显示每个已编译基本块的目标汇编代码&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op&lt;/code&gt;：显示每个已编译基本块的微操作&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op_opt&lt;/code&gt;：显示优化后的微操作&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op_ind&lt;/code&gt;：显示间接降低前的微操作&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt;：以简短格式显示中断/异常&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exec&lt;/code&gt;：在每次执行基本块前显示跟踪（大量日志）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cpu&lt;/code&gt;：在进入基本块前显示 CPU 寄存器（大量日志）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fpu&lt;/code&gt;：在“cpu”日志中包含 FPU 寄存器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mmu&lt;/code&gt;：记录与 MMU 相关的活动&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pcall&lt;/code&gt;：仅限 x86：显示保护模式下的远程调用/返回/异常&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cpu_reset&lt;/code&gt;：在 CPU 重置前显示 CPU 状态&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unimp&lt;/code&gt;：记录未实现的功能&lt;/li&gt;
&lt;li&gt;&lt;code&gt;guest_errors&lt;/code&gt;：当来宾操作系统执行无效操作（例如访问不存在的寄存器）时记录日志&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page&lt;/code&gt;：在用户模式仿真开始时转储页面&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nochain&lt;/code&gt;：不链式编译基本块，以便“exec”和“cpu”显示完整的跟踪&lt;/li&gt;
&lt;li&gt;&lt;code&gt;plugin&lt;/code&gt;：输出来自 TCG 插件的信息&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strace&lt;/code&gt;：记录每个用户模式系统调用、其输入和结果&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trace:PATTERN&lt;/code&gt;：启用跟踪事件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用 &lt;code&gt;&amp;quot;-d trace:help&amp;quot;&lt;/code&gt; 来获取跟踪事件列表。&lt;/p&gt;
&lt;h2 id=&#34;qemu-system-xxx-常用相关参数&#34;&gt;qemu-system-xxx 常用相关参数&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;-M：指定 machine，-help 可以列出所有所支持的 machine&lt;/li&gt;
&lt;li&gt;-cpu：指定模拟的 CPU 型号，例如 &lt;code&gt;cortex-a57&lt;/code&gt;、&lt;code&gt;cortex-a53&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;-smp：CPU 核数&lt;/li&gt;
&lt;li&gt;-m：RAM 容量&lt;/li&gt;
&lt;li&gt;-kernel：Linux kernel 文件&lt;/li&gt;
&lt;li&gt;-append：Linux Kernel 的 bootargs。这个命令的参数很复杂，具体可以参考 kernel 文档。&lt;/li&gt;
&lt;li&gt;-console：设备名必须和 machine 的串口一致，否则会看不到 kernel log。而不同的平台 console 名称都是不一样的，这是 Kernel 很不友好的一点。&lt;/li&gt;
&lt;li&gt;-ignore_loglevel：可以让你看到尽可能多的 kernel log，当然也会减慢 kernel 的启动速度&lt;/li&gt;
&lt;li&gt;-init：必须保证 initrd 里有/linuxrc 这个文件，否则会无法启动 shell&lt;/li&gt;
&lt;li&gt;-initrd：指定 initrd 文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;快照管理&#34;&gt;快照管理&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 拍快照&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img snapshot -c oe-rv-snapshot1  openEuler-22.09-riscv64-qemu.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 列举快照&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img snapshot-l openEuler-22.09-riscv64-qemu.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 恢复快照&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img snapshot -a my_snapshot mydisk.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;查看虚拟硬盘信息&#34;&gt;查看虚拟硬盘信息&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img info /path/to/image
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;将虚拟硬盘转换为-qcow2-格式&#34;&gt;将虚拟硬盘转换为 QCOW2 格式&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img convert -O qcow2 /path/to/image /path/to/new/image
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;调整磁盘大小&#34;&gt;调整磁盘大小&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img resize ubuntu.qcow2 +5G
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;显示器选项&#34;&gt;显示器选项&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -vga std
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -display sdl
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -display gtk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-vga std：使用标准 VGA 显示器&lt;/li&gt;
&lt;li&gt;-display sdl：使用 SDL 显示器&lt;/li&gt;
&lt;li&gt;-display gtk：使用 GTK 显示器&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;输入选项&#34;&gt;输入选项&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -k en-us
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -device usb-mouse
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-k en-us：使用英文键盘布局&lt;/li&gt;
&lt;li&gt;-usb：启用 USB 支持&lt;/li&gt;
&lt;li&gt;-device usb-mouse：使用 USB 鼠标设备&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;声音选项&#34;&gt;声音选项&lt;/h2&gt;
&lt;p&gt;以下命令用于配置虚拟机的声音：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -soundhw all
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -soundhw sb16
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -audiodev pa,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;pa1,out.mixing-engine&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;off
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-soundhw all：启用所有声卡&lt;/li&gt;
&lt;li&gt;-soundhw sb16：启用 SoundBlaster 16 声卡&lt;/li&gt;
&lt;li&gt;-audiodev pa,id=pa1,out.mixing-engine=off：使用 PulseAudio 声音设备&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;usb-设备管理&#34;&gt;USB 设备管理&lt;/h2&gt;
&lt;p&gt;以下命令用于管理虚拟机的 USB 设备：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usbdevice host:1234:5678
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usbdevice tablet
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usbdevice keyboard
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-usbdevice host🔢5678：将主机的 USB 设备 1234:5678 分配给虚拟机&lt;/li&gt;
&lt;li&gt;-usbdevice tablet：使用 USB 触摸板&lt;/li&gt;
&lt;li&gt;-usbdevice keyboard：使用 USB 键盘&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<p>QEMU 是一个开源的虚拟化软件，它能够模拟不同的硬件平台，让用户在不同的操作系统之间进行切换和测试。以下是 QEMU 常用命令的总结文档，包含每个命令的功能说明。</p>
<h2 id="qemu-安装">QEMU 安装</h2>
<h3 id="源码下载">源码下载</h3>
<h4 id="压缩包方式">压缩包方式</h4>
<p>版本可以修改</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> wget  <span class="s2">&#34;https://download.qemu.org/qemu-8.0.2.tar.xz&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">tar -xf qemu-8.0.2.tar.xz --strip-components<span class="o">=</span><span class="m">1</span> 
</span></span></code></pre></div><h4 id="clone-方式">Clone 方式</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">git clone https://gitlab.com/qemu-project/qemu.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">cd qemu</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">git submodule init</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">git submodule update --recursive</span><span class="w">
</span></span></span></code></pre></div><h3 id="配置编译选项">配置编译选项</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./configure --target-list<span class="o">=</span>riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu <span class="se">\
</span></span></span><span class="line"><span class="cl">--enable-kvm <span class="se">\
</span></span></span><span class="line"><span class="cl">--enable-debug <span class="se">\
</span></span></span><span class="line"><span class="cl">--enable-sdl <span class="se">\
</span></span></span><span class="line"><span class="cl">--prefix<span class="o">=</span>/home/user/program/riscv64-qemu <span class="se">\
</span></span></span><span class="line"><span class="cl">--python<span class="o">=</span>/usr/bin/python3
</span></span><span class="line"><span class="cl"><span class="c1"># --python=python路径，如果提示默认python版本低，可以加这个参数</span>
</span></span><span class="line"><span class="cl"><span class="c1"># --prefix 选项设置qemu的安装位置绝对路径，之后若要卸载删除qemu只要删除该文件夹即可，--enable-kvm开启kvm</span>
</span></span><span class="line"><span class="cl"><span class="c1"># config完，可以在指定的qemu安装文件夹下面找到config-host.mak文件，</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 该文件记录着qemu配置的选项，可以和自己设置的进行对比，确保配置和自己已知</span>
</span></span></code></pre></div><h4 id="一些常用的编译选项">一些常用的编译选项</h4>
<ul>
<li>&ndash;enable-debug：编译调试版本，调试版本的运行速度非常慢</li>
<li>&ndash;disable-werror：忽略警告，否则任何编译警告都被视为编译错误</li>
<li>&ndash;enable-plugins：开启TCG Plugin支持</li>
<li>&ndash;disable-stack-protector：关闭QEMU自身的栈保护</li>
<li>&ndash;extra-cflags=&quot;-O3&quot;：能让你的QEMU提速5~10%，如果编译时报错，请加上<code>--disable-werror</code></li>
<li>&ndash;prefix=&lt;路径&gt;：指定安装目录的路径</li>
<li>&ndash;target-list=&lt;架构&gt;：指定要编译的目标架构列表，例如 <code>x86_64-softmmu,arm-softmmu</code>。</li>
<li>&ndash;enable-&lt;功能&gt;：启用指定的功能。例如，<code>--enable-kvm</code> 启用 KVM 支持，<code>--enable-gtk</code> 启用 GTK 图形界面等。</li>
<li>&ndash;disable-&lt;功能&gt;：禁用指定的功能。</li>
<li>&ndash;enable-debug：启用调试模式，包括调试符号和调试输出。</li>
<li>&ndash;enable-virtfs：启用 virtio 文件系统支持。</li>
<li>&ndash;enable-modules：启用模块支持。</li>
<li>&ndash;disable-guest-agent：禁用客户机代理支持。</li>
<li>&ndash;enable-trace-backend=&lt;后端&gt;：指定跟踪后端，例如 <code>simple</code>、<code>log</code> 或 <code>dtrace</code>。</li>
<li>&ndash;disable-vhost-net：禁用 vhost-net 支持。</li>
</ul>
<h4 id="编译时输出i-文件用于查看宏定义展开">编译时输出.i 文件用于查看宏定义展开</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">$ ./configure  --extra-cflags=&#34;-save-temps&#34;   --target-list=riscv64-linux-user,riscv64-softmmu --disable-werror  --python=/usr/bin/python3</span><span class="w">
</span></span></span></code></pre></div><h2 id="启动虚拟机">启动虚拟机</h2>
<p>以下命令用于启动虚拟机：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -boot d -cdrom /path/to/iso -m <span class="m">1024</span> -hda /path/to/hda.img
</span></span></code></pre></div><ul>
<li>-boot d：从 CD/DVD 启动</li>
<li>-cdrom /path/to/iso：指定 ISO 文件的路径</li>
<li>-m 1024：设置虚拟机的内存大小为 1024MB</li>
<li>-hda /path/to/hda.img：指定虚拟硬盘的路径</li>
</ul>
<h2 id="安装系统至磁盘">安装系统至磁盘</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 <span class="se">\
</span></span></span><span class="line"><span class="cl">    -cdrom ~/Downloads/ubuntu.iso <span class="se">\
</span></span></span><span class="line"><span class="cl">    -drive <span class="nv">file</span><span class="o">=</span>ubuntu.qcow2 <span class="se">\
</span></span></span><span class="line"><span class="cl">    -enable-kvm <span class="se">\
</span></span></span><span class="line"><span class="cl">    -cpu host <span class="se">\
</span></span></span><span class="line"><span class="cl">    -smp <span class="nv">cores</span><span class="o">=</span>2,threads<span class="o">=</span><span class="m">2</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    -m 2G <span class="se">\
</span></span></span><span class="line"><span class="cl">    -vga virtio <span class="se">\
</span></span></span><span class="line"><span class="cl">    -display sdl,gl<span class="o">=</span>on 
</span></span></code></pre></div><h2 id="网络配置">网络配置</h2>
<p>以下命令用于配置虚拟机的网络：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -net nic -net user,hostfwd<span class="o">=</span>tcp::2222-:22
</span></span></code></pre></div><ul>
<li>-net nic：启用虚拟网卡</li>
<li>-net user：使用用户模式网络堆栈</li>
<li>hostfwd=tcp::2222-:22：将主机的 2222 端口转发到虚拟机的 22 端口</li>
</ul>
<h2 id="调试相关命令">调试相关命令</h2>
<h3 id="启动调试模式">启动调试模式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -s -S
</span></span></code></pre></div><ul>
<li>-s：启用 GDB 调试</li>
<li>-S：在启动时暂停虚拟机，等待调试器连接</li>
</ul>
<h3 id="输出调试日志">输出调试日志</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">/home/user/develop/qemu/build/qemu-riscv64 -d in_asm,exec,cpu,strace -D ./log hello</span><span class="w">
</span></span></span></code></pre></div><p>QEMU 的 <code>-d</code> 参数说明</p>
<p>日志选项（用逗号分隔）：</p>
<ul>
<li><code>out_asm</code>：显示每个已编译基本块生成的宿主汇编代码</li>
<li><code>in_asm</code>：显示每个已编译基本块的目标汇编代码</li>
<li><code>op</code>：显示每个已编译基本块的微操作</li>
<li><code>op_opt</code>：显示优化后的微操作</li>
<li><code>op_ind</code>：显示间接降低前的微操作</li>
<li><code>int</code>：以简短格式显示中断/异常</li>
<li><code>exec</code>：在每次执行基本块前显示跟踪（大量日志）</li>
<li><code>cpu</code>：在进入基本块前显示 CPU 寄存器（大量日志）</li>
<li><code>fpu</code>：在“cpu”日志中包含 FPU 寄存器</li>
<li><code>mmu</code>：记录与 MMU 相关的活动</li>
<li><code>pcall</code>：仅限 x86：显示保护模式下的远程调用/返回/异常</li>
<li><code>cpu_reset</code>：在 CPU 重置前显示 CPU 状态</li>
<li><code>unimp</code>：记录未实现的功能</li>
<li><code>guest_errors</code>：当来宾操作系统执行无效操作（例如访问不存在的寄存器）时记录日志</li>
<li><code>page</code>：在用户模式仿真开始时转储页面</li>
<li><code>nochain</code>：不链式编译基本块，以便“exec”和“cpu”显示完整的跟踪</li>
<li><code>plugin</code>：输出来自 TCG 插件的信息</li>
<li><code>strace</code>：记录每个用户模式系统调用、其输入和结果</li>
<li><code>trace:PATTERN</code>：启用跟踪事件</li>
</ul>
<p>使用 <code>&quot;-d trace:help&quot;</code> 来获取跟踪事件列表。</p>
<h2 id="qemu-system-xxx-常用相关参数">qemu-system-xxx 常用相关参数</h2>
<ul>
<li>-M：指定 machine，-help 可以列出所有所支持的 machine</li>
<li>-cpu：指定模拟的 CPU 型号，例如 <code>cortex-a57</code>、<code>cortex-a53</code> 等。</li>
<li>-smp：CPU 核数</li>
<li>-m：RAM 容量</li>
<li>-kernel：Linux kernel 文件</li>
<li>-append：Linux Kernel 的 bootargs。这个命令的参数很复杂，具体可以参考 kernel 文档。</li>
<li>-console：设备名必须和 machine 的串口一致，否则会看不到 kernel log。而不同的平台 console 名称都是不一样的，这是 Kernel 很不友好的一点。</li>
<li>-ignore_loglevel：可以让你看到尽可能多的 kernel log，当然也会减慢 kernel 的启动速度</li>
<li>-init：必须保证 initrd 里有/linuxrc 这个文件，否则会无法启动 shell</li>
<li>-initrd：指定 initrd 文件</li>
</ul>
<h2 id="快照管理">快照管理</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 拍快照</span>
</span></span><span class="line"><span class="cl">qemu-img snapshot -c oe-rv-snapshot1  openEuler-22.09-riscv64-qemu.qcow2
</span></span><span class="line"><span class="cl"><span class="c1"># 列举快照</span>
</span></span><span class="line"><span class="cl">qemu-img snapshot-l openEuler-22.09-riscv64-qemu.qcow2
</span></span><span class="line"><span class="cl"><span class="c1"># 恢复快照</span>
</span></span><span class="line"><span class="cl">qemu-img snapshot -a my_snapshot mydisk.qcow2
</span></span></code></pre></div><h2 id="查看虚拟硬盘信息">查看虚拟硬盘信息</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-img info /path/to/image
</span></span></code></pre></div><h2 id="将虚拟硬盘转换为-qcow2-格式">将虚拟硬盘转换为 QCOW2 格式</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-img convert -O qcow2 /path/to/image /path/to/new/image
</span></span></code></pre></div><h3 id="调整磁盘大小">调整磁盘大小</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-img resize ubuntu.qcow2 +5G
</span></span></code></pre></div><h2 id="显示器选项">显示器选项</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -vga std
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -display sdl
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -display gtk
</span></span></code></pre></div><ul>
<li>-vga std：使用标准 VGA 显示器</li>
<li>-display sdl：使用 SDL 显示器</li>
<li>-display gtk：使用 GTK 显示器</li>
</ul>
<h2 id="输入选项">输入选项</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -k en-us
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -usb
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -device usb-mouse
</span></span></code></pre></div><ul>
<li>-k en-us：使用英文键盘布局</li>
<li>-usb：启用 USB 支持</li>
<li>-device usb-mouse：使用 USB 鼠标设备</li>
</ul>
<h2 id="声音选项">声音选项</h2>
<p>以下命令用于配置虚拟机的声音：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -soundhw all
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -soundhw sb16
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -audiodev pa,id<span class="o">=</span>pa1,out.mixing-engine<span class="o">=</span>off
</span></span></code></pre></div><ul>
<li>-soundhw all：启用所有声卡</li>
<li>-soundhw sb16：启用 SoundBlaster 16 声卡</li>
<li>-audiodev pa,id=pa1,out.mixing-engine=off：使用 PulseAudio 声音设备</li>
</ul>
<h2 id="usb-设备管理">USB 设备管理</h2>
<p>以下命令用于管理虚拟机的 USB 设备：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -usbdevice host:1234:5678
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -usbdevice tablet
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -usbdevice keyboard
</span></span></code></pre></div><ul>
<li>-usbdevice host🔢5678：将主机的 USB 设备 1234:5678 分配给虚拟机</li>
<li>-usbdevice tablet：使用 USB 触摸板</li>
<li>-usbdevice keyboard：使用 USB 键盘</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>小白环法观赛指南</title>
      <link>https://lifeislife.cn/posts/%E5%B0%8F%E7%99%BD%E7%8E%AF%E6%B3%95%E8%A7%82%E8%B5%9B%E6%8C%87%E5%8D%97/</link>
      <pubDate>Tue, 02 Jul 2024 13:51:31 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%B0%8F%E7%99%BD%E7%8E%AF%E6%B3%95%E8%A7%82%E8%B5%9B%E6%8C%87%E5%8D%97/</guid>
      <description>&lt;h2 id=&#34;常见术语&#34;&gt;常见术语&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;黄衫（Yellow jersey/Maillot Jaune）
黄衫是总成绩领先者的象征，穿上黄衫的车手就是比赛的领头羊。&lt;/li&gt;
&lt;li&gt;绿衫（Green jersey/Maillot Vert）
绿衫代表冲刺积分的领先者，通常由擅长平地冲刺的车手穿着。&lt;/li&gt;
&lt;li&gt;圆点衫（Polka dot jersey/Maillot à Pois Rouges）
圆点衫授予爬坡积分最高的车手，爬坡能力强的车手更有机会获得。&lt;/li&gt;
&lt;li&gt;白衫（White jersey/Maillot Blanc）
白衫是最佳年轻车手（25岁以下）的象征，表现优异的年轻车手会穿上白衫。&lt;/li&gt;
&lt;li&gt;突围/兔子（Breakaway）
指比赛中一小部分车手从主集团（Peloton）中突围出来，尝试抢占领先位置。&lt;/li&gt;
&lt;li&gt;主集团（Peloton）
主集团是比赛中最大的车手队伍，大部分车手都会待在主集团里，节省体力。&lt;/li&gt;
&lt;li&gt;冲刺（Sprint）
冲刺是比赛最后几百米的激烈竞争，冲刺手（Sprinter）们会在这时全力以赴，争夺赛段冠军。&lt;/li&gt;
&lt;li&gt;掉队（Dropped）
被其他选手甩在后面，也被称为off the back或者out the back。&lt;/li&gt;
&lt;li&gt;计时赛（Time Trial）
计时赛是车手单独出发，按时间计成绩的比赛形式，包括个人计时赛（ITT）和团队计时赛（TTT）。&lt;/li&gt;
&lt;li&gt;总成绩（General Classification, GC）
总成绩是根据车手每个赛段的时间累加计算的，最终总时间最少的车手将获得黄衫。&lt;/li&gt;
&lt;li&gt;皇后赛段（Queen Stage）
指的是能够决定多日赛最终排名的关键性山地赛段，通常有数座难度很大的山峰。&lt;/li&gt;
&lt;li&gt;火车（Paceline）
指的是队友在前面做苦力，保护主将，为主将节省体力的战术。（对应的有冲刺火车或是山地火车）&lt;/li&gt;
&lt;li&gt;跟风/蹭风/吸尾流（Draft）
在高速骑行过程中，车手的大部分体力是消耗在对抗空气阻力（风阻）上。因此会有车手骑在其他人或车子后方，来降低风阻，节省体力。&lt;/li&gt;
&lt;li&gt;粘瓶（Sticky Bpttle）
选手在拿补给车的水壶时，会短暂获得补给车所带来的助力，以节省体力，通常三五秒之内都是被允许的，如果时间太长被裁判发现将会被罚款甚至取消成绩。&lt;/li&gt;
&lt;li&gt;关门时间（Time limit）
在每天的比赛中，车手们必须在不超过当日赛段冠军用时的一定比例时间内完赛，否则将被取消比赛资格，不被获准在第二天的比赛时发车；确切的比例取决于赛段的类型、地形和速度；速度快时，在平地赛段中，比例会低至5%，而速度慢时，在山地赛段中，比例会达到16、17%。在某些情况下，赛事组织者有权酌情赦免一些车手，比如当有过大一部分车手无法在关门时间内完赛时。历史上也有过特例，一些大牌冲刺手被关门，然后组委会以扣冲刺积分的形式来放他们一马。&lt;/li&gt;
&lt;li&gt;爬坡点等级（Mountain Climb Classification）
比赛中设置的爬坡抢分点，难度与级数由低至高为4、3、2、1、HC（顶级）；越高的级数代表爬坡长度越长、坡度越陡。&lt;/li&gt;
&lt;li&gt;补给区 Feed Zone
比赛中设置一个区域，让车队提供车手饮食补给袋的地方。通常，车手不会在这个区域发动攻击。&lt;/li&gt;
&lt;li&gt;垃圾丢弃区（Collection Zone）
UCI规定只能在固定的垃圾丢弃区扔垃圾，否则会被罚款。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;自行车战术&#34;&gt;自行车战术&lt;/h2&gt;
&lt;p&gt;油管上有个台湾博主叫做&lt;a href=&#34;https://www.youtube.com/watch?v=f1ncgvjB_z4&amp;amp;list=PLa5LB6K0vQT3CzQNp7traSqvvp4Sn_uaQ&#34;&gt;《喂我阿维》做了一个自行车战术的系列视频&lt;/a&gt;，这个系列的视频选择了一些实战的战况并结合了三维动画来解释各种战术，非常直观易懂，推荐大家观看。&lt;/p&gt;
&lt;h2 id=&#34;环法观赛中小白常问的问题&#34;&gt;环法观赛中小白常问的问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;为何约纳斯温格高（Jonas Vingegaard Rasmussen）外号鲨鱼佬？
温格高在进入职业比赛之前曾在鱼类加工厂干过一年左右的兼职，所以粉丝给他起了外号sha鱼佬。因为鲨鱼和sha鱼发音相同，所以就有了这个外号。其实也是为了规避社交平台的敏感词。&lt;/li&gt;
&lt;li&gt;为何提到波加查就会提到芬达？叫他芬达超人？
因为其他车手结束比赛大多会喝纯净水或者一些电解质饮料，或者一些我们不认识的功能饮料。而波加查喜欢比赛后喝芬达。不过今年很少看到他喝芬达了，不知道是不是没要到赞助费（狗头保命）。&lt;/li&gt;
&lt;li&gt;每次比赛都有四五个小时，车手怎么上厕所？
当然是站着解决了，哈哈。比赛开始时车速较慢，还可以停车在路边解决。比赛中段车速会很快，一旦停车将很难追上，通常这时车手会边骑边放水，如果你在直播中看到有在边上骑车并且骑行姿势奇怪的车手，那么他可能在放水。&lt;/li&gt;
&lt;li&gt;环法中有中国的车队吗？
很可惜，没有。目前也只有一位中国车手完成过环法，是在2014年来自捷安特·禧玛诺车队中国车手计成，以第164名，也是最后一名完成比赛。&lt;/li&gt;
&lt;li&gt;有的赛段在终点前许多车手为什么向路边扔水壶？
为了减轻负重，提高冲刺速度。&lt;/li&gt;
&lt;li&gt;卡文迪什（Mark CAVENDISH）外号由来？
&lt;ol&gt;
&lt;li&gt;盘爷：取自名字（CAVENDISH）的最后四个字母（Dish）。&lt;/li&gt;
&lt;li&gt;曼岛飞弹：因为卡文迪什是土生土长的曼岛人，并且速度飞快（曼岛以摩托赛曼岛TT著称）。&lt;/li&gt;
&lt;li&gt;卡胖：卡文迪什不擅爬坡，以及每逢佳节胖三斤的体质，为他招来了这个“雅号”。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;为何不想争赛段的车队也会在前面带风？为何要把主将带到最后三公里？
首先在集团前方相对来说较为安全，因为集团后方只能看到前面车手的屁股，对突发情况难以预判。其次因为在最后三公里发生摔车或者机械故障，成绩按照大集团的过线成绩计算，不会丢时间。
更新：UCI 已决定允许组织者和其他利益相关者试行要求修改所谓的“三公里”（或“冲刺区”）规则（《UCI 规则》第 2.6. 027 条），该规则适用于以下情况：比赛进入最后冲刺区，根据该规则，如果在公路赛段的最后三公里（不包括山顶终点）发生适当注意到的事件（例如跌倒、机械问题或爆胎），受影响的骑手的时间记入事件发生时与他一起骑车的骑手的时间。如果有正当理由，提出请求的组织者（或其他利益相关者）可以延长上述规则所考虑的距离，最多可以增加到五公里。任何变更都必须在比赛开始前获得同意。&lt;/li&gt;
&lt;li&gt;环法是不是只有一条线路？环绕法国国界吗？
不是，环法多个赛段，并且环法每年的线路都不一样，今年2024年就有21个赛段，并且也不是环绕法国边境线，有时候会经过法国以外的国家，比如今年2024年，就是从意大利的佛罗伦萨发车。&lt;/li&gt;
&lt;li&gt;比赛中的兔子是什么意思？为何有风不蹭要自己顶风骑？
之前回复哔站的一位的提问。兔子就是突围的车手，就是冲在最前面远离大集团的车手。每个车队在每场比赛中的战术不同这个目的就太多了，最简单的就是一个车手觉得自己今天状态特别好，那他就可以冲出去为了争夺赛段冠军，如果跟着大集团的话最后的冲刺会很难取胜。也有的车队想要拉赞助获得更高的曝光，那就会安排车手突围，这样可以获得更多的镜头增加曝光量。还有就是比赛一般都有荣誉衫，荣誉衫不仅仅只有成绩第一才有，也有冲刺得分最多的，爬坡得分最多的，那么就会有突围车手冲出去抢夺这些积分获得荣誉衫。再有就是只是为了打乱竞争对手的节奏，因为你突围的话，竞争对手就需要消耗大量体力追赶，很容易乱了节奏。当然你也肯定听过空中加油这个战术，在使用这个战术时就需要兔子这个角色，一般是实力较强的副将突围，在爬坡之前等自己的主将（在等待过程中就会降速恢复体力），然后在爬坡时为他破风，因为这时候竞争对手在追兔子的时候已经消耗了体力，而兔子已经稍有恢复，这时候就能将主将发射出去。&lt;/li&gt;
&lt;li&gt;车手把立位置是显示屏吗？还是一张贴纸？
是贴纸，标注了简单的赛段信息，最重要的是补给时间，比如图2就是第四赛段波加查车上贴的贴纸，标注了海拔图，还有在哪些位置进行补给。码表一般放在把立前方，突出的部分，有个架子。&lt;/li&gt;
&lt;li&gt;什么是十公里党？
就是只看比赛最后10公里的观众，因为平路赛段通常比较无聊，不会有什么进攻，只有最后几公里才有一些动作。&lt;/li&gt;
&lt;li&gt;个人计时赛（ITT）如何进行的？
所有未退赛的选手，都会参加。发车顺序是当前总成绩的倒序。发车间隔不确定（可能UCI有规定如果有知道具体计算方式的欢迎评论区告知），2024年环法第7赛段发车间隔为1分钟。和其他赛段不同，个人计时赛没有队友和其他人破风，完全需要比拼个人实力，用时最短者获胜。此外还有团队计时赛（TTT），是以车队为单位间隔发车，比拼车队整体实力，2024年环法没有团队计时赛。&lt;/li&gt;
&lt;li&gt;直播中屏幕上方的时间差是什么意思？
上方标志可以查看集团信息，黄色高亮框表示当然画面是哪个集团，比如现在就是表示画面是大集团。集团标题可能为荣誉衫的法语名称（比如Maillot Jaune是黄衫，Gr.是Group的缩写），领头羊（Tête de la Course，表示这个集团从开始就突围并一直保持领先未被追上过），明星车手的名字（比如图中的Carapaz和Johannessen），大集团（Peloton），追击集团（Chase）等等。显示的时间表示与突围集团的差距，所有的时间都是相对于突围集团的，不是相对于它的前一个集团。比如当前大集团比突围集团慢了约三分钟三十四秒，卡拉帕兹比突围集团慢了约十七秒。&lt;/li&gt;
&lt;li&gt;破风手在比赛前就定好了吗。每场比赛会轮流吗？
不是定好的，但是每个车队都会赛前定战术，赛场上变化很多，赛中车手也会听无线电里车队主管的安排。有一点是定的，主将几乎不会参与破风。&lt;/li&gt;
&lt;li&gt;最佳车队奖是如何评比的？
每个赛段结束后，根据每个车队的前三名最好成绩，计算车队的总成绩。时间最少的车队获得最佳车队奖。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;赞助商赞助的资源是否包含你提到的这些角色，是肯定包含的。但是具体赞助了什么，赞助多少，如何分配，也算是商业机密吧，我就不得而知了。我也分享一些我了解的信息。赞助商投入最大的还是在自行车上。每辆自行车至少都在一万美元以上。每个赛季都要投入一两百辆的车，这些装备肯定都是分配给车手的，此外赞助商通常还会赞助各个车队全球征战的差旅费，这就肯定包含所有后勤保障人员了。这里插一个有意思的，车队赞助商的投入不仅可以从车手装备看出差异，后勤保障车上也能管中窥豹。去年的冠军车队visma车队大巴堪比移动堡垒，移动之家，而现在只是职业车队乐透车队(Visma车队是世界级车队，比职业车队高一级)的大巴就略显“寒酸”了。感兴趣的话可以油管搜索关键词Inside Team bus。看看各个车队大巴的内部豪华设施，还是蛮有意思的。
关于赞助商的内容，我也是外行人，我给你发了一个链接，可以了解更多关于赞助的内容。也感谢你的提问，让我也学到了很多。&lt;/p&gt;
&lt;p&gt;第二个问题是关于如何获得比赛奖金以及奖金如何分配的。如何获得奖金，感兴趣的话可以直接站内搜索应该有朋友做总结，我就简单概括一下吧，奖金除了和比赛排名相关，和荣誉衫也是息息相关的。1. 最后总成绩的前三名肯定是有奖金的（第一名可以获得50万欧元的奖金），其实每个完赛的选手都有奖金。2. 每个赛段也会根据排名发放奖金，但是通常就只有前20名会有奖金。3. 每个荣誉衫每穿一天都会获得一次奖金，比如爬坡王圆点衫，每穿一天就能获得300欧元。4. 赛段中如果有标注等级的爬坡，那么率先通过这个爬坡的车手也会获得奖金。5. 组委会会为每个赛段中突围，进攻最积极的选手颁发敢斗奖，并在下个赛段用红色号码牌标记。拿敢斗奖也会获得2000欧元的奖金。6. 除了个人奖金，还有最佳车队奖，每一赛段结束后，根据每个车队的前三名最好成绩，计算车队的总成绩。时间最少的车队获得最佳车队奖（2800欧元）。7. 此外需要提醒的是，奖金只会发给真正获得荣誉衫的人，如果是顺延代穿荣誉衫，是不会获得奖金的。&lt;/p&gt;
&lt;p&gt;再说说奖金的分配，奖金从面子上讲，他是归属获得者个人的（最佳团队奖除外），但是里子上通常会根据内部规定进行分配。具体如何分配我也不够专业，就不误导你了。但是有一点你说的肯定没错，获奖一般离不开其他队友的帮助，肯定会按出力程度来分配。至于环法组委会如何发放奖金，发放到哪里，我也不太清楚，如果你有找到相关资料，欢迎分享给我。&lt;/p&gt;
&lt;h2 id=&#34;观赛工具&#34;&gt;观赛工具&lt;/h2&gt;
&lt;h3 id=&#34;环法官网&#34;&gt;&lt;a href=&#34;https://www.letour.fr/en/&#34;&gt;环法官网&lt;/a&gt;&lt;/h3&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//2024/07/07/8fd200df9d7f4927af93b6a2682ee206.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/8fd200df9d7f4927af93b6a2682ee206.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//2024/07/07/85781085ead7d1a2856045f393d59806.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/85781085ead7d1a2856045f393d59806.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//2024/07/07/cb422f01dbef804b065c91ba9ccf39f6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/cb422f01dbef804b065c91ba9ccf39f6.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//2024/07/07/01a2fd9a440f75c0a4408fd5b647c7bf.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/01a2fd9a440f75c0a4408fd5b647c7bf.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//2024/07/07/0c70407a4e8cef8a1c4936a62da7a566.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/0c70407a4e8cef8a1c4936a62da7a566.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;tour-tracker&#34;&gt;Tour Tracker&lt;/h3&gt;
&lt;p&gt;网页版：https://live.thetourtracker.com/&lt;/p&gt;
&lt;p&gt;APP版本：Google Play Store 和 Apple App Store 搜索 Tour Tracker。&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//2024/07/07/419c3b57b5e4e60f90e43f89bb2ba9b4.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/419c3b57b5e4e60f90e43f89bb2ba9b4.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//2024/07/07/dc727ffe4f928618451b2025d6d0b368.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/dc727ffe4f928618451b2025d6d0b368.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//2024/07/07/2c700c2c9c1472910d0866c24bdab9c4.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/2c700c2c9c1472910d0866c24bdab9c4.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//2024/07/07/94caa25c7ada5b0fc3b2ac9b62c7b8fd.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/94caa25c7ada5b0fc3b2ac9b62c7b8fd.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//2024/07/07/670d46e6993018f2672960004f9d65e7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/670d46e6993018f2672960004f9d65e7.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//2024/07/07/c5cdc450fe4c7fa5f1ec8c99eaf911ff.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/c5cdc450fe4c7fa5f1ec8c99eaf911ff.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//2024/07/07/6c099edf9dbd414c1aa8ab63c3246d7d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/6c099edf9dbd414c1aa8ab63c3246d7d.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//2024/07/07/d49747d9aad65e6cca31fad06b72fc0e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/d49747d9aad65e6cca31fad06b72fc0e.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//2024/07/07/2a8b7fe406cf72de8ff9378bcddd5bfa.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/2a8b7fe406cf72de8ff9378bcddd5bfa.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//2024/07/07/3eab6470657e244cfe696065884c9936.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/3eab6470657e244cfe696065884c9936.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//2024/07/07/a4881c186d1653a941978e539ca5be27.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/a4881c186d1653a941978e539ca5be27.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;h2 id=&#34;奥运会公路车概况&#34;&gt;奥运会公路车概况&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;男子个人计时赛没有中国选手参加，但是男子公路赛有吕先景一位中国选手参加。吕先景目前效力于中国华兴-铭普洲际车队。2018年雅加达亚运会上，他获得男子山地自行车银牌，2019年环中国自行车赛，获得第二赛段冠军。期待吕先景在奥运会上取得好成绩。（PS：不用惊讶哦，是有环中国比赛的，只是2019年之后因为疫情就停办了，并且也没有复办的消息）&lt;/li&gt;
&lt;li&gt;女子个人计时赛有中国选手唐欣参加，同时她还会参加女子公路赛。她是中国第一位参加巴黎鲁贝女子自行车上并完赛的车手，也是中国第一位参加环西班牙女子自行车赛并完赛的车手，期待她在奥运会上取得好成绩。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;比赛规则简述&#34;&gt;比赛规则简述&lt;/h3&gt;
&lt;p&gt;男女个人计时赛&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一场定胜负的比赛，运动员在规定的时间内完成比赛，成绩最好的运动员获得金牌；&lt;/li&gt;
&lt;li&gt;运动员按照规定的间隔时间单独出发，以运动员到达终点的成绩优劣排名；&lt;/li&gt;
&lt;li&gt;发车时由同一名裁判员扶车；&lt;/li&gt;
&lt;li&gt;间隔1-2分钟发车，具体发车时间可在Tour Tracker，奥林匹克官网，ProCyclingStats等网站查看；&lt;/li&gt;
&lt;li&gt;即使追上了前面发车的车手，也不能相互领骑；&lt;/li&gt;
&lt;li&gt;在超车以及被超越过程中，运动员不得进入对手左右2米，前后25米的空间；&lt;/li&gt;
&lt;li&gt;队车不得在运动员前方行驶，只能在运动员后方25米外行驶；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;男女公路赛&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一场定胜负的比赛，运动员在规定的时间内完成比赛，成绩最好的运动员获得金牌；&lt;/li&gt;
&lt;li&gt;发车位置按照签到顺序确定；&lt;/li&gt;
&lt;li&gt;比赛中可以跟风，可以互相帮助，交换食物和水以及工具等，但是不能相互推行；&lt;/li&gt;
&lt;li&gt;在赛程的前50公里和进入最后20公里后，不得进行补给，赛道上会有标识补给开始区域和补给结束区域；&lt;/li&gt;
&lt;li&gt;队车不得超越运动员，只有裁判同意才可以；&lt;/li&gt;
&lt;/ol&gt;
</description>
      <content:encoded><![CDATA[<h2 id="常见术语">常见术语</h2>
<ol>
<li>黄衫（Yellow jersey/Maillot Jaune）
黄衫是总成绩领先者的象征，穿上黄衫的车手就是比赛的领头羊。</li>
<li>绿衫（Green jersey/Maillot Vert）
绿衫代表冲刺积分的领先者，通常由擅长平地冲刺的车手穿着。</li>
<li>圆点衫（Polka dot jersey/Maillot à Pois Rouges）
圆点衫授予爬坡积分最高的车手，爬坡能力强的车手更有机会获得。</li>
<li>白衫（White jersey/Maillot Blanc）
白衫是最佳年轻车手（25岁以下）的象征，表现优异的年轻车手会穿上白衫。</li>
<li>突围/兔子（Breakaway）
指比赛中一小部分车手从主集团（Peloton）中突围出来，尝试抢占领先位置。</li>
<li>主集团（Peloton）
主集团是比赛中最大的车手队伍，大部分车手都会待在主集团里，节省体力。</li>
<li>冲刺（Sprint）
冲刺是比赛最后几百米的激烈竞争，冲刺手（Sprinter）们会在这时全力以赴，争夺赛段冠军。</li>
<li>掉队（Dropped）
被其他选手甩在后面，也被称为off the back或者out the back。</li>
<li>计时赛（Time Trial）
计时赛是车手单独出发，按时间计成绩的比赛形式，包括个人计时赛（ITT）和团队计时赛（TTT）。</li>
<li>总成绩（General Classification, GC）
总成绩是根据车手每个赛段的时间累加计算的，最终总时间最少的车手将获得黄衫。</li>
<li>皇后赛段（Queen Stage）
指的是能够决定多日赛最终排名的关键性山地赛段，通常有数座难度很大的山峰。</li>
<li>火车（Paceline）
指的是队友在前面做苦力，保护主将，为主将节省体力的战术。（对应的有冲刺火车或是山地火车）</li>
<li>跟风/蹭风/吸尾流（Draft）
在高速骑行过程中，车手的大部分体力是消耗在对抗空气阻力（风阻）上。因此会有车手骑在其他人或车子后方，来降低风阻，节省体力。</li>
<li>粘瓶（Sticky Bpttle）
选手在拿补给车的水壶时，会短暂获得补给车所带来的助力，以节省体力，通常三五秒之内都是被允许的，如果时间太长被裁判发现将会被罚款甚至取消成绩。</li>
<li>关门时间（Time limit）
在每天的比赛中，车手们必须在不超过当日赛段冠军用时的一定比例时间内完赛，否则将被取消比赛资格，不被获准在第二天的比赛时发车；确切的比例取决于赛段的类型、地形和速度；速度快时，在平地赛段中，比例会低至5%，而速度慢时，在山地赛段中，比例会达到16、17%。在某些情况下，赛事组织者有权酌情赦免一些车手，比如当有过大一部分车手无法在关门时间内完赛时。历史上也有过特例，一些大牌冲刺手被关门，然后组委会以扣冲刺积分的形式来放他们一马。</li>
<li>爬坡点等级（Mountain Climb Classification）
比赛中设置的爬坡抢分点，难度与级数由低至高为4、3、2、1、HC（顶级）；越高的级数代表爬坡长度越长、坡度越陡。</li>
<li>补给区 Feed Zone
比赛中设置一个区域，让车队提供车手饮食补给袋的地方。通常，车手不会在这个区域发动攻击。</li>
<li>垃圾丢弃区（Collection Zone）
UCI规定只能在固定的垃圾丢弃区扔垃圾，否则会被罚款。</li>
</ol>
<h2 id="自行车战术">自行车战术</h2>
<p>油管上有个台湾博主叫做<a href="https://www.youtube.com/watch?v=f1ncgvjB_z4&amp;list=PLa5LB6K0vQT3CzQNp7traSqvvp4Sn_uaQ">《喂我阿维》做了一个自行车战术的系列视频</a>，这个系列的视频选择了一些实战的战况并结合了三维动画来解释各种战术，非常直观易懂，推荐大家观看。</p>
<h2 id="环法观赛中小白常问的问题">环法观赛中小白常问的问题</h2>
<ol>
<li>为何约纳斯温格高（Jonas Vingegaard Rasmussen）外号鲨鱼佬？
温格高在进入职业比赛之前曾在鱼类加工厂干过一年左右的兼职，所以粉丝给他起了外号sha鱼佬。因为鲨鱼和sha鱼发音相同，所以就有了这个外号。其实也是为了规避社交平台的敏感词。</li>
<li>为何提到波加查就会提到芬达？叫他芬达超人？
因为其他车手结束比赛大多会喝纯净水或者一些电解质饮料，或者一些我们不认识的功能饮料。而波加查喜欢比赛后喝芬达。不过今年很少看到他喝芬达了，不知道是不是没要到赞助费（狗头保命）。</li>
<li>每次比赛都有四五个小时，车手怎么上厕所？
当然是站着解决了，哈哈。比赛开始时车速较慢，还可以停车在路边解决。比赛中段车速会很快，一旦停车将很难追上，通常这时车手会边骑边放水，如果你在直播中看到有在边上骑车并且骑行姿势奇怪的车手，那么他可能在放水。</li>
<li>环法中有中国的车队吗？
很可惜，没有。目前也只有一位中国车手完成过环法，是在2014年来自捷安特·禧玛诺车队中国车手计成，以第164名，也是最后一名完成比赛。</li>
<li>有的赛段在终点前许多车手为什么向路边扔水壶？
为了减轻负重，提高冲刺速度。</li>
<li>卡文迪什（Mark CAVENDISH）外号由来？
<ol>
<li>盘爷：取自名字（CAVENDISH）的最后四个字母（Dish）。</li>
<li>曼岛飞弹：因为卡文迪什是土生土长的曼岛人，并且速度飞快（曼岛以摩托赛曼岛TT著称）。</li>
<li>卡胖：卡文迪什不擅爬坡，以及每逢佳节胖三斤的体质，为他招来了这个“雅号”。</li>
</ol>
</li>
<li>为何不想争赛段的车队也会在前面带风？为何要把主将带到最后三公里？
首先在集团前方相对来说较为安全，因为集团后方只能看到前面车手的屁股，对突发情况难以预判。其次因为在最后三公里发生摔车或者机械故障，成绩按照大集团的过线成绩计算，不会丢时间。
更新：UCI 已决定允许组织者和其他利益相关者试行要求修改所谓的“三公里”（或“冲刺区”）规则（《UCI 规则》第 2.6. 027 条），该规则适用于以下情况：比赛进入最后冲刺区，根据该规则，如果在公路赛段的最后三公里（不包括山顶终点）发生适当注意到的事件（例如跌倒、机械问题或爆胎），受影响的骑手的时间记入事件发生时与他一起骑车的骑手的时间。如果有正当理由，提出请求的组织者（或其他利益相关者）可以延长上述规则所考虑的距离，最多可以增加到五公里。任何变更都必须在比赛开始前获得同意。</li>
<li>环法是不是只有一条线路？环绕法国国界吗？
不是，环法多个赛段，并且环法每年的线路都不一样，今年2024年就有21个赛段，并且也不是环绕法国边境线，有时候会经过法国以外的国家，比如今年2024年，就是从意大利的佛罗伦萨发车。</li>
<li>比赛中的兔子是什么意思？为何有风不蹭要自己顶风骑？
之前回复哔站的一位的提问。兔子就是突围的车手，就是冲在最前面远离大集团的车手。每个车队在每场比赛中的战术不同这个目的就太多了，最简单的就是一个车手觉得自己今天状态特别好，那他就可以冲出去为了争夺赛段冠军，如果跟着大集团的话最后的冲刺会很难取胜。也有的车队想要拉赞助获得更高的曝光，那就会安排车手突围，这样可以获得更多的镜头增加曝光量。还有就是比赛一般都有荣誉衫，荣誉衫不仅仅只有成绩第一才有，也有冲刺得分最多的，爬坡得分最多的，那么就会有突围车手冲出去抢夺这些积分获得荣誉衫。再有就是只是为了打乱竞争对手的节奏，因为你突围的话，竞争对手就需要消耗大量体力追赶，很容易乱了节奏。当然你也肯定听过空中加油这个战术，在使用这个战术时就需要兔子这个角色，一般是实力较强的副将突围，在爬坡之前等自己的主将（在等待过程中就会降速恢复体力），然后在爬坡时为他破风，因为这时候竞争对手在追兔子的时候已经消耗了体力，而兔子已经稍有恢复，这时候就能将主将发射出去。</li>
<li>车手把立位置是显示屏吗？还是一张贴纸？
是贴纸，标注了简单的赛段信息，最重要的是补给时间，比如图2就是第四赛段波加查车上贴的贴纸，标注了海拔图，还有在哪些位置进行补给。码表一般放在把立前方，突出的部分，有个架子。</li>
<li>什么是十公里党？
就是只看比赛最后10公里的观众，因为平路赛段通常比较无聊，不会有什么进攻，只有最后几公里才有一些动作。</li>
<li>个人计时赛（ITT）如何进行的？
所有未退赛的选手，都会参加。发车顺序是当前总成绩的倒序。发车间隔不确定（可能UCI有规定如果有知道具体计算方式的欢迎评论区告知），2024年环法第7赛段发车间隔为1分钟。和其他赛段不同，个人计时赛没有队友和其他人破风，完全需要比拼个人实力，用时最短者获胜。此外还有团队计时赛（TTT），是以车队为单位间隔发车，比拼车队整体实力，2024年环法没有团队计时赛。</li>
<li>直播中屏幕上方的时间差是什么意思？
上方标志可以查看集团信息，黄色高亮框表示当然画面是哪个集团，比如现在就是表示画面是大集团。集团标题可能为荣誉衫的法语名称（比如Maillot Jaune是黄衫，Gr.是Group的缩写），领头羊（Tête de la Course，表示这个集团从开始就突围并一直保持领先未被追上过），明星车手的名字（比如图中的Carapaz和Johannessen），大集团（Peloton），追击集团（Chase）等等。显示的时间表示与突围集团的差距，所有的时间都是相对于突围集团的，不是相对于它的前一个集团。比如当前大集团比突围集团慢了约三分钟三十四秒，卡拉帕兹比突围集团慢了约十七秒。</li>
<li>破风手在比赛前就定好了吗。每场比赛会轮流吗？
不是定好的，但是每个车队都会赛前定战术，赛场上变化很多，赛中车手也会听无线电里车队主管的安排。有一点是定的，主将几乎不会参与破风。</li>
<li>最佳车队奖是如何评比的？
每个赛段结束后，根据每个车队的前三名最好成绩，计算车队的总成绩。时间最少的车队获得最佳车队奖。</li>
</ol>
<p>赞助商赞助的资源是否包含你提到的这些角色，是肯定包含的。但是具体赞助了什么，赞助多少，如何分配，也算是商业机密吧，我就不得而知了。我也分享一些我了解的信息。赞助商投入最大的还是在自行车上。每辆自行车至少都在一万美元以上。每个赛季都要投入一两百辆的车，这些装备肯定都是分配给车手的，此外赞助商通常还会赞助各个车队全球征战的差旅费，这就肯定包含所有后勤保障人员了。这里插一个有意思的，车队赞助商的投入不仅可以从车手装备看出差异，后勤保障车上也能管中窥豹。去年的冠军车队visma车队大巴堪比移动堡垒，移动之家，而现在只是职业车队乐透车队(Visma车队是世界级车队，比职业车队高一级)的大巴就略显“寒酸”了。感兴趣的话可以油管搜索关键词Inside Team bus。看看各个车队大巴的内部豪华设施，还是蛮有意思的。
关于赞助商的内容，我也是外行人，我给你发了一个链接，可以了解更多关于赞助的内容。也感谢你的提问，让我也学到了很多。</p>
<p>第二个问题是关于如何获得比赛奖金以及奖金如何分配的。如何获得奖金，感兴趣的话可以直接站内搜索应该有朋友做总结，我就简单概括一下吧，奖金除了和比赛排名相关，和荣誉衫也是息息相关的。1. 最后总成绩的前三名肯定是有奖金的（第一名可以获得50万欧元的奖金），其实每个完赛的选手都有奖金。2. 每个赛段也会根据排名发放奖金，但是通常就只有前20名会有奖金。3. 每个荣誉衫每穿一天都会获得一次奖金，比如爬坡王圆点衫，每穿一天就能获得300欧元。4. 赛段中如果有标注等级的爬坡，那么率先通过这个爬坡的车手也会获得奖金。5. 组委会会为每个赛段中突围，进攻最积极的选手颁发敢斗奖，并在下个赛段用红色号码牌标记。拿敢斗奖也会获得2000欧元的奖金。6. 除了个人奖金，还有最佳车队奖，每一赛段结束后，根据每个车队的前三名最好成绩，计算车队的总成绩。时间最少的车队获得最佳车队奖（2800欧元）。7. 此外需要提醒的是，奖金只会发给真正获得荣誉衫的人，如果是顺延代穿荣誉衫，是不会获得奖金的。</p>
<p>再说说奖金的分配，奖金从面子上讲，他是归属获得者个人的（最佳团队奖除外），但是里子上通常会根据内部规定进行分配。具体如何分配我也不够专业，就不误导你了。但是有一点你说的肯定没错，获奖一般离不开其他队友的帮助，肯定会按出力程度来分配。至于环法组委会如何发放奖金，发放到哪里，我也不太清楚，如果你有找到相关资料，欢迎分享给我。</p>
<h2 id="观赛工具">观赛工具</h2>
<h3 id="环法官网"><a href="https://www.letour.fr/en/">环法官网</a></h3>
<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//2024/07/07/8fd200df9d7f4927af93b6a2682ee206.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/8fd200df9d7f4927af93b6a2682ee206.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//2024/07/07/85781085ead7d1a2856045f393d59806.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/85781085ead7d1a2856045f393d59806.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//2024/07/07/cb422f01dbef804b065c91ba9ccf39f6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/cb422f01dbef804b065c91ba9ccf39f6.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//2024/07/07/01a2fd9a440f75c0a4408fd5b647c7bf.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/01a2fd9a440f75c0a4408fd5b647c7bf.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//2024/07/07/0c70407a4e8cef8a1c4936a62da7a566.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/0c70407a4e8cef8a1c4936a62da7a566.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="tour-tracker">Tour Tracker</h3>
<p>网页版：https://live.thetourtracker.com/</p>
<p>APP版本：Google Play Store 和 Apple App Store 搜索 Tour Tracker。</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//2024/07/07/419c3b57b5e4e60f90e43f89bb2ba9b4.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/419c3b57b5e4e60f90e43f89bb2ba9b4.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//2024/07/07/dc727ffe4f928618451b2025d6d0b368.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/dc727ffe4f928618451b2025d6d0b368.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//2024/07/07/2c700c2c9c1472910d0866c24bdab9c4.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/2c700c2c9c1472910d0866c24bdab9c4.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//2024/07/07/94caa25c7ada5b0fc3b2ac9b62c7b8fd.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/94caa25c7ada5b0fc3b2ac9b62c7b8fd.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//2024/07/07/670d46e6993018f2672960004f9d65e7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/670d46e6993018f2672960004f9d65e7.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//2024/07/07/c5cdc450fe4c7fa5f1ec8c99eaf911ff.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/c5cdc450fe4c7fa5f1ec8c99eaf911ff.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//2024/07/07/6c099edf9dbd414c1aa8ab63c3246d7d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/6c099edf9dbd414c1aa8ab63c3246d7d.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//2024/07/07/d49747d9aad65e6cca31fad06b72fc0e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/d49747d9aad65e6cca31fad06b72fc0e.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//2024/07/07/2a8b7fe406cf72de8ff9378bcddd5bfa.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/2a8b7fe406cf72de8ff9378bcddd5bfa.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//2024/07/07/3eab6470657e244cfe696065884c9936.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/3eab6470657e244cfe696065884c9936.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//2024/07/07/a4881c186d1653a941978e539ca5be27.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/07/07/a4881c186d1653a941978e539ca5be27.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>
<h2 id="奥运会公路车概况">奥运会公路车概况</h2>
<ol>
<li>男子个人计时赛没有中国选手参加，但是男子公路赛有吕先景一位中国选手参加。吕先景目前效力于中国华兴-铭普洲际车队。2018年雅加达亚运会上，他获得男子山地自行车银牌，2019年环中国自行车赛，获得第二赛段冠军。期待吕先景在奥运会上取得好成绩。（PS：不用惊讶哦，是有环中国比赛的，只是2019年之后因为疫情就停办了，并且也没有复办的消息）</li>
<li>女子个人计时赛有中国选手唐欣参加，同时她还会参加女子公路赛。她是中国第一位参加巴黎鲁贝女子自行车上并完赛的车手，也是中国第一位参加环西班牙女子自行车赛并完赛的车手，期待她在奥运会上取得好成绩。</li>
</ol>
<h3 id="比赛规则简述">比赛规则简述</h3>
<p>男女个人计时赛</p>
<ol>
<li>一场定胜负的比赛，运动员在规定的时间内完成比赛，成绩最好的运动员获得金牌；</li>
<li>运动员按照规定的间隔时间单独出发，以运动员到达终点的成绩优劣排名；</li>
<li>发车时由同一名裁判员扶车；</li>
<li>间隔1-2分钟发车，具体发车时间可在Tour Tracker，奥林匹克官网，ProCyclingStats等网站查看；</li>
<li>即使追上了前面发车的车手，也不能相互领骑；</li>
<li>在超车以及被超越过程中，运动员不得进入对手左右2米，前后25米的空间；</li>
<li>队车不得在运动员前方行驶，只能在运动员后方25米外行驶；</li>
</ol>
<p>男女公路赛</p>
<ol>
<li>一场定胜负的比赛，运动员在规定的时间内完成比赛，成绩最好的运动员获得金牌；</li>
<li>发车位置按照签到顺序确定；</li>
<li>比赛中可以跟风，可以互相帮助，交换食物和水以及工具等，但是不能相互推行；</li>
<li>在赛程的前50公里和进入最后20公里后，不得进行补给，赛道上会有标识补给开始区域和补给结束区域；</li>
<li>队车不得超越运动员，只有裁判同意才可以；</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>Milk-V Duo S 启动</title>
      <link>https://lifeislife.cn/posts/milk-v-duo-s-%E5%90%AF%E5%8A%A8/</link>
      <pubDate>Sun, 30 Jun 2024 21:04:51 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/milk-v-duo-s-%E5%90%AF%E5%8A%A8/</guid>
      <description>&lt;h2 id=&#34;准备工作&#34;&gt;准备工作&lt;/h2&gt;
&lt;p&gt;一直在QEMU上做RISC-V开发，最近入手了Milk-V Duo S开发板，准备尝试在硬件上玩玩RISC-V。99块钱的开发板，性价比还是很高的。以下内容就是参考官方文档&lt;a href=&#34;https://milkv.io/zh/docs/duo/getting-started/boot&#34;&gt;从 microSD 卡启动 Duo | Milk-V&lt;/a&gt;，复现一遍。先熟悉一下硬件。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Milk-V Duo S开发板一块&lt;/li&gt;
&lt;li&gt;大于 1GB 的 microSD 卡，我买的是 SD 卡版本，如果买的自带eMMC的就不需要了。&lt;/li&gt;
&lt;li&gt;Type-C 数据线&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;准备镜像并烧录&#34;&gt;准备镜像并烧录&lt;/h2&gt;
&lt;p&gt;下载&lt;a href=&#34;https://github.com/milkv-duo/duo-buildroot-sdk/releases/&#34;&gt;官方镜像&lt;/a&gt;，我使用的是milkv-duos-sd-v1.1.1-2024-0528.img.zip。解压后就可以得到镜像文件。我习惯使用&lt;a href=&#34;https://rufus.ie/en/&#34;&gt;Rufus&lt;/a&gt;烧录镜像。&lt;/p&gt;
&lt;h2 id=&#34;milk-v-duo-s-启动&#34;&gt;Milk-V Duo S 启动！&lt;/h2&gt;
&lt;p&gt;将SD卡插入开发板，然后使用Type-C线连接Duo S开发板和电脑。稍等片刻开发板上的蓝色LED灯将闪烁，红色LED保持常量亮。说明开发板已经启动成功。接下来就可以通过SSH连接开发板了。&lt;/p&gt;
&lt;p&gt;在使用SSH之前需要开启Windows RNDIS。开启过程参考官方文档&lt;a href=&#34;https://milkv.io/zh/docs/duo/getting-started/setup&#34;&gt;设置工作环境 | Milk-V&lt;/a&gt;，我就不再重复了，图片有点多，看官方文档更直观。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="准备工作">准备工作</h2>
<p>一直在QEMU上做RISC-V开发，最近入手了Milk-V Duo S开发板，准备尝试在硬件上玩玩RISC-V。99块钱的开发板，性价比还是很高的。以下内容就是参考官方文档<a href="https://milkv.io/zh/docs/duo/getting-started/boot">从 microSD 卡启动 Duo | Milk-V</a>，复现一遍。先熟悉一下硬件。</p>
<ul>
<li>Milk-V Duo S开发板一块</li>
<li>大于 1GB 的 microSD 卡，我买的是 SD 卡版本，如果买的自带eMMC的就不需要了。</li>
<li>Type-C 数据线</li>
</ul>
<h2 id="准备镜像并烧录">准备镜像并烧录</h2>
<p>下载<a href="https://github.com/milkv-duo/duo-buildroot-sdk/releases/">官方镜像</a>，我使用的是milkv-duos-sd-v1.1.1-2024-0528.img.zip。解压后就可以得到镜像文件。我习惯使用<a href="https://rufus.ie/en/">Rufus</a>烧录镜像。</p>
<h2 id="milk-v-duo-s-启动">Milk-V Duo S 启动！</h2>
<p>将SD卡插入开发板，然后使用Type-C线连接Duo S开发板和电脑。稍等片刻开发板上的蓝色LED灯将闪烁，红色LED保持常量亮。说明开发板已经启动成功。接下来就可以通过SSH连接开发板了。</p>
<p>在使用SSH之前需要开启Windows RNDIS。开启过程参考官方文档<a href="https://milkv.io/zh/docs/duo/getting-started/setup">设置工作环境 | Milk-V</a>，我就不再重复了，图片有点多，看官方文档更直观。</p>
]]></content:encoded>
    </item>
    <item>
      <title>BusyBox 构建并启动 RISC-V Linux 内核</title>
      <link>https://lifeislife.cn/posts/busybox-%E6%9E%84%E5%BB%BA%E5%B9%B6%E5%90%AF%E5%8A%A8-risc-v-linux-%E5%86%85%E6%A0%B8/</link>
      <pubDate>Thu, 20 Jun 2024 21:10:49 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/busybox-%E6%9E%84%E5%BB%BA%E5%B9%B6%E5%90%AF%E5%8A%A8-risc-v-linux-%E5%86%85%E6%A0%B8/</guid>
      <description>&lt;h1 id=&#34;根文件系统&#34;&gt;根文件系统&lt;/h1&gt;
&lt;h2 id=&#34;文件系统与根文件系统&#34;&gt;文件系统与根文件系统&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;文件系统&lt;/strong&gt;（File System）是操作系统用于管理和存储数据的一种方式。它定义了如何在存储设备（如硬盘、SSD、USB 驱动器等）上组织文件和目录，以及如何进行数据的读写操作。&lt;/p&gt;
&lt;p&gt;常见的文件系统类型有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ext4：Linux 最常用的文件系统，支持大文件和大分区。&lt;/li&gt;
&lt;li&gt;NTFS：Windows 操作系统常用的文件系统，支持文件加密和权限控制。&lt;/li&gt;
&lt;li&gt;FAT32：一种兼容性广泛的文件系统，常用于 USB 驱动器和内存卡。&lt;/li&gt;
&lt;li&gt;XFS：适用于高性能和高容量存储需求的文件系统。&lt;/li&gt;
&lt;li&gt;btrfs：一种现代 Linux 文件系统，支持快照、压缩和多设备管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;根文件系统&lt;/strong&gt;（Root File System，通常简称为 rootfs）是文件系统层次结构中的顶级文件系统。它包含了操作系统启动和运行所需的所有基本文件和目录。根文件系统是整个文件系统层次的起点，在 Linux 中由单个斜杠（&lt;code&gt;/&lt;/code&gt;）表示。&lt;strong&gt;根文件系统首先是内核启动时所 mount 的第一个文件系统&lt;/strong&gt;，内核代码映像文件保存在根文件系统中，而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;根文件系统是建立在文件系统之上的。根文件系统使用某种具体的文件系统类型（如 ext4）来管理和存储其内容。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;基于内存的文件系统&#34;&gt;基于内存的文件系统&lt;/h2&gt;
&lt;p&gt;ramdisk 是一种基于内存的文件系统，它将内存的一部分用作硬盘驱动器，这样就可以在内存中创建一个文件系统。ramdisk 是一个虚拟磁盘，它的大小和硬盘驱动器的大小一样。ramdisk 的优点是速度快，缺点是断电后数据丢失。&lt;/p&gt;
&lt;h2 id=&#34;根文件系统中各种配置文件的作用以及配置文件的格式介绍&#34;&gt;根文件系统中各种配置文件的作用以及配置文件的格式介绍&lt;/h2&gt;
&lt;h3 id=&#34;etcinittab&#34;&gt;/etc/inittab&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/etc/inittab&lt;/code&gt; 是 Linux 系统中的一个配置文件，它是 init 程序的配置文件，用于配置系统的运行级别和 init 程序的行为。在 Linux 系统中，init 程序是系统的第一个进程，它负责启动系统中的所有其他进程。&lt;code&gt;/etc/inittab&lt;/code&gt; 文件中的每一行都是一个配置项，每个配置项由四个字段组成，字段之间用空格或制表符分隔。&lt;code&gt;/etc/inittab&lt;/code&gt; 文件的格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;id:runlevels:action:process
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个字段用冒号分隔，可以缺省。各字段的含义如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;id：配置项的标识符，用于标识配置项。&lt;/li&gt;
&lt;li&gt;runlevels：配置项所对应的运行级别，可以是一个或多个运行级别的组合。&lt;/li&gt;
&lt;li&gt;action：配置项的动作，可以是以下几种动作之一：
&lt;ul&gt;
&lt;li&gt;sysinit：系统初始化时运行。&lt;/li&gt;
&lt;li&gt;respawn：如果进程终止，立即重新启动。&lt;/li&gt;
&lt;li&gt;askfirst：在运行 process 之前询问用户。并在控制台上显示 Please press Enter to active this console。&lt;/li&gt;
&lt;li&gt;wait：等待进程终止，然后继续执行下一个配置项。&lt;/li&gt;
&lt;li&gt;once：只运行一次，进程终止后不会重新启动。&lt;/li&gt;
&lt;li&gt;boot：在系统引导时运行。&lt;/li&gt;
&lt;li&gt;bootwait：在系统引导时运行，等待进程终止后继续引导。&lt;/li&gt;
&lt;li&gt;initdefault：设置默认运行级别。&lt;/li&gt;
&lt;li&gt;shutdown：在系统关机时运行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;process：要执行的进程或脚本的路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /etc/inittab&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#系统开机或重新启动，执行rcS文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;::sysinit:/etc/init.d/rcS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#系统启动后，运行登录程序  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;::askfirst:-/bin/login
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#按下组合键“ctrl+alt+del”，重启Linux系统&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;::ctrlaltdel:-/sbin/reboot   
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#系统关机时，卸载所有文件系统&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;::shutdown:/bin/umount -a -r
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#重启init进程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;::restart:/sbin/init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;etcfstab&#34;&gt;/etc/fstab&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/etc/fstab&lt;/code&gt; 是 Linux 系统中的一个配置文件，用于配置文件系统的挂载信息。在 Linux 系统中，文件系统是通过挂载的方式来访问的，&lt;code&gt;/etc/fstab&lt;/code&gt; 文件中记录了系统中所有文件系统的挂载信息。&lt;code&gt;/etc/fstab&lt;/code&gt; 文件的格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;device mount_point fs_type options dump pass
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;各字段的含义如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;device：设备文件或 UUID。&lt;/li&gt;
&lt;li&gt;mount_point：挂载点。&lt;/li&gt;
&lt;li&gt;fs_type：文件系统类型。&lt;/li&gt;
&lt;li&gt;options：挂载选项。&lt;/li&gt;
&lt;li&gt;dump：备份标志，用于备份工具。&lt;/li&gt;
&lt;li&gt;pass：文件系统检查顺序。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /etc/fstab&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# &amp;lt;文件系统&amp;gt;  &amp;lt;挂载点&amp;gt;  &amp;lt;类型&amp;gt;  &amp;lt;选项&amp;gt;        &amp;lt;dump&amp;gt;  &amp;lt;fsck&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 根文件系统&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda1       /           ext4    defaults        &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# 根分区，使用 ext4 文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /boot 分区，用于存放引导加载程序和内核镜像&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda2       /boot       ext4    defaults        &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# /boot 分区，使用 ext4 文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 交换分区，用作虚拟内存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda3       none        swap    sw              &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# 交换分区，不需要挂载点，使用 swap 类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /home 分区，用于存放用户的个人数据&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda4       /home       ext4    defaults        &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# /home 分区，使用 ext4 文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /tmp 分区，用于存放临时文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda5       /tmp        ext4    defaults        &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# /tmp 分区，使用 ext4 文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /var 分区，用于存放可变数据文件，如日志、邮件、缓存等&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda6       /var        ext4    defaults        &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# /var 分区，使用 ext4 文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /mnt/data 分区，用于存放额外的数据文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda7       /mnt/data   ext4    defaults        &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# /mnt/data 分区，使用 ext4 文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 proc 文件系统，用于访问进程和系统信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;proc            /proc       proc    defaults        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# proc 虚拟文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 sysfs 文件系统，用于访问设备和内核信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sysfs           /sys        sysfs   defaults        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# sysfs 虚拟文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 tmpfs 文件系统，用于临时文件存储&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tmpfs           /dev/shm    tmpfs   defaults        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# tmpfs 虚拟文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 devpts 文件系统，用于伪终端设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;devpts          /dev/pts    devpts  &lt;span class=&#34;nv&#34;&gt;gid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;5,mode&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;620&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# devpts 虚拟文件系统，默认挂载选项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;etcinitdrcs&#34;&gt;/etc/init.d/rcS&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/etc/init.d/rcS&lt;/code&gt; 是 Linux 系统中的一个初始化脚本，用于系统初始化时运行。在 Linux 系统中，&lt;code&gt;/etc/init.d/rcS&lt;/code&gt; 脚本是系统初始化时运行的第一个脚本，它负责初始化系统中的各种服务和配置。&lt;code&gt;/etc/init.d/rcS&lt;/code&gt; 脚本通常包含了一些初始化命令，如加载模块、挂载文件系统、启动服务等。&lt;code&gt;/etc/init.d/rcS&lt;/code&gt; 脚本的格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 这行是 shebang，用于指定这个脚本将由 /bin/sh 解释执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 proc 文件系统到 /proc 目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t proc none /proc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# proc 文件系统是一个虚拟文件系统，用于提供内核和进程的信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 sysfs 文件系统到 /sys 目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t sysfs none /sys
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# sysfs 文件系统是一个虚拟文件系统，用于提供设备和内核信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 devtmpfs 文件系统到 /dev 目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t devtmpfs none /dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# devtmpfs 是一个用于设备节点的临时文件系统&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 运行 mdev 以创建设备节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/sbin/mdev -s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# mdev 是一个轻量级的设备管理工具，用于创建和管理 /dev 目录中的设备节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置主机名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;myhostname&amp;#34;&lt;/span&gt; &amp;gt; /proc/sys/kernel/hostname
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将主机名设置为 &amp;#34;myhostname&amp;#34;，这个名字可以根据需要更改&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启动网络接口配置脚本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig lo up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启动回环接口 lo，这是一个特殊的网络接口，用于网络软件本地通信&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 运行所有位于 /etc/init.d/ 目录中的初始化脚本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; script in /etc/init.d/S*&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -x &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$script&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$script&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; start
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 遍历 /etc/init.d/ 目录中的所有以 S 开头的脚本，并执行它们以启动相应的服务&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -x 选项用于检查文件是否可执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载用户文件系统（可选，根据实际需要）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 挂载 /etc/fstab 文件中列出的所有文件系统&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 打印启动完成信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;System initialization complete.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出系统初始化完成信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;etcprofile&#34;&gt;/etc/profile&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/etc/profile&lt;/code&gt; 是 Linux 系统中的一个全局配置文件，用于配置系统的环境变量和用户的 shell 环境。在 Linux 系统中，&lt;code&gt;/etc/profile&lt;/code&gt; 文件是系统启动时加载的第一个配置文件，它包含了系统的全局环境变量和用户的 shell 环境配置。&lt;code&gt;/etc/profile&lt;/code&gt; 文件的格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /etc/profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#!/bin/sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 这行是 shebang，用于指定这个脚本将由 /bin/sh 解释执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置 PATH 环境变量，定义系统命令的搜索路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; PATH
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /usr/local/sbin:/usr/local/bin 是系统管理员和用户安装的程序路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /sbin:/bin:/usr/sbin:/usr/bin 是系统默认的命令路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置用户 umask，定义新文件和目录的默认权限掩码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;umask&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;022&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# umask 022 表示新文件的默认权限是 755，新目录的默认权限是 644&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置系统语言和区域&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LANG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;en_US.UTF-8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; LANG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# LANG 变量定义了系统的语言环境，这里设置为美国英语 UTF-8 编码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置历史记录文件和大小&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;HISTSIZE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;HISTFILESIZE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; HISTSIZE HISTFILESIZE
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# HISTSIZE 定义了 shell 会话历史记录的条目数，HISTFILESIZE 定义了历史记录文件的最大条目数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 系统启动信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Welcome to your Linux system!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在用户登录时显示欢迎信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 读取并执行 /etc/bashrc（如果存在）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f /etc/bashrc &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    . /etc/bashrc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# /etc/bashrc 是另一个全局配置文件，通常包含 bash shell 的配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 使用 . /etc/bashrc 命令将其内容导入当前 shell 环境&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 配置别名（示例）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;alias&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ll&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ls -l&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;alias&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;la&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ls -A&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;alias&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ls -CF&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 定义一些常用命令的别名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ll 代表 ls -l，显示详细列表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# la 代表 ls -A，显示所有文件包括隐藏文件（但不包括 . 和 ..）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# l 代表 ls -CF，以分类格式显示文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 读取用户特定的配置文件（如果存在）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/.profile&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    . &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/.profile&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# .profile 是用户的个人配置文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 使用 . $HOME/.profile 命令将其内容导入当前 shell 环境&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置编辑器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;EDITOR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;vim
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; EDITOR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 定义默认的文本编辑器为 vim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置主机名显示，也就是终端最前面提示符的样式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PS1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\u@\h:\w\$ &amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; PS1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# PS1 定义了 shell 提示符的样式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# \u 代表用户名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# \h 代表主机名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# \w 代表当前工作目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;busybox-简介以及如何制作-busybox-文件系统&#34;&gt;Busybox 简介以及如何制作 Busybox 文件系统&lt;/h2&gt;
&lt;h3 id=&#34;准备-qemu&#34;&gt;准备 QEMU&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p /home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; workspace
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/qemu/qemu.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./configure --target-list&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --enable-kvm --enable-sdl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make install -j &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;准备-opensbi&#34;&gt;准备 OpenSBI&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; workspace
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/riscv-software-src/opensbi.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; opensbi
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;ARCH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv &lt;span class=&#34;nv&#34;&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu- &lt;span class=&#34;nv&#34;&gt;PLATFORM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;generic
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;准备-linux-内核&#34;&gt;准备 Linux 内核&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; workspace
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/torvalds/linux.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;ARCH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv &lt;span class=&#34;nv&#34;&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu- defconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;ARCH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv &lt;span class=&#34;nv&#34;&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu- -j &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;准备-busybox&#34;&gt;准备 Busybox&lt;/h3&gt;
&lt;p&gt;进入下载软件包，&lt;a href=&#34;https://busybox.net/downloads/&#34;&gt;https://busybox.net/downloads/&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xvf busybox-1.36.1.tar.bz2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv busybox-1.36.1 busybox
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; busybox
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置编译选项&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu- menuconfig
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;选择静态库模式，设置路径：Settings -&amp;gt; Build static binary (no shared libs)&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/10-51-50-e83454415403aacffad1e9320676356e-20240607105149-9cc407.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-51-50-e83454415403aacffad1e9320676356e-20240607105149-9cc407.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/10-52-13-e92a2a3f782236215a33b93161226d99-20240607105212-428b5c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-52-13-e92a2a3f782236215a33b93161226d99-20240607105212-428b5c.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;编译 Busybox&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu- -j &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;如果需要频繁编译，也可以在 Menuconfig 中设置交叉编译器路径，这样就不需要每次都指定了。设置路径为：Settings -&amp;gt; Build Options -&amp;gt; Cross Compiler prefix。将其设置为 riscv64-unknown-linux-gnu-。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;安装 Busybox：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu- install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Busybox 默认安装在当前目录的_install 目录下，也可以在 Menuconfig 中设置安装目录，路径为：Settings -&amp;gt; Build Options -&amp;gt; Destination path for &amp;lsquo;make install&amp;rsquo;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;查看安装目录&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ls _install 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bin  linuxrc  sbin  usr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;制作根文件系统&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img create -f raw rootfs.img 256M
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkfs.ext4 rootfs.img
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir rootfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mount -o loop rootfs.img rootfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo cp -r _install/* rootfs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; rootfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mkdir proc sys dev etc etc/init.d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; etc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; init.d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo touch rcS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x rcS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim rcS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;写入以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t proc none /proc  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t sysfs none /sys  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/sbin/mdev -s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo umount rootfs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;qemu 启动内核：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; -en &lt;span class=&#34;s1&#34;&gt;&amp;#39;\001\033[0m\002&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; -en &lt;span class=&#34;s1&#34;&gt;&amp;#39;\001\033[00;33m\002&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;## Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;vcpu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;memory&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;drive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/user/workspace/rootfs.img&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;kernel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; /home/user/workspace/linux-stable/arch/riscv/boot/Image&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fw&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/user/workspace/opensbi/build/platform/generic/firmware/fw_jump.bin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ssh_port&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;12070&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/user/program/riscv64-qemu/bin/qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -cpu rv64\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -kernel &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$kernel&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;,format=raw,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -append &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/vda &lt;span class=&#34;nv&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;ttyS0&lt;span class=&#34;s2&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;:: Starting VM...&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;:: Using following configuration&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;vCPU Cores: &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;Memory: &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;G&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;Disk: &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;SSH Port: &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$ssh_port&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;:: NOTE: Make sure ONLY ONE .qcow2 file is&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;in the current directory&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;:: Tip: Try setting DNS manually &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; QEMU user network doesn&lt;span class=&#34;se&#34;&gt;\&amp;#39;&lt;/span&gt;t work well. &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;:: HOWTO -&lt;span class=&#34;se&#34;&gt;\&amp;gt;&lt;/span&gt; https://serverfault.com/a/810639 &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;RESTORE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sleep &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cmd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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/17-24-09-3656ec35f835ab8650a4628ceac4dad8-20240618172408-48a595.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-24-09-3656ec35f835ab8650a4628ceac4dad8-20240618172408-48a595.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;
</description>
      <content:encoded><![CDATA[<h1 id="根文件系统">根文件系统</h1>
<h2 id="文件系统与根文件系统">文件系统与根文件系统</h2>
<p><strong>文件系统</strong>（File System）是操作系统用于管理和存储数据的一种方式。它定义了如何在存储设备（如硬盘、SSD、USB 驱动器等）上组织文件和目录，以及如何进行数据的读写操作。</p>
<p>常见的文件系统类型有：</p>
<ul>
<li>ext4：Linux 最常用的文件系统，支持大文件和大分区。</li>
<li>NTFS：Windows 操作系统常用的文件系统，支持文件加密和权限控制。</li>
<li>FAT32：一种兼容性广泛的文件系统，常用于 USB 驱动器和内存卡。</li>
<li>XFS：适用于高性能和高容量存储需求的文件系统。</li>
<li>btrfs：一种现代 Linux 文件系统，支持快照、压缩和多设备管理。</li>
</ul>
<p><strong>根文件系统</strong>（Root File System，通常简称为 rootfs）是文件系统层次结构中的顶级文件系统。它包含了操作系统启动和运行所需的所有基本文件和目录。根文件系统是整个文件系统层次的起点，在 Linux 中由单个斜杠（<code>/</code>）表示。<strong>根文件系统首先是内核启动时所 mount 的第一个文件系统</strong>，内核代码映像文件保存在根文件系统中，而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。</p>
<p><strong>根文件系统是建立在文件系统之上的。根文件系统使用某种具体的文件系统类型（如 ext4）来管理和存储其内容。</strong></p>
<h2 id="基于内存的文件系统">基于内存的文件系统</h2>
<p>ramdisk 是一种基于内存的文件系统，它将内存的一部分用作硬盘驱动器，这样就可以在内存中创建一个文件系统。ramdisk 是一个虚拟磁盘，它的大小和硬盘驱动器的大小一样。ramdisk 的优点是速度快，缺点是断电后数据丢失。</p>
<h2 id="根文件系统中各种配置文件的作用以及配置文件的格式介绍">根文件系统中各种配置文件的作用以及配置文件的格式介绍</h2>
<h3 id="etcinittab">/etc/inittab</h3>
<p><code>/etc/inittab</code> 是 Linux 系统中的一个配置文件，它是 init 程序的配置文件，用于配置系统的运行级别和 init 程序的行为。在 Linux 系统中，init 程序是系统的第一个进程，它负责启动系统中的所有其他进程。<code>/etc/inittab</code> 文件中的每一行都是一个配置项，每个配置项由四个字段组成，字段之间用空格或制表符分隔。<code>/etc/inittab</code> 文件的格式如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">id:runlevels:action:process
</span></span></code></pre></div><p>每个字段用冒号分隔，可以缺省。各字段的含义如下：</p>
<ul>
<li>id：配置项的标识符，用于标识配置项。</li>
<li>runlevels：配置项所对应的运行级别，可以是一个或多个运行级别的组合。</li>
<li>action：配置项的动作，可以是以下几种动作之一：
<ul>
<li>sysinit：系统初始化时运行。</li>
<li>respawn：如果进程终止，立即重新启动。</li>
<li>askfirst：在运行 process 之前询问用户。并在控制台上显示 Please press Enter to active this console。</li>
<li>wait：等待进程终止，然后继续执行下一个配置项。</li>
<li>once：只运行一次，进程终止后不会重新启动。</li>
<li>boot：在系统引导时运行。</li>
<li>bootwait：在系统引导时运行，等待进程终止后继续引导。</li>
<li>initdefault：设置默认运行级别。</li>
<li>shutdown：在系统关机时运行。</li>
</ul>
</li>
<li>process：要执行的进程或脚本的路径。</li>
</ul>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># /etc/inittab</span>
</span></span><span class="line"><span class="cl"><span class="c1">#系统开机或重新启动，执行rcS文件</span>
</span></span><span class="line"><span class="cl">::sysinit:/etc/init.d/rcS
</span></span><span class="line"><span class="cl"><span class="c1">#系统启动后，运行登录程序  </span>
</span></span><span class="line"><span class="cl">::askfirst:-/bin/login
</span></span><span class="line"><span class="cl"><span class="c1">#按下组合键“ctrl+alt+del”，重启Linux系统</span>
</span></span><span class="line"><span class="cl">::ctrlaltdel:-/sbin/reboot   
</span></span><span class="line"><span class="cl"><span class="c1">#系统关机时，卸载所有文件系统</span>
</span></span><span class="line"><span class="cl">::shutdown:/bin/umount -a -r
</span></span><span class="line"><span class="cl"><span class="c1">#重启init进程</span>
</span></span><span class="line"><span class="cl">::restart:/sbin/init
</span></span></code></pre></div><h3 id="etcfstab">/etc/fstab</h3>
<p><code>/etc/fstab</code> 是 Linux 系统中的一个配置文件，用于配置文件系统的挂载信息。在 Linux 系统中，文件系统是通过挂载的方式来访问的，<code>/etc/fstab</code> 文件中记录了系统中所有文件系统的挂载信息。<code>/etc/fstab</code> 文件的格式如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">device mount_point fs_type options dump pass
</span></span></code></pre></div><p>各字段的含义如下：</p>
<ul>
<li>device：设备文件或 UUID。</li>
<li>mount_point：挂载点。</li>
<li>fs_type：文件系统类型。</li>
<li>options：挂载选项。</li>
<li>dump：备份标志，用于备份工具。</li>
<li>pass：文件系统检查顺序。</li>
</ul>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># /etc/fstab</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &lt;文件系统&gt;  &lt;挂载点&gt;  &lt;类型&gt;  &lt;选项&gt;        &lt;dump&gt;  &lt;fsck&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 根文件系统</span>
</span></span><span class="line"><span class="cl">/dev/sda1       /           ext4    defaults        <span class="m">1</span>       <span class="m">1</span>  <span class="c1"># 根分区，使用 ext4 文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># /boot 分区，用于存放引导加载程序和内核镜像</span>
</span></span><span class="line"><span class="cl">/dev/sda2       /boot       ext4    defaults        <span class="m">1</span>       <span class="m">2</span>  <span class="c1"># /boot 分区，使用 ext4 文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 交换分区，用作虚拟内存</span>
</span></span><span class="line"><span class="cl">/dev/sda3       none        swap    sw              <span class="m">0</span>       <span class="m">0</span>  <span class="c1"># 交换分区，不需要挂载点，使用 swap 类型</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># /home 分区，用于存放用户的个人数据</span>
</span></span><span class="line"><span class="cl">/dev/sda4       /home       ext4    defaults        <span class="m">1</span>       <span class="m">2</span>  <span class="c1"># /home 分区，使用 ext4 文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># /tmp 分区，用于存放临时文件</span>
</span></span><span class="line"><span class="cl">/dev/sda5       /tmp        ext4    defaults        <span class="m">1</span>       <span class="m">2</span>  <span class="c1"># /tmp 分区，使用 ext4 文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># /var 分区，用于存放可变数据文件，如日志、邮件、缓存等</span>
</span></span><span class="line"><span class="cl">/dev/sda6       /var        ext4    defaults        <span class="m">1</span>       <span class="m">2</span>  <span class="c1"># /var 分区，使用 ext4 文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># /mnt/data 分区，用于存放额外的数据文件</span>
</span></span><span class="line"><span class="cl">/dev/sda7       /mnt/data   ext4    defaults        <span class="m">1</span>       <span class="m">2</span>  <span class="c1"># /mnt/data 分区，使用 ext4 文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 proc 文件系统，用于访问进程和系统信息</span>
</span></span><span class="line"><span class="cl">proc            /proc       proc    defaults        <span class="m">0</span>       <span class="m">0</span>  <span class="c1"># proc 虚拟文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 sysfs 文件系统，用于访问设备和内核信息</span>
</span></span><span class="line"><span class="cl">sysfs           /sys        sysfs   defaults        <span class="m">0</span>       <span class="m">0</span>  <span class="c1"># sysfs 虚拟文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 tmpfs 文件系统，用于临时文件存储</span>
</span></span><span class="line"><span class="cl">tmpfs           /dev/shm    tmpfs   defaults        <span class="m">0</span>       <span class="m">0</span>  <span class="c1"># tmpfs 虚拟文件系统，默认挂载选项</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 devpts 文件系统，用于伪终端设备</span>
</span></span><span class="line"><span class="cl">devpts          /dev/pts    devpts  <span class="nv">gid</span><span class="o">=</span>5,mode<span class="o">=</span><span class="m">620</span>  <span class="m">0</span>       <span class="m">0</span>  <span class="c1"># devpts 虚拟文件系统，默认挂载选项</span>
</span></span></code></pre></div><h3 id="etcinitdrcs">/etc/init.d/rcS</h3>
<p><code>/etc/init.d/rcS</code> 是 Linux 系统中的一个初始化脚本，用于系统初始化时运行。在 Linux 系统中，<code>/etc/init.d/rcS</code> 脚本是系统初始化时运行的第一个脚本，它负责初始化系统中的各种服务和配置。<code>/etc/init.d/rcS</code> 脚本通常包含了一些初始化命令，如加载模块、挂载文件系统、启动服务等。<code>/etc/init.d/rcS</code> 脚本的格式如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/bin/sh
</span></span></span><span class="line"><span class="cl"><span class="c1"># 这行是 shebang，用于指定这个脚本将由 /bin/sh 解释执行</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 proc 文件系统到 /proc 目录</span>
</span></span><span class="line"><span class="cl">mount -t proc none /proc
</span></span><span class="line"><span class="cl"><span class="c1"># proc 文件系统是一个虚拟文件系统，用于提供内核和进程的信息</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 sysfs 文件系统到 /sys 目录</span>
</span></span><span class="line"><span class="cl">mount -t sysfs none /sys
</span></span><span class="line"><span class="cl"><span class="c1"># sysfs 文件系统是一个虚拟文件系统，用于提供设备和内核信息</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 devtmpfs 文件系统到 /dev 目录</span>
</span></span><span class="line"><span class="cl">mount -t devtmpfs none /dev
</span></span><span class="line"><span class="cl"><span class="c1"># devtmpfs 是一个用于设备节点的临时文件系统</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 运行 mdev 以创建设备节点</span>
</span></span><span class="line"><span class="cl">/sbin/mdev -s
</span></span><span class="line"><span class="cl"><span class="c1"># mdev 是一个轻量级的设备管理工具，用于创建和管理 /dev 目录中的设备节点</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置主机名</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;myhostname&#34;</span> &gt; /proc/sys/kernel/hostname
</span></span><span class="line"><span class="cl"><span class="c1"># 将主机名设置为 &#34;myhostname&#34;，这个名字可以根据需要更改</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 启动网络接口配置脚本</span>
</span></span><span class="line"><span class="cl">ifconfig lo up
</span></span><span class="line"><span class="cl"><span class="c1"># 启动回环接口 lo，这是一个特殊的网络接口，用于网络软件本地通信</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 运行所有位于 /etc/init.d/ 目录中的初始化脚本</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> script in /etc/init.d/S*<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[</span> -x <span class="s2">&#34;</span><span class="nv">$script</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;</span><span class="nv">$script</span><span class="s2">&#34;</span> start
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 遍历 /etc/init.d/ 目录中的所有以 S 开头的脚本，并执行它们以启动相应的服务</span>
</span></span><span class="line"><span class="cl"><span class="c1"># -x 选项用于检查文件是否可执行</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载用户文件系统（可选，根据实际需要）</span>
</span></span><span class="line"><span class="cl">mount -a
</span></span><span class="line"><span class="cl"><span class="c1"># 挂载 /etc/fstab 文件中列出的所有文件系统</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 打印启动完成信息</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;System initialization complete.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 输出系统初始化完成信息</span>
</span></span></code></pre></div><h3 id="etcprofile">/etc/profile</h3>
<p><code>/etc/profile</code> 是 Linux 系统中的一个全局配置文件，用于配置系统的环境变量和用户的 shell 环境。在 Linux 系统中，<code>/etc/profile</code> 文件是系统启动时加载的第一个配置文件，它包含了系统的全局环境变量和用户的 shell 环境配置。<code>/etc/profile</code> 文件的格式如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># /etc/profile</span>
</span></span><span class="line"><span class="cl"><span class="c1">#!/bin/sh</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 这行是 shebang，用于指定这个脚本将由 /bin/sh 解释执行</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置 PATH 环境变量，定义系统命令的搜索路径</span>
</span></span><span class="line"><span class="cl"><span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> PATH
</span></span><span class="line"><span class="cl"><span class="c1"># /usr/local/sbin:/usr/local/bin 是系统管理员和用户安装的程序路径</span>
</span></span><span class="line"><span class="cl"><span class="c1"># /sbin:/bin:/usr/sbin:/usr/bin 是系统默认的命令路径</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置用户 umask，定义新文件和目录的默认权限掩码</span>
</span></span><span class="line"><span class="cl"><span class="nb">umask</span> <span class="m">022</span>
</span></span><span class="line"><span class="cl"><span class="c1"># umask 022 表示新文件的默认权限是 755，新目录的默认权限是 644</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置系统语言和区域</span>
</span></span><span class="line"><span class="cl"><span class="nv">LANG</span><span class="o">=</span><span class="s2">&#34;en_US.UTF-8&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> LANG
</span></span><span class="line"><span class="cl"><span class="c1"># LANG 变量定义了系统的语言环境，这里设置为美国英语 UTF-8 编码</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置历史记录文件和大小</span>
</span></span><span class="line"><span class="cl"><span class="nv">HISTSIZE</span><span class="o">=</span><span class="m">1000</span>
</span></span><span class="line"><span class="cl"><span class="nv">HISTFILESIZE</span><span class="o">=</span><span class="m">2000</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> HISTSIZE HISTFILESIZE
</span></span><span class="line"><span class="cl"><span class="c1"># HISTSIZE 定义了 shell 会话历史记录的条目数，HISTFILESIZE 定义了历史记录文件的最大条目数</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 系统启动信息</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Welcome to your Linux system!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 在用户登录时显示欢迎信息</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 读取并执行 /etc/bashrc（如果存在）</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -f /etc/bashrc <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    . /etc/bashrc
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="c1"># /etc/bashrc 是另一个全局配置文件，通常包含 bash shell 的配置</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 . /etc/bashrc 命令将其内容导入当前 shell 环境</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 配置别名（示例）</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">ll</span><span class="o">=</span><span class="s1">&#39;ls -l&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">la</span><span class="o">=</span><span class="s1">&#39;ls -A&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">l</span><span class="o">=</span><span class="s1">&#39;ls -CF&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 定义一些常用命令的别名</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ll 代表 ls -l，显示详细列表</span>
</span></span><span class="line"><span class="cl"><span class="c1"># la 代表 ls -A，显示所有文件包括隐藏文件（但不包括 . 和 ..）</span>
</span></span><span class="line"><span class="cl"><span class="c1"># l 代表 ls -CF，以分类格式显示文件</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 读取用户特定的配置文件（如果存在）</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.profile&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    . <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.profile&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="c1"># .profile 是用户的个人配置文件</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 . $HOME/.profile 命令将其内容导入当前 shell 环境</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置编辑器</span>
</span></span><span class="line"><span class="cl"><span class="nv">EDITOR</span><span class="o">=</span>vim
</span></span><span class="line"><span class="cl"><span class="nb">export</span> EDITOR
</span></span><span class="line"><span class="cl"><span class="c1"># 定义默认的文本编辑器为 vim</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置主机名显示，也就是终端最前面提示符的样式</span>
</span></span><span class="line"><span class="cl"><span class="nv">PS1</span><span class="o">=</span><span class="s1">&#39;\u@\h:\w\$ &#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> PS1
</span></span><span class="line"><span class="cl"><span class="c1"># PS1 定义了 shell 提示符的样式</span>
</span></span><span class="line"><span class="cl"><span class="c1"># \u 代表用户名</span>
</span></span><span class="line"><span class="cl"><span class="c1"># \h 代表主机名</span>
</span></span><span class="line"><span class="cl"><span class="c1"># \w 代表当前工作目录</span>
</span></span></code></pre></div><h2 id="busybox-简介以及如何制作-busybox-文件系统">Busybox 简介以及如何制作 Busybox 文件系统</h2>
<h3 id="准备-qemu">准备 QEMU</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">&amp;&amp;</span> mkdir -p /home/user/program/riscv64-qemu
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> workspace
</span></span><span class="line"><span class="cl">git clone https://github.com/qemu/qemu.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> qemu
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> qemu <span class="o">&amp;&amp;</span> ./configure --target-list<span class="o">=</span>riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu <span class="se">\
</span></span></span><span class="line"><span class="cl">               --enable-kvm --enable-sdl <span class="se">\
</span></span></span><span class="line"><span class="cl">               --prefix<span class="o">=</span>/home/user/program/riscv64-qemu
</span></span><span class="line"><span class="cl">make install -j <span class="k">$(</span>nproc<span class="k">)</span>
</span></span></code></pre></div><h3 id="准备-opensbi">准备 OpenSBI</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> workspace
</span></span><span class="line"><span class="cl">git clone https://github.com/riscv-software-src/opensbi.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> opensbi
</span></span><span class="line"><span class="cl">make <span class="nv">ARCH</span><span class="o">=</span>riscv <span class="nv">CROSS_COMPILE</span><span class="o">=</span>riscv64-unknown-linux-gnu- <span class="nv">PLATFORM</span><span class="o">=</span>generic
</span></span></code></pre></div><h3 id="准备-linux-内核">准备 Linux 内核</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> workspace
</span></span><span class="line"><span class="cl">git clone https://github.com/torvalds/linux.git
</span></span><span class="line"><span class="cl">make <span class="nv">ARCH</span><span class="o">=</span>riscv <span class="nv">CROSS_COMPILE</span><span class="o">=</span>riscv64-unknown-linux-gnu- defconfig
</span></span><span class="line"><span class="cl">make <span class="nv">ARCH</span><span class="o">=</span>riscv <span class="nv">CROSS_COMPILE</span><span class="o">=</span>riscv64-unknown-linux-gnu- -j <span class="k">$(</span>nproc<span class="k">)</span>
</span></span></code></pre></div><h3 id="准备-busybox">准备 Busybox</h3>
<p>进入下载软件包，<a href="https://busybox.net/downloads/">https://busybox.net/downloads/</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
</span></span><span class="line"><span class="cl">tar -xvf busybox-1.36.1.tar.bz2
</span></span><span class="line"><span class="cl">mv busybox-1.36.1 busybox
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> busybox
</span></span></code></pre></div><p>配置编译选项</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">make <span class="nv">CROSS_COMPILE</span><span class="o">=</span>riscv64-unknown-linux-gnu- menuconfig
</span></span></code></pre></div><p>选择静态库模式，设置路径：Settings -&gt; Build static binary (no shared libs)</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/10-51-50-e83454415403aacffad1e9320676356e-20240607105149-9cc407.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-51-50-e83454415403aacffad1e9320676356e-20240607105149-9cc407.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/10-52-13-e92a2a3f782236215a33b93161226d99-20240607105212-428b5c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-52-13-e92a2a3f782236215a33b93161226d99-20240607105212-428b5c.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>编译 Busybox</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">make <span class="nv">CROSS_COMPILE</span><span class="o">=</span>riscv64-unknown-linux-gnu- -j <span class="k">$(</span>nproc<span class="k">)</span>
</span></span></code></pre></div><blockquote>
<p>如果需要频繁编译，也可以在 Menuconfig 中设置交叉编译器路径，这样就不需要每次都指定了。设置路径为：Settings -&gt; Build Options -&gt; Cross Compiler prefix。将其设置为 riscv64-unknown-linux-gnu-。</p>
</blockquote>
<p>安装 Busybox：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">make <span class="nv">CROSS_COMPILE</span><span class="o">=</span>riscv64-unknown-linux-gnu- install
</span></span></code></pre></div><blockquote>
<p>Busybox 默认安装在当前目录的_install 目录下，也可以在 Menuconfig 中设置安装目录，路径为：Settings -&gt; Build Options -&gt; Destination path for &lsquo;make install&rsquo;。</p>
</blockquote>
<p>查看安装目录</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ls _install 
</span></span><span class="line"><span class="cl">bin  linuxrc  sbin  usr
</span></span></code></pre></div><p>制作根文件系统</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">qemu-img create -f raw rootfs.img 256M
</span></span><span class="line"><span class="cl">mkfs.ext4 rootfs.img
</span></span><span class="line"><span class="cl">mkdir rootfs
</span></span><span class="line"><span class="cl">sudo mount -o loop rootfs.img rootfs
</span></span><span class="line"><span class="cl">sudo cp -r _install/* rootfs
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">cd</span> rootfs
</span></span><span class="line"><span class="cl">sudo mkdir proc sys dev etc etc/init.d
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> etc
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> init.d
</span></span><span class="line"><span class="cl">sudo touch rcS
</span></span><span class="line"><span class="cl">sudo chmod +x rcS
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo vim rcS
</span></span></code></pre></div><p>写入以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/bin/sh  
</span></span></span><span class="line"><span class="cl">mount -t proc none /proc  
</span></span><span class="line"><span class="cl">mount -t sysfs none /sys  
</span></span><span class="line"><span class="cl">/sbin/mdev -s
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo umount rootfs
</span></span></code></pre></div><p>qemu 启动内核：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="nv">RESTORE</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> -en <span class="s1">&#39;\001\033[0m\002&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">YELLOW</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> -en <span class="s1">&#39;\001\033[00;33m\002&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">## Configuration</span>
</span></span><span class="line"><span class="cl"><span class="nv">vcpu</span><span class="o">=</span><span class="m">8</span>
</span></span><span class="line"><span class="cl"><span class="nv">memory</span><span class="o">=</span><span class="m">16</span>
</span></span><span class="line"><span class="cl"><span class="nv">drive</span><span class="o">=</span><span class="s2">&#34;/home/user/workspace/rootfs.img&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">kernel</span><span class="o">=</span><span class="s2">&#34; /home/user/workspace/linux-stable/arch/riscv/boot/Image&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">fw</span><span class="o">=</span><span class="s2">&#34;/home/user/workspace/opensbi/build/platform/generic/firmware/fw_jump.bin&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">ssh_port</span><span class="o">=</span><span class="m">12070</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;/home/user/program/riscv64-qemu/bin/qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -cpu rv64\
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -kernel &#34;</span><span class="nv">$kernel</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=</span><span class="nv">$drive</span><span class="s2">,format=raw,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -append &#34;</span><span class="nv">root</span><span class="o">=</span>/dev/vda <span class="nv">console</span><span class="o">=</span>ttyS0<span class="s2">&#34; &#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>:: Starting VM...<span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>:: Using following configuration<span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>vCPU Cores: <span class="s2">&#34;</span><span class="nv">$vcpu</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>Memory: <span class="s2">&#34;</span><span class="nv">$memory</span><span class="s2">&#34;</span>G<span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>Disk: <span class="s2">&#34;</span><span class="nv">$drive</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>SSH Port: <span class="s2">&#34;</span><span class="nv">$ssh_port</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>:: NOTE: Make sure ONLY ONE .qcow2 file is<span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>in the current directory<span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>:: Tip: Try setting DNS manually <span class="k">if</span> QEMU user network doesn<span class="se">\&#39;</span>t work well. <span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="si">${</span><span class="nv">YELLOW</span><span class="si">}</span>:: HOWTO -<span class="se">\&gt;</span> https://serverfault.com/a/810639 <span class="si">${</span><span class="nv">RESTORE</span><span class="si">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sleep <span class="m">2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">eval</span> <span class="nv">$cmd</span>
</span></span></code></pre></div><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/17-24-09-3656ec35f835ab8650a4628ceac4dad8-20240618172408-48a595.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-24-09-3656ec35f835ab8650a4628ceac4dad8-20240618172408-48a595.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>
]]></content:encoded>
    </item>
    <item>
      <title>Google Play 改区</title>
      <link>https://lifeislife.cn/posts/google-play-%E6%94%B9%E5%8C%BA/</link>
      <pubDate>Fri, 14 Jun 2024 15:03:35 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/google-play-%E6%94%B9%E5%8C%BA/</guid>
      <description>&lt;h1 id=&#34;google-play-改区&#34;&gt;Google Play 改区&lt;/h1&gt;
&lt;p&gt;第一步登录 &lt;a href=&#34;https://pay.google.com/&#34;&gt;Google Pay&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//2024/06/14/9a6c2e3777731d27ef47d33cb95c8a0c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/9a6c2e3777731d27ef47d33cb95c8a0c.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;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//2024/06/14/416db3056aacd9cc840f3b8416706ad0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/416db3056aacd9cc840f3b8416706ad0.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;第三步添加信用卡或者借记卡，填写卡号、有效期、CVV、姓名、地址等信息。所有信息可以使用&lt;a href=&#34;https://meiguodizhi.com/&#34;&gt;美国地址&lt;/a&gt;生成。美国地址只是网站的名称，根据实际情况可以选择其他国家的地址。&lt;/p&gt;
&lt;p&gt;需要注意的是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;你要改哪个国家就选择哪个国家，并且生成对应国家的地址。&lt;/li&gt;
&lt;li&gt;地址第一行和地址第二行可以随便填。&lt;/li&gt;
&lt;li&gt;市/区、州/省、邮编、必须使用生成的地址。&lt;/li&gt;
&lt;/ol&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//2024/06/14/9e1f29106a98d1523c0ad1997c2ba6b8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/9e1f29106a98d1523c0ad1997c2ba6b8.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;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//2024/06/14/fad550a9a2368632bfea7b9630bd27de.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/fad550a9a2368632bfea7b9630bd27de.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//2024/06/14/5936fc2ff0b8cecbda4d9657e5d34964.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/5936fc2ff0b8cecbda4d9657e5d34964.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;第四步添加完成后点击保存，此时底部会有弹窗提示，如图所示。这是正常现象，点击继续。关闭窗口到Google Pay查看地区已经完成更改。&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//2024/06/14/387bb8e143fa033d297fc16ecb8090ad.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/387bb8e143fa033d297fc16ecb8090ad.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;如果不巧，你也跟我一样没能修改成功，可以尝试在手机端登录Google Play，在手机端添加付款方式。添加方式和电脑端一样，只是在手机端添加更容易成功。&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//2024/06/14/c1d58ff3b9a2af365173c4a1e8788a59.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/c1d58ff3b9a2af365173c4a1e8788a59.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//2024/06/14/3cb6f585435ad59e4cb577ed5b4a2361.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/3cb6f585435ad59e4cb577ed5b4a2361.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//2024/06/14/da8f883bc9c529b3736e802830eef028.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/da8f883bc9c529b3736e802830eef028.jpg&#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;
</description>
      <content:encoded><![CDATA[<h1 id="google-play-改区">Google Play 改区</h1>
<p>第一步登录 <a href="https://pay.google.com/">Google Pay</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//2024/06/14/9a6c2e3777731d27ef47d33cb95c8a0c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/9a6c2e3777731d27ef47d33cb95c8a0c.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>
<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//2024/06/14/416db3056aacd9cc840f3b8416706ad0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/416db3056aacd9cc840f3b8416706ad0.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>第三步添加信用卡或者借记卡，填写卡号、有效期、CVV、姓名、地址等信息。所有信息可以使用<a href="https://meiguodizhi.com/">美国地址</a>生成。美国地址只是网站的名称，根据实际情况可以选择其他国家的地址。</p>
<p>需要注意的是：</p>
<ol>
<li>你要改哪个国家就选择哪个国家，并且生成对应国家的地址。</li>
<li>地址第一行和地址第二行可以随便填。</li>
<li>市/区、州/省、邮编、必须使用生成的地址。</li>
</ol>
<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//2024/06/14/9e1f29106a98d1523c0ad1997c2ba6b8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/9e1f29106a98d1523c0ad1997c2ba6b8.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>
<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//2024/06/14/fad550a9a2368632bfea7b9630bd27de.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/fad550a9a2368632bfea7b9630bd27de.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//2024/06/14/5936fc2ff0b8cecbda4d9657e5d34964.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/5936fc2ff0b8cecbda4d9657e5d34964.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>第四步添加完成后点击保存，此时底部会有弹窗提示，如图所示。这是正常现象，点击继续。关闭窗口到Google Pay查看地区已经完成更改。</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//2024/06/14/387bb8e143fa033d297fc16ecb8090ad.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/387bb8e143fa033d297fc16ecb8090ad.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>如果不巧，你也跟我一样没能修改成功，可以尝试在手机端登录Google Play，在手机端添加付款方式。添加方式和电脑端一样，只是在手机端添加更容易成功。</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//2024/06/14/c1d58ff3b9a2af365173c4a1e8788a59.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/c1d58ff3b9a2af365173c4a1e8788a59.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//2024/06/14/3cb6f585435ad59e4cb577ed5b4a2361.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/3cb6f585435ad59e4cb577ed5b4a2361.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//2024/06/14/da8f883bc9c529b3736e802830eef028.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/14/da8f883bc9c529b3736e802830eef028.jpg" 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>
]]></content:encoded>
    </item>
    <item>
      <title>VSCode关闭滚动条预览</title>
      <link>https://lifeislife.cn/posts/vscode%E5%85%B3%E9%97%AD%E6%BB%9A%E5%8A%A8%E6%9D%A1%E9%A2%84%E8%A7%88/</link>
      <pubDate>Thu, 13 Jun 2024 17:02:36 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/vscode%E5%85%B3%E9%97%AD%E6%BB%9A%E5%8A%A8%E6%9D%A1%E9%A2%84%E8%A7%88/</guid>
      <description>&lt;p&gt;VSCode滚动条左侧有较宽的滚动条，这玩意叫小地图Minimap，作用和游戏里的小地图类似，就是为了看一下整个代码结构，便于快速定位，但是我习惯用搜索，觉得这玩意太占地方了，可以在设置里搜索Minimap进行关闭，或者设置为自动隐藏。&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//2024/06/13/ce2107079ac66fccc4da8bee943287e5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/ce2107079ac66fccc4da8bee943287e5.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//2024/06/13/f0c97573a783141a977601dfedd1e5d6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/f0c97573a783141a977601dfedd1e5d6.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;
</description>
      <content:encoded><![CDATA[<p>VSCode滚动条左侧有较宽的滚动条，这玩意叫小地图Minimap，作用和游戏里的小地图类似，就是为了看一下整个代码结构，便于快速定位，但是我习惯用搜索，觉得这玩意太占地方了，可以在设置里搜索Minimap进行关闭，或者设置为自动隐藏。</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//2024/06/13/ce2107079ac66fccc4da8bee943287e5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/ce2107079ac66fccc4da8bee943287e5.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//2024/06/13/f0c97573a783141a977601dfedd1e5d6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/f0c97573a783141a977601dfedd1e5d6.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>
]]></content:encoded>
    </item>
    <item>
      <title>FRP 内网穿透</title>
      <link>https://lifeislife.cn/posts/frp%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F/</link>
      <pubDate>Mon, 01 Apr 2024 21:08:03 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/frp%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F/</guid>
      <description>&lt;p&gt;懒得自己搭建了，还要准备一台公网服务器，直接找第三方服务商。签到免费送流量，不过流量不多，不过用来穿透个小服务还是够的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://chickfrp.com?affcode=2649EJBPNQ&#34;&gt;https://chickfrp.com?affcode=2649EJBPNQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://console.openfrp.net/&#34;&gt;https://console.openfrp.net/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;创建隧道，下载配置文件如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;common]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;protocol=tcp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;server_addr=ali-shanghai-a.chickfrp.com&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;server_port=7000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;user=xxxxxxxxx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;token=xxxxxxxx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;tcp_mux=true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;openai ]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;type= tcp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;remote_port= 10006&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;local_ip= 192.168.1.9&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;local_port= 3322&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;use_compression=false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;use_encryption=false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置客户端，下面以 Linux 为例，因为需要配置 Docker 服务，所以做个简单记录。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 下载 frp 应用，解压后 frpc 是客户端，frps 是服务端，今天我们只用到 frpc&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;wget https://github.com/fatedier/frp/releases/download/v0.56.0/frp_0.56.0_linux_amd64.tar.gz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;tar zxvf frp_0.56.0_linux_amd64.tar.gz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将服务商提供的配置文件复制到&lt;code&gt;frpc.toml&lt;/code&gt;中。服务商提供的可能是旧版本的 frp 配置，是&lt;code&gt;ini&lt;/code&gt;格式，需要转换成&lt;code&gt;toml&lt;/code&gt;格式。有能力可以自己稍微改一下格式就行，不会的话，可以用在线转换工具，比如：https://toml.info/zh/ini-to-toml。&lt;/p&gt;
&lt;p&gt;简单启动：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./frpc  -c ./frpc.toml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2024-04-01 21:04:29.326 &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;I&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;client/control.go:170&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;19341bf77b5454753237&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;SDSDasdaderf.openai&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; start proxy success
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置 Docker-compose 一键启动，将配置文件&lt;code&gt;frpc.toml&lt;/code&gt;映射到容器中。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;3.7&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;frp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;stilleshan/frpc:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;frp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/path/to/frpc.toml:/frp/frpc.toml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;docker-compose up -d&lt;/code&gt;启动容器。&lt;code&gt;docker logs frp&lt;/code&gt;查看日志。出现&lt;code&gt;start proxy success&lt;/code&gt;表示成功。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>懒得自己搭建了，还要准备一台公网服务器，直接找第三方服务商。签到免费送流量，不过流量不多，不过用来穿透个小服务还是够的。</p>
<ul>
<li><a href="https://chickfrp.com?affcode=2649EJBPNQ">https://chickfrp.com?affcode=2649EJBPNQ</a></li>
<li><a href="https://console.openfrp.net/">https://console.openfrp.net/</a></li>
</ul>
<p>创建隧道，下载配置文件如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="p">[</span><span class="l">common]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">protocol=tcp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">server_addr=ali-shanghai-a.chickfrp.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">server_port=7000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">user=xxxxxxxxx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">token=xxxxxxxx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">tcp_mux=true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">[</span><span class="w"> </span><span class="l">openai ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">type= tcp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">remote_port= 10006</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">local_ip= 192.168.1.9</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">local_port= 3322</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">use_compression=false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">use_encryption=false</span><span class="w">
</span></span></span></code></pre></div><p>配置客户端，下面以 Linux 为例，因为需要配置 Docker 服务，所以做个简单记录。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># 下载 frp 应用，解压后 frpc 是客户端，frps 是服务端，今天我们只用到 frpc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">wget https://github.com/fatedier/frp/releases/download/v0.56.0/frp_0.56.0_linux_amd64.tar.gz</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">tar zxvf frp_0.56.0_linux_amd64.tar.gz</span><span class="w">
</span></span></span></code></pre></div><p>将服务商提供的配置文件复制到<code>frpc.toml</code>中。服务商提供的可能是旧版本的 frp 配置，是<code>ini</code>格式，需要转换成<code>toml</code>格式。有能力可以自己稍微改一下格式就行，不会的话，可以用在线转换工具，比如：https://toml.info/zh/ini-to-toml。</p>
<p>简单启动：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./frpc  -c ./frpc.toml
</span></span><span class="line"><span class="cl">2024-04-01 21:04:29.326 <span class="o">[</span>I<span class="o">]</span> <span class="o">[</span>client/control.go:170<span class="o">]</span> <span class="o">[</span>19341bf77b5454753237<span class="o">]</span> <span class="o">[</span>SDSDasdaderf.openai<span class="o">]</span> start proxy success
</span></span></code></pre></div><p>配置 Docker-compose 一键启动，将配置文件<code>frpc.toml</code>映射到容器中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3.7&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">frp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">stilleshan/frpc:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">frp</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/path/to/frpc.toml:/frp/frpc.toml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span></code></pre></div><p><code>docker-compose up -d</code>启动容器。<code>docker logs frp</code>查看日志。出现<code>start proxy success</code>表示成功。</p>
]]></content:encoded>
    </item>
    <item>
      <title>BootROM 中 SPI 如何适配不同厂家的 Flash 芯片</title>
      <link>https://lifeislife.cn/posts/bootrom%E4%B8%ADspi%E5%A6%82%E4%BD%95%E9%80%82%E9%85%8D%E4%B8%8D%E5%90%8C%E5%8E%82%E5%AE%B6%E7%9A%84flash%E8%8A%AF%E7%89%87/</link>
      <pubDate>Sat, 30 Mar 2024 21:38:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/bootrom%E4%B8%ADspi%E5%A6%82%E4%BD%95%E9%80%82%E9%85%8D%E4%B8%8D%E5%90%8C%E5%8E%82%E5%AE%B6%E7%9A%84flash%E8%8A%AF%E7%89%87/</guid>
      <description>&lt;h1 id=&#34;问题背景&#34;&gt;问题背景&lt;/h1&gt;
&lt;p&gt;一款 SoC 芯片，会支持多种方式启动，比如从 NAND Flash、SPI Flash、eMMC、USB、UART 启动。对于 SPI Flash 启动，BootROM 需要知道 SPI Flash 的型号、容量、页大小、擦除大小等信息，以便正确读取、写入、擦除 SPI Flash。但是，不同厂家的 SPI Flash，这些参数可能不同，而 ROM 中的代码是无法修改的，并且容量有限，如何以最小的代码量，适配不同厂家的 SPI Flash 呢？&lt;/p&gt;
&lt;h1 id=&#34;spi-开发流程&#34;&gt;SPI 开发流程&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SPI 控制器初始&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SPI 控制器有自己的寄存器，可以配置 SPI 的页，块的擦除时间，是否需要 DMA，是否开启中断，单次读写的大小等等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 SPI 对 Flash 进行配置&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BootROM 的 SPI 支持三种模式：Single、Dual、Quad，这不仅要配置 SPI 本身的寄存器，也需要配置 Flash 的寄存器。配置 Flash 的寄存器就需要使用 SPI 来完成。
&lt;ul&gt;
&lt;li&gt;BootROM 中配置 SPI 控制器的 &lt;code&gt;SPIC_CSR_01&lt;/code&gt; 寄存器的&lt;code&gt;spi_bus_mode&lt;/code&gt; 位可以配置 SPI 的模式；&lt;/li&gt;
&lt;li&gt;查阅 Flash 手册，找到配置总线模式的寄存器以及操作它的命令，如 WINBOND-W25Q128JW 这款 Flash 的配置寄存器为 &lt;code&gt;Status Register-2&lt;/code&gt;，Flash 的寄存器是无法直接读写的，所以需要在 Flash 手册中找到操作这个寄存器的命令，在手册的 Instructions 章节可以找到这款 Flash 的配置命令为 &lt;code&gt;Write Status Register-2&lt;/code&gt;，命令码为 &lt;code&gt;0x31&lt;/code&gt;，只要我们向 Flash 发送 &lt;code&gt;0x31&lt;/code&gt;，就可以配置 Flash 的总线模式了。


&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/%2F2024%2F03%2F30%2Fe7024b05e381b724d3471744f323de76.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F30%2Fe7024b05e381b724d3471744f323de76.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;!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/%2F2024%2F03%2F30%2F350d91ef1d63ed0c5650e4dfd2c72917.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F30%2F350d91ef1d63ed0c5650e4dfd2c72917.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;/li&gt;
&lt;li&gt;如何向 Flash 发送指令？需要将真正要发送的指令写入 SPI 的寄存器&lt;code&gt;SPIC_CSR_06&lt;/code&gt;，该寄存器的各个 Bit 可以配置命令码，命令类型，命令是否有效等信息。我们将之前的命令码&lt;code&gt;0x31&lt;/code&gt;写入到&lt;code&gt;SPIC_CSR_06&lt;/code&gt;的&lt;code&gt;command_code&lt;/code&gt;位，然后将&lt;code&gt;SPIC_CSR_06&lt;/code&gt;的&lt;code&gt;command_type&lt;/code&gt;位设置为&lt;code&gt;write status register&lt;/code&gt;，SPI 就会自动将这些信息发送给 Flash。Flash 接收到命令后，会根据命令码执行配置寄存器的操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SPI 读写 Flash&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;和配置 Flash 总线模式一样，读写 Flash 也需要配置 SPI 控制器的寄存器，以及配置 Flash 的寄存器。SPI 控制器的寄存器配置和配置 Flash 总线模式一样，只是命令码和命令类型不同。稍有不同的是需要先配置好 SPI 寄存器，配置读写的大小以及读写的地址，然后再配置&lt;code&gt;SPIC_CSR_06&lt;/code&gt;。Flash接收到命令后，会根据命令码执行读写操作。主设备只需要读写 Flash 的指定地址即可读写到数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;spi-驱动如何适配不同的-flash&#34;&gt;SPI 驱动如何适配不同的 Flash&lt;/h1&gt;
&lt;p&gt;在开发流程中我们并未考虑不同厂家的 Flash 如何操作，只是关心了 WINBOND-W25Q128JW 这款 Flash 的操作。在操作过程中我们可以发现，不同厂家的 Flash 的操作基本一致，只是命令码、页大小、擦除大小等参数不同。我们只要针对不同的 Flash，配置不同的参数即可。但是，如何知道当前 Flash 的参数呢？这就需要用到 SFDP 了。&lt;/p&gt;
&lt;p&gt;SFDP 是 JEDEC 发布的 JESD216 的一个新标准，目前的版本号是 V1.0。简而言之，SFDP（Serial Flash Discoverable Parameters）就相当于一张存储了 FLASH 部分属性的表，此表是不占用 FLASH 本身的存储空间的。SFDP 中的信息自出厂就被固定，只供读取，开发人员可通过发送操作指令 0x5A 来读取当前 FLASH 的 SFDP 相关内容，这有利于开发人员了解 FLASH 之间的差异，提高开发效率，缩短整个开发周期。&lt;/p&gt;
&lt;p&gt;也就是只要 Flash 研发时遵循了 JESD216 这个标准，我们都可以通过发送 0x5A 这个命令码，去获取Flash相关参数。最为关键的就是其中会保存 Flash 厂商的 ID，当我们获取到 Flash 厂商的 ID 时，我们就可以根据 ID 执行不同的配置操作。&lt;/p&gt;
&lt;p&gt;对于 WINBOND-W25Q128JW 这款 Flash，在数据手册的Feature里就可以看到它是支持 SFDP 的：


&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/%2F2024%2F03%2F30%2Fbe54a549cc9266d120b1e2db37d68555.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F30%2Fbe54a549cc9266d120b1e2db37d68555.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;
</description>
      <content:encoded><![CDATA[<h1 id="问题背景">问题背景</h1>
<p>一款 SoC 芯片，会支持多种方式启动，比如从 NAND Flash、SPI Flash、eMMC、USB、UART 启动。对于 SPI Flash 启动，BootROM 需要知道 SPI Flash 的型号、容量、页大小、擦除大小等信息，以便正确读取、写入、擦除 SPI Flash。但是，不同厂家的 SPI Flash，这些参数可能不同，而 ROM 中的代码是无法修改的，并且容量有限，如何以最小的代码量，适配不同厂家的 SPI Flash 呢？</p>
<h1 id="spi-开发流程">SPI 开发流程</h1>
<ul>
<li>
<p>SPI 控制器初始</p>
<ul>
<li>SPI 控制器有自己的寄存器，可以配置 SPI 的页，块的擦除时间，是否需要 DMA，是否开启中断，单次读写的大小等等。</li>
</ul>
</li>
<li>
<p>使用 SPI 对 Flash 进行配置</p>
<ul>
<li>BootROM 的 SPI 支持三种模式：Single、Dual、Quad，这不仅要配置 SPI 本身的寄存器，也需要配置 Flash 的寄存器。配置 Flash 的寄存器就需要使用 SPI 来完成。
<ul>
<li>BootROM 中配置 SPI 控制器的 <code>SPIC_CSR_01</code> 寄存器的<code>spi_bus_mode</code> 位可以配置 SPI 的模式；</li>
<li>查阅 Flash 手册，找到配置总线模式的寄存器以及操作它的命令，如 WINBOND-W25Q128JW 这款 Flash 的配置寄存器为 <code>Status Register-2</code>，Flash 的寄存器是无法直接读写的，所以需要在 Flash 手册中找到操作这个寄存器的命令，在手册的 Instructions 章节可以找到这款 Flash 的配置命令为 <code>Write Status Register-2</code>，命令码为 <code>0x31</code>，只要我们向 Flash 发送 <code>0x31</code>，就可以配置 Flash 的总线模式了。


<!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/%2F2024%2F03%2F30%2Fe7024b05e381b724d3471744f323de76.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F30%2Fe7024b05e381b724d3471744f323de76.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>


<!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/%2F2024%2F03%2F30%2F350d91ef1d63ed0c5650e4dfd2c72917.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F30%2F350d91ef1d63ed0c5650e4dfd2c72917.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></li>
<li>如何向 Flash 发送指令？需要将真正要发送的指令写入 SPI 的寄存器<code>SPIC_CSR_06</code>，该寄存器的各个 Bit 可以配置命令码，命令类型，命令是否有效等信息。我们将之前的命令码<code>0x31</code>写入到<code>SPIC_CSR_06</code>的<code>command_code</code>位，然后将<code>SPIC_CSR_06</code>的<code>command_type</code>位设置为<code>write status register</code>，SPI 就会自动将这些信息发送给 Flash。Flash 接收到命令后，会根据命令码执行配置寄存器的操作。</li>
</ul>
</li>
</ul>
</li>
<li>
<p>SPI 读写 Flash</p>
<ul>
<li>和配置 Flash 总线模式一样，读写 Flash 也需要配置 SPI 控制器的寄存器，以及配置 Flash 的寄存器。SPI 控制器的寄存器配置和配置 Flash 总线模式一样，只是命令码和命令类型不同。稍有不同的是需要先配置好 SPI 寄存器，配置读写的大小以及读写的地址，然后再配置<code>SPIC_CSR_06</code>。Flash接收到命令后，会根据命令码执行读写操作。主设备只需要读写 Flash 的指定地址即可读写到数据。</li>
</ul>
</li>
</ul>
<h1 id="spi-驱动如何适配不同的-flash">SPI 驱动如何适配不同的 Flash</h1>
<p>在开发流程中我们并未考虑不同厂家的 Flash 如何操作，只是关心了 WINBOND-W25Q128JW 这款 Flash 的操作。在操作过程中我们可以发现，不同厂家的 Flash 的操作基本一致，只是命令码、页大小、擦除大小等参数不同。我们只要针对不同的 Flash，配置不同的参数即可。但是，如何知道当前 Flash 的参数呢？这就需要用到 SFDP 了。</p>
<p>SFDP 是 JEDEC 发布的 JESD216 的一个新标准，目前的版本号是 V1.0。简而言之，SFDP（Serial Flash Discoverable Parameters）就相当于一张存储了 FLASH 部分属性的表，此表是不占用 FLASH 本身的存储空间的。SFDP 中的信息自出厂就被固定，只供读取，开发人员可通过发送操作指令 0x5A 来读取当前 FLASH 的 SFDP 相关内容，这有利于开发人员了解 FLASH 之间的差异，提高开发效率，缩短整个开发周期。</p>
<p>也就是只要 Flash 研发时遵循了 JESD216 这个标准，我们都可以通过发送 0x5A 这个命令码，去获取Flash相关参数。最为关键的就是其中会保存 Flash 厂商的 ID，当我们获取到 Flash 厂商的 ID 时，我们就可以根据 ID 执行不同的配置操作。</p>
<p>对于 WINBOND-W25Q128JW 这款 Flash，在数据手册的Feature里就可以看到它是支持 SFDP 的：


<!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/%2F2024%2F03%2F30%2Fbe54a549cc9266d120b1e2db37d68555.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F30%2Fbe54a549cc9266d120b1e2db37d68555.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>
]]></content:encoded>
    </item>
    <item>
      <title>GitHub Copilot CLI命令行AI工具</title>
      <link>https://lifeislife.cn/posts/github-copilot-cli%E5%91%BD%E4%BB%A4%E8%A1%8Cai%E5%B7%A5%E5%85%B7/</link>
      <pubDate>Sun, 24 Mar 2024 09:44:16 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/github-copilot-cli%E5%91%BD%E4%BB%A4%E8%A1%8Cai%E5%B7%A5%E5%85%B7/</guid>
      <description>&lt;p&gt;GitHub Copilot CLI 是一个命令行工具，它允许你在终端中使用 GitHub Copilot。你可以使用它来获取代码建议，这些建议是由 OpenAI 的 GPT-4 模型生成的。这个工具可以在任何支持命令行的环境中使用，包括 Visual Studio Code 的集成终端。&lt;/p&gt;
&lt;p&gt;参考官方文档：&lt;a href=&#34;https://docs.github.com/en/copilot/github-copilot-in-the-cli/using-github-copilot-in-the-cli&#34;&gt;Using GitHub Copilot in the CLI - GitHub Docs&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;安装&#34;&gt;安装&lt;/h1&gt;
&lt;p&gt;前提：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;需要订阅Github Copilot，每月$10。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;需要安装GH CLI&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;订阅自行解决，接下来安装GH CLI：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mkdir -p -m &lt;span class=&#34;m&#34;&gt;755&lt;/span&gt; /etc/apt/keyrings &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg &amp;gt; /dev/null &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deb [arch=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;dpkg --print-architecture&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee /etc/apt/sources.list.d/github-cli.list &amp;gt; /dev/null &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt update &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt install gh -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果没法安装，可以下载deb文件手动安装。&lt;a href=&#34;https://github.com/cli/cli/releases/tag/v2.46.0&#34;&gt;Release GitHub CLI 2.46.0 · cli/cli&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;安装完GH CLI后，安装Copilot CLI：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gh auth login
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 选择登录账号类型为Github.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;? What account &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; you want to log into?  &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Use arrows to move, &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; to filter&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; GitHub.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  GitHub Enterprise Server
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 选择传输协议为SSH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;? What is your preferred protocol &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; Git operations on this host?  &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Use arrows to move, &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; to filter&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  HTTPS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; SSH
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 选择一个本地的公钥，并命名SSH key的名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;? Title &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; your SSH key: &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;GitHub CLI&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; wsl2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 选择验证方式为使用浏览器验证&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;? How would you like to authenticate GitHub CLI?  &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Use arrows to move, &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; to filter&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; Login with a web browser
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Paste an authentication token
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;! First copy your one-time code: 7AAA-SA47
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 回车后会弹出浏览器，输入验证码即可&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Press Enter to open github.com in your browser...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;! Failed opening a web browser at https://github.com/login/device
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  fork/exec /usr/bin/winchrome: &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; format error
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Please try entering the URL in your browser manually
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;WSL2 环境下可能会出现无法打开浏览器的情况，可以手动打开浏览器输入地址验证https://github.com/login/device。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;使用&#34;&gt;使用&lt;/h1&gt;
&lt;p&gt;支持两个命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 解释代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gh copilot explain
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 获取命令行建议&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gh copilot suggest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gh copilot explain &lt;span class=&#34;s2&#34;&gt;&amp;#34;sudo apt-get&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Welcome to GitHub Copilot in the CLI!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;version 1.0.1 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2024-03-22&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;I&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;m powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve. For more information, see https://gh.io/gh-copilot-transparency
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Explanation:                                                                                                                                                                       
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                                                                                                                                                   
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  • sudo is used to run a &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt; with elevated rights, typically as a superuser.                                                                                                  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    • apt-get is the Ubuntu package manager.                                                                                                                                       
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      • It is used to manage packages on the system, including installing, updating, and removing software packages.                                                               
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      • It requires administrative privileges to perform these operations.                                                                                                         
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      • Additional sub-commands can be used with apt-get to perform specific tasks, such as install, remove, update, etc.                                                          
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gh copilot suggest &lt;span class=&#34;s2&#34;&gt;&amp;#34;Install git&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Welcome to GitHub Copilot in the CLI!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;version 1.0.1 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2024-03-22&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;I&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;m powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve. For more information, see https://gh.io/gh-copilot-transparency
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;? What kind of &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt; can I &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt; you with?  &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Use arrows to move, &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; to filter&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; generic shell &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  gh &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  git &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Suggestion:                                                                                                                                                                        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  sudo apt-get install git                                                                                                                                                     
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;? Select an option  &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Use arrows to move, &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; to filter&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; Copy &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt; to clipboard
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Explain &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Execute &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Revise &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Rate response
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Exit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;gh copilot 默认提供了这两个命令的别名，无需输入完整命令，可以直接使用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ghce 为 gh copilot explain 的别名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ghce &lt;span class=&#34;s2&#34;&gt;&amp;#34;sudo apt-get&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ghcs 为 gh copilot suggest 的别名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ghcs &lt;span class=&#34;s2&#34;&gt;&amp;#34;install git&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为了便于在每个终端中使用 gh copilot cli，将以下配置放到配置文件中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;eval &amp;#34;$(gh copilot alias -- zsh)&amp;#34;&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.zshrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>GitHub Copilot CLI 是一个命令行工具，它允许你在终端中使用 GitHub Copilot。你可以使用它来获取代码建议，这些建议是由 OpenAI 的 GPT-4 模型生成的。这个工具可以在任何支持命令行的环境中使用，包括 Visual Studio Code 的集成终端。</p>
<p>参考官方文档：<a href="https://docs.github.com/en/copilot/github-copilot-in-the-cli/using-github-copilot-in-the-cli">Using GitHub Copilot in the CLI - GitHub Docs</a></p>
<h1 id="安装">安装</h1>
<p>前提：</p>
<ol>
<li>
<p>需要订阅Github Copilot，每月$10。</p>
</li>
<li>
<p>需要安装GH CLI</p>
</li>
</ol>
<p>订阅自行解决，接下来安装GH CLI：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo mkdir -p -m <span class="m">755</span> /etc/apt/keyrings <span class="o">&amp;&amp;</span> wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg <span class="p">|</span> sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg &gt; /dev/null <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="o">&amp;&amp;</span> sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">&#34;deb [arch=</span><span class="k">$(</span>dpkg --print-architecture<span class="k">)</span><span class="s2"> signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main&#34;</span> <span class="p">|</span> sudo tee /etc/apt/sources.list.d/github-cli.list &gt; /dev/null <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="o">&amp;&amp;</span> sudo apt update <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="o">&amp;&amp;</span> sudo apt install gh -y
</span></span></code></pre></div><p>如果没法安装，可以下载deb文件手动安装。<a href="https://github.com/cli/cli/releases/tag/v2.46.0">Release GitHub CLI 2.46.0 · cli/cli</a></p>
<p>安装完GH CLI后，安装Copilot CLI：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ gh auth login
</span></span><span class="line"><span class="cl"><span class="c1"># 选择登录账号类型为Github.com</span>
</span></span><span class="line"><span class="cl">? What account <span class="k">do</span> you want to log into?  <span class="o">[</span>Use arrows to move, <span class="nb">type</span> to filter<span class="o">]</span>
</span></span><span class="line"><span class="cl">&gt; GitHub.com
</span></span><span class="line"><span class="cl">  GitHub Enterprise Server
</span></span><span class="line"><span class="cl"><span class="c1"># 选择传输协议为SSH</span>
</span></span><span class="line"><span class="cl">? What is your preferred protocol <span class="k">for</span> Git operations on this host?  <span class="o">[</span>Use arrows to move, <span class="nb">type</span> to filter<span class="o">]</span>
</span></span><span class="line"><span class="cl">  HTTPS
</span></span><span class="line"><span class="cl">&gt; SSH
</span></span><span class="line"><span class="cl"><span class="c1"># 选择一个本地的公钥，并命名SSH key的名称</span>
</span></span><span class="line"><span class="cl">? Title <span class="k">for</span> your SSH key: <span class="o">(</span>GitHub CLI<span class="o">)</span> wsl2
</span></span><span class="line"><span class="cl"><span class="c1"># 选择验证方式为使用浏览器验证</span>
</span></span><span class="line"><span class="cl">? How would you like to authenticate GitHub CLI?  <span class="o">[</span>Use arrows to move, <span class="nb">type</span> to filter<span class="o">]</span>
</span></span><span class="line"><span class="cl">&gt; Login with a web browser
</span></span><span class="line"><span class="cl">  Paste an authentication token
</span></span><span class="line"><span class="cl">! First copy your one-time code: 7AAA-SA47
</span></span><span class="line"><span class="cl"><span class="c1"># 回车后会弹出浏览器，输入验证码即可</span>
</span></span><span class="line"><span class="cl">Press Enter to open github.com in your browser...
</span></span><span class="line"><span class="cl">! Failed opening a web browser at https://github.com/login/device
</span></span><span class="line"><span class="cl">  fork/exec /usr/bin/winchrome: <span class="nb">exec</span> format error
</span></span><span class="line"><span class="cl">  Please try entering the URL in your browser manually
</span></span></code></pre></div><blockquote>
<p>WSL2 环境下可能会出现无法打开浏览器的情况，可以手动打开浏览器输入地址验证https://github.com/login/device。</p>
</blockquote>
<h1 id="使用">使用</h1>
<p>支持两个命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 解释代码</span>
</span></span><span class="line"><span class="cl">gh copilot explain
</span></span><span class="line"><span class="cl"><span class="c1"># 获取命令行建议</span>
</span></span><span class="line"><span class="cl">gh copilot suggest
</span></span></code></pre></div><p>示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ gh copilot explain <span class="s2">&#34;sudo apt-get&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Welcome to GitHub Copilot in the CLI!
</span></span><span class="line"><span class="cl">version 1.0.1 <span class="o">(</span>2024-03-22<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">I<span class="err">&#39;</span>m powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve. For more information, see https://gh.io/gh-copilot-transparency
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Explanation:                                                                                                                                                                       
</span></span><span class="line"><span class="cl">                                                                                                                                                                                   
</span></span><span class="line"><span class="cl">  • sudo is used to run a <span class="nb">command</span> with elevated rights, typically as a superuser.                                                                                                  
</span></span><span class="line"><span class="cl">    • apt-get is the Ubuntu package manager.                                                                                                                                       
</span></span><span class="line"><span class="cl">      • It is used to manage packages on the system, including installing, updating, and removing software packages.                                                               
</span></span><span class="line"><span class="cl">      • It requires administrative privileges to perform these operations.                                                                                                         
</span></span><span class="line"><span class="cl">      • Additional sub-commands can be used with apt-get to perform specific tasks, such as install, remove, update, etc.                                                          
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ gh copilot suggest <span class="s2">&#34;Install git&#34;</span>
</span></span><span class="line"><span class="cl">Welcome to GitHub Copilot in the CLI!
</span></span><span class="line"><span class="cl">version 1.0.1 <span class="o">(</span>2024-03-22<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">I<span class="err">&#39;</span>m powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve. For more information, see https://gh.io/gh-copilot-transparency
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">? What kind of <span class="nb">command</span> can I <span class="nb">help</span> you with?  <span class="o">[</span>Use arrows to move, <span class="nb">type</span> to filter<span class="o">]</span>
</span></span><span class="line"><span class="cl">&gt; generic shell <span class="nb">command</span>
</span></span><span class="line"><span class="cl">  gh <span class="nb">command</span>
</span></span><span class="line"><span class="cl">  git <span class="nb">command</span>
</span></span><span class="line"><span class="cl">Suggestion:                                                                                                                                                                        
</span></span><span class="line"><span class="cl">  sudo apt-get install git                                                                                                                                                     
</span></span><span class="line"><span class="cl">? Select an option  <span class="o">[</span>Use arrows to move, <span class="nb">type</span> to filter<span class="o">]</span>
</span></span><span class="line"><span class="cl">&gt; Copy <span class="nb">command</span> to clipboard
</span></span><span class="line"><span class="cl">  Explain <span class="nb">command</span>
</span></span><span class="line"><span class="cl">  Execute <span class="nb">command</span>
</span></span><span class="line"><span class="cl">  Revise <span class="nb">command</span>
</span></span><span class="line"><span class="cl">  Rate response
</span></span><span class="line"><span class="cl">  Exit
</span></span></code></pre></div><p>gh copilot 默认提供了这两个命令的别名，无需输入完整命令，可以直接使用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># ghce 为 gh copilot explain 的别名</span>
</span></span><span class="line"><span class="cl">ghce <span class="s2">&#34;sudo apt-get&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ghcs 为 gh copilot suggest 的别名</span>
</span></span><span class="line"><span class="cl">ghcs <span class="s2">&#34;install git&#34;</span>
</span></span></code></pre></div><p>为了便于在每个终端中使用 gh copilot cli，将以下配置放到配置文件中：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;eval &#34;$(gh copilot alias -- zsh)&#34;&#39;</span> &gt;&gt; ~/.zshrc
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>RemoteX11 远程调试带GUI应用</title>
      <link>https://lifeislife.cn/posts/remotex11-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95%E5%B8%A6gui%E5%BA%94%E7%94%A8/</link>
      <pubDate>Sun, 03 Mar 2024 20:45:56 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/remotex11-%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95%E5%B8%A6gui%E5%BA%94%E7%94%A8/</guid>
      <description>&lt;p&gt;Windows上通过WSL2进行Linux开发，但是有时候需要开发带GUI的引用，这样就需要将图像转发。&lt;/p&gt;
&lt;h2 id=&#34;配置windows&#34;&gt;配置Windows&lt;/h2&gt;
&lt;p&gt;下载安装XMing，启动Xlaunch。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;选择MultiWindow&lt;/li&gt;
&lt;li&gt;设置Display number为10（可以自行设置，主要是需要和后面在WSL2中设置的变量保持一致）&lt;/li&gt;
&lt;li&gt;选择Start no client（Windows的XMing是被动等待接收图像数据，所以选择该项）&lt;/li&gt;
&lt;li&gt;一直下一页，其余保持默认，点击完成即可。&lt;/li&gt;
&lt;/ol&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//2024/03/03/b1fa974ebfa7e08576cc61bce4307640.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/b1fa974ebfa7e08576cc61bce4307640.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//2024/03/03/b0a29ceb2b30d4dc68a9d969859698c3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/b0a29ceb2b30d4dc68a9d969859698c3.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;h2 id=&#34;配置vscode&#34;&gt;配置VSCode&lt;/h2&gt;
&lt;p&gt;安装RemoteX11插件，直接在插件中心搜索安装即可。&lt;/p&gt;
&lt;p&gt;打开设置页面，搜索Remote x11，找到如下配置项，将Display Number配置为10&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//2024/03/03/6bf072325b38892eda270dabf4d3a1be.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/6bf072325b38892eda270dabf4d3a1be.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;h2 id=&#34;配置wsl2&#34;&gt;配置WSL2&lt;/h2&gt;
&lt;p&gt;安装xclock用于测试&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install xclock
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;设置环境变量&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;export DISPLAY=localhost:10.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 或者&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;export DISPLAY=:0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行xclock查看结果&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//2024/03/03/2e35553c8672c1bd92e4320b2d88d562.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/2e35553c8672c1bd92e4320b2d88d562.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;
</description>
      <content:encoded><![CDATA[<p>Windows上通过WSL2进行Linux开发，但是有时候需要开发带GUI的引用，这样就需要将图像转发。</p>
<h2 id="配置windows">配置Windows</h2>
<p>下载安装XMing，启动Xlaunch。</p>
<ol>
<li>选择MultiWindow</li>
<li>设置Display number为10（可以自行设置，主要是需要和后面在WSL2中设置的变量保持一致）</li>
<li>选择Start no client（Windows的XMing是被动等待接收图像数据，所以选择该项）</li>
<li>一直下一页，其余保持默认，点击完成即可。</li>
</ol>
<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//2024/03/03/b1fa974ebfa7e08576cc61bce4307640.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/b1fa974ebfa7e08576cc61bce4307640.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//2024/03/03/b0a29ceb2b30d4dc68a9d969859698c3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/b0a29ceb2b30d4dc68a9d969859698c3.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>
<h2 id="配置vscode">配置VSCode</h2>
<p>安装RemoteX11插件，直接在插件中心搜索安装即可。</p>
<p>打开设置页面，搜索Remote x11，找到如下配置项，将Display Number配置为10</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//2024/03/03/6bf072325b38892eda270dabf4d3a1be.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/6bf072325b38892eda270dabf4d3a1be.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>
<h2 id="配置wsl2">配置WSL2</h2>
<p>安装xclock用于测试</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt-get install xclock
</span></span></code></pre></div><p>设置环境变量</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="l">export DISPLAY=localhost:10.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># 或者</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">export DISPLAY=:0</span><span class="w">
</span></span></span></code></pre></div><p>运行xclock查看结果</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//2024/03/03/2e35553c8672c1bd92e4320b2d88d562.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/03/2e35553c8672c1bd92e4320b2d88d562.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>
]]></content:encoded>
    </item>
    <item>
      <title>解决系统依赖错误GLIBCXX_3.4.29 not found</title>
      <link>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3%E7%B3%BB%E7%BB%9F%E4%BE%9D%E8%B5%96%E9%94%99%E8%AF%AFglibcxx-3-4-29-not-found/</link>
      <pubDate>Sat, 02 Mar 2024 22:21:23 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3%E7%B3%BB%E7%BB%9F%E4%BE%9D%E8%B5%96%E9%94%99%E8%AF%AFglibcxx-3-4-29-not-found/</guid>
      <description>&lt;p&gt;以前对软件包的构建不太了解，喜欢随意修改软件源列表，软件源和当前系统的版本不一致就会出现安装了一个依赖较多的软件包后会出现连锁反应，修改了所有依赖的软件包版本，导致系统故障。最常出现的就是修改了GCC版本，导致GLIBCXX版本不一致，导致系统软件无法运行。&lt;/p&gt;
&lt;p&gt;如果你的系统还能正常安装软件，那么修改软件源和当前系统版本保存一致，然后更新软件，并重新安装GCC即可解决问题。具体步骤如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改软件源&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查当前系统版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lsb_release -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将软件源修改为当前系统版本的软件源，Ubuntu系统版本号对应的软件源列表可以在https://wiki.ubuntu.com/Releases查看&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 更新软件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装GCC，build-essential包含了GCC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install build-essential
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你和我一样倒霉，连 apt 都无法使用，那么可以使用 dpkg 命令手动安装 GCC。&lt;/p&gt;
&lt;p&gt;因为误操作在 Ubuntu 20.04 上安装了 Ubuntu 18.04 的 GCC，导致系统软件无法运行，apt 也无法使用，所以只能手动安装 GCC。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt: libx86_64-linux-gnu-libstdc++.so.6: version &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;GLIBCXX_3.4.29&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt; not found &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;required by libx86_64-linux-gnulibapt-private.so.0.0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;既然libstdc++版本不一致，我们就去下载对应版本的GCC，访问https://packages.ubuntu.com/，在下方的搜索框中输入libstdc++6，选择对应的系统版本，然后下载对应的GCC。&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//2024/03/02/64ad9d26ea0d40d39a17330698dd805d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/02/64ad9d26ea0d40d39a17330698dd805d.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;点击搜索结果，点击系统的架构，一般为amd64，&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//2024/03/02/4965061df423ae7897faa87cce2a5fc6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/02/4965061df423ae7897faa87cce2a5fc6.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;具体下载地址比较隐蔽，直接点击红框的链接没有反应，你可以右键另存为到本地，我习惯复制链接后用wget下载。&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//2024/03/02/b69d28c1b5515dd8898213332baa9b99.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/02/b69d28c1b5515dd8898213332baa9b99.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;下载完成后，使用dpkg命令安装GCC。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dpkg -i libstdc++6_12.3.0-1ubuntu1&lt;span class=&#34;se&#34;&gt;\~&lt;/span&gt;22.04_amd64.deb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;之后可以检查一下缺失的GLIBCXX版本已经安装。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ strings /lib/x86_64-linux-gnu/libstdc++.so.6 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep GLIBCXX                               
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.6
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.7
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.9
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.11
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.13
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.14
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.15
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.16
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.17
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.18
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.19
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.20
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.21
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.22
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.23
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.25
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.26
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.27
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.28
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.29
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_3.4.30
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GLIBCXX_DEBUG_MESSAGE_LENGTH
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时apt 应该就可以正常使用了，我们只需要修复一下所有软件包，让它回到正确的版本即可恢复系统。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt --fix-broken install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>以前对软件包的构建不太了解，喜欢随意修改软件源列表，软件源和当前系统的版本不一致就会出现安装了一个依赖较多的软件包后会出现连锁反应，修改了所有依赖的软件包版本，导致系统故障。最常出现的就是修改了GCC版本，导致GLIBCXX版本不一致，导致系统软件无法运行。</p>
<p>如果你的系统还能正常安装软件，那么修改软件源和当前系统版本保存一致，然后更新软件，并重新安装GCC即可解决问题。具体步骤如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 修改软件源</span>
</span></span><span class="line"><span class="cl">sudo vim /etc/apt/sources.list
</span></span><span class="line"><span class="cl"><span class="c1"># 检查当前系统版本</span>
</span></span><span class="line"><span class="cl">lsb_release -a
</span></span><span class="line"><span class="cl"><span class="c1"># 将软件源修改为当前系统版本的软件源，Ubuntu系统版本号对应的软件源列表可以在https://wiki.ubuntu.com/Releases查看</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 更新软件</span>
</span></span><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl"><span class="c1"># 安装GCC，build-essential包含了GCC</span>
</span></span><span class="line"><span class="cl">sudo apt install build-essential
</span></span></code></pre></div><p>如果你和我一样倒霉，连 apt 都无法使用，那么可以使用 dpkg 命令手动安装 GCC。</p>
<p>因为误操作在 Ubuntu 20.04 上安装了 Ubuntu 18.04 的 GCC，导致系统软件无法运行，apt 也无法使用，所以只能手动安装 GCC。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt: libx86_64-linux-gnu-libstdc++.so.6: version <span class="sb">`</span>GLIBCXX_3.4.29<span class="err">&#39;</span> not found <span class="o">(</span>required by libx86_64-linux-gnulibapt-private.so.0.0<span class="o">)</span>
</span></span></code></pre></div><p>既然libstdc++版本不一致，我们就去下载对应版本的GCC，访问https://packages.ubuntu.com/，在下方的搜索框中输入libstdc++6，选择对应的系统版本，然后下载对应的GCC。</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//2024/03/02/64ad9d26ea0d40d39a17330698dd805d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/02/64ad9d26ea0d40d39a17330698dd805d.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>点击搜索结果，点击系统的架构，一般为amd64，</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//2024/03/02/4965061df423ae7897faa87cce2a5fc6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/02/4965061df423ae7897faa87cce2a5fc6.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>具体下载地址比较隐蔽，直接点击红框的链接没有反应，你可以右键另存为到本地，我习惯复制链接后用wget下载。</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//2024/03/02/b69d28c1b5515dd8898213332baa9b99.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/02/b69d28c1b5515dd8898213332baa9b99.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>下载完成后，使用dpkg命令安装GCC。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo dpkg -i libstdc++6_12.3.0-1ubuntu1<span class="se">\~</span>22.04_amd64.deb
</span></span></code></pre></div><p>之后可以检查一下缺失的GLIBCXX版本已经安装。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ strings /lib/x86_64-linux-gnu/libstdc++.so.6 <span class="p">|</span> grep GLIBCXX                               
</span></span><span class="line"><span class="cl">GLIBCXX_3.4
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.1
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.2
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.3
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.4
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.5
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.6
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.7
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.8
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.9
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.10
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.11
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.12
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.13
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.14
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.15
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.16
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.17
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.18
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.19
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.20
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.21
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.22
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.23
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.24
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.25
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.26
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.27
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.28
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.29
</span></span><span class="line"><span class="cl">GLIBCXX_3.4.30
</span></span><span class="line"><span class="cl">GLIBCXX_DEBUG_MESSAGE_LENGTH
</span></span></code></pre></div><p>此时apt 应该就可以正常使用了，我们只需要修复一下所有软件包，让它回到正确的版本即可恢复系统。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt --fix-broken install
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>repo源配置解析</title>
      <link>https://lifeislife.cn/posts/repo%E6%BA%90%E9%85%8D%E7%BD%AE%E8%A7%A3%E6%9E%90/</link>
      <pubDate>Wed, 17 Jan 2024 21:32:04 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/repo%E6%BA%90%E9%85%8D%E7%BD%AE%E8%A7%A3%E6%9E%90/</guid>
      <description>&lt;h1 id=&#34;repo-源配置解析&#34;&gt;repo 源配置解析&lt;/h1&gt;
&lt;p&gt;openEuler 的软件源配置文件位于/etc/yum.repos.d/目录下，以.repo 为后缀名，文件名可以任意取，但是必须以.repo 结尾。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#generic-repos is licensed under the Mulan PSL v2.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#You can use this software according to the terms and conditions of the Mulan PSL v2.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#You may obtain a copy of Mulan PSL v2 at:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#    http://license.coscl.org.cn/MulanPSL2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#THIS SOFTWARE IS PROVIDED ON AN &amp;#34;AS IS&amp;#34; BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#PURPOSE.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#See the Mulan PSL v2 for more details.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[OS]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;name=OS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;baseurl=http://repo.openeuler.org/openEuler-23.09/OS/$basearch/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/OS&amp;amp;arch=$basearch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;metadata_expire=1h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpgcheck=1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpgkey=http://repo.openeuler.org/openEuler-23.09/OS/$basearch/RPM-GPG-KEY-openEuler
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[source]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;name=source
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;baseurl=http://repo.openeuler.org/openEuler-23.09/source/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;metalink=https://mirrors.openeuler.org/metalink?repo=$releasever&amp;amp;arch=source
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;metadata_expire=1h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpgcheck=1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpgkey=http://repo.openeuler.org/openEuler-23.09/source/RPM-GPG-KEY-openEuler
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中各个配置项的含义如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[repoid]中的 repoid 为软件仓库（repository）的 ID 号，所有.repo 配置文件中的各 repoid 不能重复，必须唯一。示例中 repoid 为 OS 和 source。&lt;/li&gt;
&lt;li&gt;name 为软件仓库描述的字符串，可以任意取，但是建议取一个有意义的名称，方便用户理解。示例中 name 为 OS 和 source。&lt;/li&gt;
&lt;li&gt;baseurl 为软件仓库的地址，可以是 http、https、ftp 等协议，也可以是本地目录。&lt;/li&gt;
&lt;li&gt;enabled 为是否启用该软件源仓库，可选值为 1 和 0。默认值为 1，表示启用该软件源仓库。示例中 enabled 为 1。&lt;/li&gt;
&lt;li&gt;metalink 为动态的镜像地址，用于镜像加速。&lt;/li&gt;
&lt;li&gt;metadata_expire 为元数据过期时间，单位为秒。默认值为 90 分钟，即 5400 秒。示例中 metadata_expire 为 1h，即 1 小时。&lt;/li&gt;
&lt;li&gt;gpgcheck 可设置为 1 或 0，1 表示进行 gpg（GNU Private Guard）校验，0 表示不进行 gpg 校验，gpgcheck 可以确定 rpm 包的来源是有效和安全的。&lt;/li&gt;
&lt;li&gt;gpgkey 为验证签名用的公钥地址，如果 gpgcheck 为 1，则必须设置 gpgkey。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;gpgcheck-详解&#34;&gt;gpgcheck 详解&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;RPM-GPG-KEY&lt;/code&gt; 是一个公共密钥，用于验证由该密钥签名的RPM包的真实性和完整性。在使用 &lt;code&gt;yum&lt;/code&gt; 或 &lt;code&gt;dnf&lt;/code&gt; 这样的包管理工具时，这些工具会使用 GPG 密钥来验证软件包的签名，以确保软件包来自于可信的源，且未被篡改。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;命名约定：&lt;/strong&gt; &lt;code&gt;RPM-GPG-KEY&lt;/code&gt; 是一个命名约定，通常与其所属的仓库或发行版相关。例如，如果你在使用某个特定发行版的官方仓库，它可能会提供一个 &lt;code&gt;RPM-GPG-KEY&lt;/code&gt; 文件来进行软件包签名验证。如openEuler官方仓库提供的 &lt;code&gt;RPM-GPG-KEY&lt;/code&gt; 文件名为 &lt;code&gt;RPM-GPG-KEY-openEuler&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;密钥生成：&lt;/strong&gt; 这个密钥是通过 GPG（GNU Privacy Guard）工具生成的。GPG 是一个用于进行加密和签名的开源工具。&lt;code&gt;RPM-GPG-KEY&lt;/code&gt; 文件包含了一个公钥，该公钥由仓库所有者使用私钥签署软件包，而用户使用公钥验证软件包。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;验证软件包：&lt;/strong&gt; 当用户使用 &lt;code&gt;yum&lt;/code&gt; 或 &lt;code&gt;dnf&lt;/code&gt; 安装软件包时，这些工具会检查软件包的签名，并使用相应的 &lt;code&gt;RPM-GPG-KEY&lt;/code&gt; 文件中的公钥来验证签名。如果验证通过，工具会认为软件包是可信的，否则将会发出警告或拒绝安装。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;导入密钥：&lt;/strong&gt; 为了使用 &lt;code&gt;RPM-GPG-KEY&lt;/code&gt; 文件，用户通常需要将密钥导入到本地系统中。这通常可以通过运行类似于以下命令的导入密钥的操作来完成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述命令中的 &lt;code&gt;/etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler&lt;/code&gt; 路径可能会因发行版和配置而有所不同。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;rpm包签名&#34;&gt;RPM包签名&lt;/h2&gt;
&lt;p&gt;对于发布的 RPM 包进行 GPG 签名是一种重要的安全措施，可以确保接收者能够验证软件包的真实性和完整性。以下是一般的步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;生成 GPG 密钥：&lt;/strong&gt; 如果你还没有 GPG 密钥对，你需要使用 GPG 工具生成一对密钥，包括私钥和公钥。你可以运行以下命令来生成密钥：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --gen-key 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当你运行 &lt;code&gt;gpg --gen-key&lt;/code&gt; 命令时，它会启动 GPG（GNU Privacy Guard）的密钥生成过程。这个过程将引导你提供一些必要的信息以生成密钥对，包括私钥和公钥。以下是这个命令的详细步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;选择密钥类型：&lt;/strong&gt; 你将被要求选择密钥的类型。通常，默认的 RSA 和 DSA 都是可接受的选择，你可以通过键入数字来选择。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;选择密钥大小：&lt;/strong&gt; 你将被要求选择密钥的大小。通常，默认值（通常是2048位）是足够的，但你也可以选择更大的值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;选择密钥的有效期：&lt;/strong&gt; 你将被要求选择密钥的有效期。你可以选择密钥永久有效，或者在一段特定的时间内有效。如果你选择了特定的时间，你需要输入一个表示有效期的值，例如1y表示一年，1m表示一个月。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;提供用户标识信息：&lt;/strong&gt; 你将被要求提供与密钥相关联的用户标识信息。这包括你的真实姓名、电子邮件地址和一个可选的注释。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;确认提供的信息：&lt;/strong&gt; GPG 将显示你提供的信息并询问你是否确认。如果确认无误，你可以输入 &lt;code&gt;O&lt;/code&gt; 或直接按回车键。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;输入保护密语（passphrase）：&lt;/strong&gt; 你将被要求输入保护密语，用于保护你的私钥。请确保选择一个强密码。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;等待密钥生成：&lt;/strong&gt; GPG 将使用提供的信息生成密钥对。这可能需要一些时间，具体取决于你选择的密钥大小。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;生成完成：&lt;/strong&gt; 一旦生成完成，你将看到一条消息表明密钥生成成功。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在整个过程中，你将看到类似以下的一些提示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg: key ABCDEFGH marked as ultimately trusted
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg: revocation certificate stored as &lt;span class=&#34;s1&#34;&gt;&amp;#39;/home/your_user/.gnupg/openpgp-revocs.d/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.rev&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;public and secret key created and signed.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg: checking the trustdb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg: &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; marginal&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; needed, &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; complete&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; needed, PGP trust model
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg: depth: &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  valid:   &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;  signed:   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  trust: 0-, 0q, 0n, 0m, 0f, 2u
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pub   2048R/XXXXXXXX 2024-01-01 &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;expires: 2024-01-01&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Key &lt;span class=&#34;nv&#34;&gt;fingerprint&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; XXXX YYYY ZZZZ AAAA BBBB  CCCC DDDD EEEE FFFF &lt;span class=&#34;m&#34;&gt;1111&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uid                  Your Name &amp;lt;your.email@example.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sub   2048R/YYYYYYYY 2024-01-01 &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;expires: 2024-01-01&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，&lt;code&gt;XXXXXXXX&lt;/code&gt; 是你的密钥 ID，&lt;code&gt;YYYYYYYY&lt;/code&gt; 是子密钥的 ID。你可以使用这些 ID 来引用你的密钥。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;导出公钥：&lt;/strong&gt; 生成密钥后，你需要将公钥导出。运行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --output RPM-GPG-KEY-your-repo --armor --export your@email.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将生成一个 ASCII 格式的公钥文件 &lt;code&gt;RPM-GPG-KEY-your-repo&lt;/code&gt;，你可以与软件包一起发布。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;为 RPM 包签名：&lt;/strong&gt; 在构建 RPM 包时，使用 &lt;code&gt;rpmbuild&lt;/code&gt; 命令时，可以通过添加 &lt;code&gt;--sign&lt;/code&gt; 选项来指示 &lt;code&gt;rpmbuild&lt;/code&gt; 对 RPM 包进行签名。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rpmbuild -ba your-package.spec --sign
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当你执行 &lt;code&gt;rpmbuild -ba your-package.spec --sign&lt;/code&gt; 命令时，&lt;code&gt;rpmbuild&lt;/code&gt; 会使用默认的 GPG 密钥进行签名。这通常是你在系统上配置为默认 GPG 密钥的密钥。&lt;/p&gt;
&lt;p&gt;你可以通过检查 &lt;code&gt;rpmbuild&lt;/code&gt; 使用的 GPG 密钥来确认它是哪个密钥：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rpm -q gpg-pubkey --qf &lt;span class=&#34;s1&#34;&gt;&amp;#39;%{name}-%{version}-%{release} --&amp;gt; %{summary}\n&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个命令将显示系统上安装的 GPG 公钥，其中默认的密钥可能是 &amp;ldquo;gpg-pubkey-xxxxxxxx-yyyyyyyy&amp;rdquo;。你可以根据密钥的 &amp;ldquo;xxxxxxxx-yyyyyyyy&amp;rdquo; 部分来确定默认使用的 GPG 密钥。&lt;/p&gt;
&lt;p&gt;如果你想使用不同的 GPG 密钥进行签名，可以在 &lt;code&gt;rpmbuild&lt;/code&gt; 命令中使用 &lt;code&gt;--signwith&lt;/code&gt; 选项，例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rpmbuild -ba your-package.spec --signwith &amp;lt;key-id&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;&amp;lt;key-id&amp;gt;&lt;/code&gt; 是你想要使用的 GPG 密钥的 ID。这会覆盖默认的密钥。确保你在构建和签名 RPM 包时使用的是正确的 GPG 密钥。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;导入密钥：&lt;/strong&gt; 为了验证你的软件包，用户需要导入你的公钥。他们可以运行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rpm --import RPM-GPG-KEY-your-repo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;发布：&lt;/strong&gt; 将签名的 RPM 包和公钥一起发布。确保用户知道他们可以使用导入的公钥来验证软件包的签名。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <content:encoded><![CDATA[<h1 id="repo-源配置解析">repo 源配置解析</h1>
<p>openEuler 的软件源配置文件位于/etc/yum.repos.d/目录下，以.repo 为后缀名，文件名可以任意取，但是必须以.repo 结尾。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">#generic-repos is licensed under the Mulan PSL v2.
</span></span><span class="line"><span class="cl">#You can use this software according to the terms and conditions of the Mulan PSL v2.
</span></span><span class="line"><span class="cl">#You may obtain a copy of Mulan PSL v2 at:
</span></span><span class="line"><span class="cl">#    http://license.coscl.org.cn/MulanPSL2
</span></span><span class="line"><span class="cl">#THIS SOFTWARE IS PROVIDED ON AN &#34;AS IS&#34; BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
</span></span><span class="line"><span class="cl">#IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
</span></span><span class="line"><span class="cl">#PURPOSE.
</span></span><span class="line"><span class="cl">#See the Mulan PSL v2 for more details.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[OS]
</span></span><span class="line"><span class="cl">name=OS
</span></span><span class="line"><span class="cl">baseurl=http://repo.openeuler.org/openEuler-23.09/OS/$basearch/
</span></span><span class="line"><span class="cl">metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/OS&amp;arch=$basearch
</span></span><span class="line"><span class="cl">metadata_expire=1h
</span></span><span class="line"><span class="cl">enabled=1
</span></span><span class="line"><span class="cl">gpgcheck=1
</span></span><span class="line"><span class="cl">gpgkey=http://repo.openeuler.org/openEuler-23.09/OS/$basearch/RPM-GPG-KEY-openEuler
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[source]
</span></span><span class="line"><span class="cl">name=source
</span></span><span class="line"><span class="cl">baseurl=http://repo.openeuler.org/openEuler-23.09/source/
</span></span><span class="line"><span class="cl">metalink=https://mirrors.openeuler.org/metalink?repo=$releasever&amp;arch=source
</span></span><span class="line"><span class="cl">metadata_expire=1h
</span></span><span class="line"><span class="cl">enabled=1
</span></span><span class="line"><span class="cl">gpgcheck=1
</span></span><span class="line"><span class="cl">gpgkey=http://repo.openeuler.org/openEuler-23.09/source/RPM-GPG-KEY-openEuler
</span></span></code></pre></div><p>其中各个配置项的含义如下：</p>
<ul>
<li>[repoid]中的 repoid 为软件仓库（repository）的 ID 号，所有.repo 配置文件中的各 repoid 不能重复，必须唯一。示例中 repoid 为 OS 和 source。</li>
<li>name 为软件仓库描述的字符串，可以任意取，但是建议取一个有意义的名称，方便用户理解。示例中 name 为 OS 和 source。</li>
<li>baseurl 为软件仓库的地址，可以是 http、https、ftp 等协议，也可以是本地目录。</li>
<li>enabled 为是否启用该软件源仓库，可选值为 1 和 0。默认值为 1，表示启用该软件源仓库。示例中 enabled 为 1。</li>
<li>metalink 为动态的镜像地址，用于镜像加速。</li>
<li>metadata_expire 为元数据过期时间，单位为秒。默认值为 90 分钟，即 5400 秒。示例中 metadata_expire 为 1h，即 1 小时。</li>
<li>gpgcheck 可设置为 1 或 0，1 表示进行 gpg（GNU Private Guard）校验，0 表示不进行 gpg 校验，gpgcheck 可以确定 rpm 包的来源是有效和安全的。</li>
<li>gpgkey 为验证签名用的公钥地址，如果 gpgcheck 为 1，则必须设置 gpgkey。</li>
</ul>
<h2 id="gpgcheck-详解">gpgcheck 详解</h2>
<p><code>RPM-GPG-KEY</code> 是一个公共密钥，用于验证由该密钥签名的RPM包的真实性和完整性。在使用 <code>yum</code> 或 <code>dnf</code> 这样的包管理工具时，这些工具会使用 GPG 密钥来验证软件包的签名，以确保软件包来自于可信的源，且未被篡改。</p>
<ol>
<li>
<p><strong>命名约定：</strong> <code>RPM-GPG-KEY</code> 是一个命名约定，通常与其所属的仓库或发行版相关。例如，如果你在使用某个特定发行版的官方仓库，它可能会提供一个 <code>RPM-GPG-KEY</code> 文件来进行软件包签名验证。如openEuler官方仓库提供的 <code>RPM-GPG-KEY</code> 文件名为 <code>RPM-GPG-KEY-openEuler</code>。</p>
</li>
<li>
<p><strong>密钥生成：</strong> 这个密钥是通过 GPG（GNU Privacy Guard）工具生成的。GPG 是一个用于进行加密和签名的开源工具。<code>RPM-GPG-KEY</code> 文件包含了一个公钥，该公钥由仓库所有者使用私钥签署软件包，而用户使用公钥验证软件包。</p>
</li>
<li>
<p><strong>验证软件包：</strong> 当用户使用 <code>yum</code> 或 <code>dnf</code> 安装软件包时，这些工具会检查软件包的签名，并使用相应的 <code>RPM-GPG-KEY</code> 文件中的公钥来验证签名。如果验证通过，工具会认为软件包是可信的，否则将会发出警告或拒绝安装。</p>
</li>
<li>
<p><strong>导入密钥：</strong> 为了使用 <code>RPM-GPG-KEY</code> 文件，用户通常需要将密钥导入到本地系统中。这通常可以通过运行类似于以下命令的导入密钥的操作来完成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler
</span></span></code></pre></div><p>上述命令中的 <code>/etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler</code> 路径可能会因发行版和配置而有所不同。</p>
</li>
</ol>
<h2 id="rpm包签名">RPM包签名</h2>
<p>对于发布的 RPM 包进行 GPG 签名是一种重要的安全措施，可以确保接收者能够验证软件包的真实性和完整性。以下是一般的步骤：</p>
<ol>
<li>
<p><strong>生成 GPG 密钥：</strong> 如果你还没有 GPG 密钥对，你需要使用 GPG 工具生成一对密钥，包括私钥和公钥。你可以运行以下命令来生成密钥：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gpg --gen-key 
</span></span></code></pre></div><p>当你运行 <code>gpg --gen-key</code> 命令时，它会启动 GPG（GNU Privacy Guard）的密钥生成过程。这个过程将引导你提供一些必要的信息以生成密钥对，包括私钥和公钥。以下是这个命令的详细步骤：</p>
<ul>
<li>
<p><strong>选择密钥类型：</strong> 你将被要求选择密钥的类型。通常，默认的 RSA 和 DSA 都是可接受的选择，你可以通过键入数字来选择。</p>
</li>
<li>
<p><strong>选择密钥大小：</strong> 你将被要求选择密钥的大小。通常，默认值（通常是2048位）是足够的，但你也可以选择更大的值。</p>
</li>
<li>
<p><strong>选择密钥的有效期：</strong> 你将被要求选择密钥的有效期。你可以选择密钥永久有效，或者在一段特定的时间内有效。如果你选择了特定的时间，你需要输入一个表示有效期的值，例如1y表示一年，1m表示一个月。</p>
</li>
<li>
<p><strong>提供用户标识信息：</strong> 你将被要求提供与密钥相关联的用户标识信息。这包括你的真实姓名、电子邮件地址和一个可选的注释。</p>
</li>
<li>
<p><strong>确认提供的信息：</strong> GPG 将显示你提供的信息并询问你是否确认。如果确认无误，你可以输入 <code>O</code> 或直接按回车键。</p>
</li>
<li>
<p><strong>输入保护密语（passphrase）：</strong> 你将被要求输入保护密语，用于保护你的私钥。请确保选择一个强密码。</p>
</li>
<li>
<p><strong>等待密钥生成：</strong> GPG 将使用提供的信息生成密钥对。这可能需要一些时间，具体取决于你选择的密钥大小。</p>
</li>
<li>
<p><strong>生成完成：</strong> 一旦生成完成，你将看到一条消息表明密钥生成成功。</p>
</li>
</ul>
<p>在整个过程中，你将看到类似以下的一些提示：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gpg: key ABCDEFGH marked as ultimately trusted
</span></span><span class="line"><span class="cl">gpg: revocation certificate stored as <span class="s1">&#39;/home/your_user/.gnupg/openpgp-revocs.d/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.rev&#39;</span>
</span></span><span class="line"><span class="cl">public and secret key created and signed.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">gpg: checking the trustdb
</span></span><span class="line"><span class="cl">gpg: <span class="m">3</span> marginal<span class="o">(</span>s<span class="o">)</span> needed, <span class="m">1</span> complete<span class="o">(</span>s<span class="o">)</span> needed, PGP trust model
</span></span><span class="line"><span class="cl">gpg: depth: <span class="m">0</span>  valid:   <span class="m">2</span>  signed:   <span class="m">0</span>  trust: 0-, 0q, 0n, 0m, 0f, 2u
</span></span><span class="line"><span class="cl">pub   2048R/XXXXXXXX 2024-01-01 <span class="o">[</span>expires: 2024-01-01<span class="o">]</span>
</span></span><span class="line"><span class="cl">    Key <span class="nv">fingerprint</span> <span class="o">=</span> XXXX YYYY ZZZZ AAAA BBBB  CCCC DDDD EEEE FFFF <span class="m">1111</span>
</span></span><span class="line"><span class="cl">uid                  Your Name &lt;your.email@example.com&gt;
</span></span><span class="line"><span class="cl">sub   2048R/YYYYYYYY 2024-01-01 <span class="o">[</span>expires: 2024-01-01<span class="o">]</span>
</span></span></code></pre></div><p>在这个例子中，<code>XXXXXXXX</code> 是你的密钥 ID，<code>YYYYYYYY</code> 是子密钥的 ID。你可以使用这些 ID 来引用你的密钥。</p>
</li>
<li>
<p><strong>导出公钥：</strong> 生成密钥后，你需要将公钥导出。运行以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gpg --output RPM-GPG-KEY-your-repo --armor --export your@email.com
</span></span></code></pre></div><p>这将生成一个 ASCII 格式的公钥文件 <code>RPM-GPG-KEY-your-repo</code>，你可以与软件包一起发布。</p>
</li>
<li>
<p><strong>为 RPM 包签名：</strong> 在构建 RPM 包时，使用 <code>rpmbuild</code> 命令时，可以通过添加 <code>--sign</code> 选项来指示 <code>rpmbuild</code> 对 RPM 包进行签名。例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rpmbuild -ba your-package.spec --sign
</span></span></code></pre></div><p>当你执行 <code>rpmbuild -ba your-package.spec --sign</code> 命令时，<code>rpmbuild</code> 会使用默认的 GPG 密钥进行签名。这通常是你在系统上配置为默认 GPG 密钥的密钥。</p>
<p>你可以通过检查 <code>rpmbuild</code> 使用的 GPG 密钥来确认它是哪个密钥：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rpm -q gpg-pubkey --qf <span class="s1">&#39;%{name}-%{version}-%{release} --&gt; %{summary}\n&#39;</span>
</span></span></code></pre></div><p>这个命令将显示系统上安装的 GPG 公钥，其中默认的密钥可能是 &ldquo;gpg-pubkey-xxxxxxxx-yyyyyyyy&rdquo;。你可以根据密钥的 &ldquo;xxxxxxxx-yyyyyyyy&rdquo; 部分来确定默认使用的 GPG 密钥。</p>
<p>如果你想使用不同的 GPG 密钥进行签名，可以在 <code>rpmbuild</code> 命令中使用 <code>--signwith</code> 选项，例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rpmbuild -ba your-package.spec --signwith &lt;key-id&gt;
</span></span></code></pre></div><p>其中 <code>&lt;key-id&gt;</code> 是你想要使用的 GPG 密钥的 ID。这会覆盖默认的密钥。确保你在构建和签名 RPM 包时使用的是正确的 GPG 密钥。</p>
</li>
<li>
<p><strong>导入密钥：</strong> 为了验证你的软件包，用户需要导入你的公钥。他们可以运行以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rpm --import RPM-GPG-KEY-your-repo
</span></span></code></pre></div></li>
<li>
<p><strong>发布：</strong> 将签名的 RPM 包和公钥一起发布。确保用户知道他们可以使用导入的公钥来验证软件包的签名。</p>
</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>Linux内核模块校验机制</title>
      <link>https://lifeislife.cn/posts/linux%E5%86%85%E6%A0%B8%E6%A8%A1%E5%9D%97%E6%A0%A1%E9%AA%8C%E6%9C%BA%E5%88%B6/</link>
      <pubDate>Sat, 13 Jan 2024 22:00:16 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/linux%E5%86%85%E6%A0%B8%E6%A8%A1%E5%9D%97%E6%A0%A1%E9%AA%8C%E6%9C%BA%E5%88%B6/</guid>
      <description>&lt;p&gt;初学 Linux 内核或者第一次编译使用内核模块时经常会遇到类似这样的错误：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;insmod: ERROR: could not insert module kvm.ko: Invalid module format 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个报错通常由于当前插入&lt;code&gt;kvm.ko&lt;/code&gt;的&lt;code&gt;version magic&lt;/code&gt;版本信息与正在运行的 kernel 的&lt;code&gt;version magic&lt;/code&gt;版本不一致导致的。&lt;/p&gt;
&lt;h1 id=&#34;内核校验模块的流程&#34;&gt;内核校验模块的流程&lt;/h1&gt;
&lt;p&gt;我们从问题出发，看看内核是如何校验模块的。搜索了内核源码，找到了在函数&lt;code&gt;check_version&lt;/code&gt;中抛出了&lt;code&gt;disagrees about version of symbol&lt;/code&gt;错误信息，我们根据源码来回溯一下整个过程。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// kernel/module.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;check_version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s32&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;Elf_Shdr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sechdrs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sechdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;versindex&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;num_versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modversion_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cm&#34;&gt;/* Exporting module didn&amp;#39;t supply crcs?  OK, we&amp;#39;re already tainted. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cm&#34;&gt;/* No versions at all?  modprobe --force does this. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;versindex&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;try_to_force_load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;versions&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sechdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;versindex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;num_versions&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sechdrs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;versindex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh_size&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modversion_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;num_versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;crcval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;strcmp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IS_ENABLED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CONFIG_MODULE_REL_CRCS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;n&#34;&gt;crcval&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;resolve_rel_crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;n&#34;&gt;crcval&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;crcval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;pr_debug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Found checksum %X vs module %lX&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;crcval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;goto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bad_version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cm&#34;&gt;/* Broken toolchain. Warn once, then let it go.. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nf&#34;&gt;pr_warn_once&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%s: no symbol version for %s&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nl&#34;&gt;bad_version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nf&#34;&gt;pr_warn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%s: disagrees about version of symbol %s&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;参数说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;info: 包含正在加载信息的结构体。&lt;/li&gt;
&lt;li&gt;symname: 需要查找对比的符号的名称。&lt;/li&gt;
&lt;li&gt;mod: 表示模块的结构体。&lt;/li&gt;
&lt;li&gt;crc: 当前正在运行的内核的模块符号的 CRC（Cyclic Redundancy Check）值&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;如果 CRC 为空，不检查直接 PASS&lt;/li&gt;
&lt;li&gt;如果模块中没有_versions 小节，表示模块没有开启 CRC
&lt;ol&gt;
&lt;li&gt;如果开启了 CONFIG_MODULE_FORCE_LOAD，则强制加载，内核标记为 tainted，直接 PASS&lt;/li&gt;
&lt;li&gt;如果没有开启 CONFIG_MODULE_FORCE_LOAD，则报错&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;如果模块中有_versions 小节，没有找到 module_layout 符号，报 warning，直接 PASS&lt;/li&gt;
&lt;li&gt;如果模块中有_versions 小节，找到 module_layout 符号
&lt;ol&gt;
&lt;li&gt;对比_versions 小节中的 CRC 和参数中的 CRC，如果一致，PASS&lt;/li&gt;
&lt;li&gt;如果不一致，报错&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;插播一句，CRC是什么？在 Linux 内核中，模块符号 CRC（Cyclic Redundancy Check）是一种校验值，用于确保模块中的符号（函数、变量等）在加载时与内核中的符号一致。当模块被构建时，针对每个符号都会计算一个 CRC 值，然后将这些 CRC 值保存在模块的符号版本表中。简单理解就是如果要保持CRC不变，需要满足两个条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;语法保持不变
遵守这个条件，说明如果模块在新内核下重新编译，那应该没有任何语法问题。 即导出符号的类型名没有变化，如果是函数，则要求参数和返回值类型没有任何变化；如果这些类型是结构体的话，结构体的成员名也没有有任何变化。&lt;/li&gt;
&lt;li&gt;语义保持不变
这要求符号的类型不能有变化，如果类型本身是结构体(struct)，则它成员的类型不能有变化，成员在结构体内的位置不能有变化，以及成员本身不能增删。
如果想要深入了解如何计算CRC可以参考这篇博客：&lt;a href=&#34;https://blog.csdn.net/linyt/article/details/42559639&#34;&gt;Linux内核模块符号CRC检查机制-CSDN博客&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;分析代码我们可以知道，内核会通过遍历正在加载的模块的版本信息的数组&lt;code&gt;versions&lt;/code&gt;，从中查找与给定符号名称匹配的版本信息。如果找到匹配的版本信息，则计算 CRC 值，与参数中的 CRC 值进行比较。（通过后续分析我们知道参数的 CRC 就是正在运行的内核的 module_layout 符号的 CRC）如果匹配，表示版本一致，返回 1。如果不匹配，打印调试信息，并跳转到 &lt;code&gt;bad_version&lt;/code&gt;，输出警告信息。&lt;/p&gt;
&lt;p&gt;这里有两个疑问，&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;versions&lt;/code&gt; 内容是怎么链接到模块的 elf 文件中的？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们找到模块的&lt;code&gt;mod.c&lt;/code&gt;文件，打开可以发现以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;MODULE_INFO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vermagic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;VERMAGIC_STRING&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;MODULE_INFO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KBUILD_MODNAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;__visible&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;__this_module&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;__section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gnu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linkonce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;this_module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KBUILD_MODNAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;init_module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifdef CONFIG_MODULE_UNLOAD
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cleanup_module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MODULE_ARCH_INIT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifdef CONFIG_RETPOLINE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;MODULE_INFO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;retpoline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Y&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modversion_info&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;____versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;__used&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;__versions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x3e549f1d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;module_layout&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x5138e6ba&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;param_ops_int&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x183b57f9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;phy_ethtool_nway_reset&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x6e5363eb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;eth_validate_addr&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x4df55e5b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;usb_deregister&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x8e48f69b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;usb_register_driver&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个文件是在编译过程中调用了&lt;code&gt;scripts/modpost&lt;/code&gt;脚本生成的，它的功能是在里面增加了 2 个&lt;code&gt;__section&lt;/code&gt;，&lt;code&gt;.gnu.linkonce.this_module&lt;/code&gt;和&lt;code&gt;__versions&lt;/code&gt;。__versions 小节的内容就是一些字符串和值组成的数组，&lt;code&gt;check_version&lt;/code&gt;就是解析这个小节去做验证。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;CRC 值哪来的？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们继续向上跟踪，找到函数&lt;code&gt;check_modstruct_version&lt;/code&gt;，其中&lt;code&gt;find_symbol&lt;/code&gt;会在内核符号表中查找给定符号名称的符号信息，接着调用&lt;code&gt;check_version&lt;/code&gt;函数，传入符号名称、模块结构体和 CRC 值，进行版本匹配。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// kernel/module.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;check_modstruct_version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s32&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;  * Since this should be found in kernel (which can&amp;#39;t be removed), no
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;  * locking is necessary -- use preempt_disable() to placate lockdep.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nf&#34;&gt;preempt_disable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;find_symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;module_layout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;preempt_enable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;BUG&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nf&#34;&gt;preempt_enable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;check_version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;module_layout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取当前运行内核 module_layout 函数的 crc 值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;find_symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;module_layout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;each_symbol_section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;find_exported_symbol_in_section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fsa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 遍历内核三个导出符号表段__start___ksymtab，__start___ksymtab_gpl 和__start___ksymtab_gpl_future，为每段调用 find_symbol_in_section
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;each_symbol_in_section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 遍历内核每个已加载模块的三个导出符号表段 mod-&amp;gt;syms,mod-&amp;gt;gpl_syms,mod-&amp;gt;gpl_future_syms，为每段调用 find_symbol_in_section
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;each_symbol_in_section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 对导出符号表进行二分查找，按照字符串排序，符号表的地址按照地址排序
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;find_exported_symbol_in_section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symsearch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syms&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Linux 对可装载模块采取了两层验证&lt;/strong&gt;：除了上述的模块 CRC 值校验外还有 &lt;code&gt;vermagic&lt;/code&gt; 的检查。模块 vermagic（即 Version Magic String）保存了模块编译时的内核版本以及 SMP 等配置信息，当模块 vermagic 与主机信息不相符时也无法加载模块。&lt;/p&gt;
&lt;p&gt;在内核中&lt;code&gt;load_module&lt;/code&gt;函数调用&lt;code&gt;check_modstruct_version&lt;/code&gt;函数完成 CRC 校验后，就会继续调用&lt;code&gt;layout_and_allocate --&amp;gt; check_modinfo&lt;/code&gt; 完成 vermagic 校验。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// kernel/module.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;check_modinfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;modmagic&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_modinfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;vermagic&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MODULE_INIT_IGNORE_VERMAGIC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;modmagic&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cm&#34;&gt;/* This is allowed: modprobe --force will invalidate it. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;modmagic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;try_to_force_load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;bad vermagic&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;same_magic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;modmagic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vermagic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;pr_err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%s: version magic &amp;#39;%s&amp;#39; should be &amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modmagic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vermagic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ENOEXEC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;get_modinfo&lt;/code&gt; 会获取内核中的 vermagic 信息，模块 vermagic 信息则被保存在了 ELF 的 &lt;code&gt;.modinfo&lt;/code&gt; 小节中。这里我们说的 vermagic 就是下文提到的拓展版本信息，它就是系统配置信息组成的一个字符串。&lt;/p&gt;
&lt;h1 id=&#34;如何解决模块校验错误&#34;&gt;如何解决模块校验错误&lt;/h1&gt;
&lt;p&gt;Linux 对可装载模块采取了两层验证，我们需要分别从 CRC 和 vermagic 两个方面来解决模块校验错误。首先从简单的 vermagic 校验开始。我们需要保证运行的内核版本与模块编译时的内核版本一致，这样才能保证 vermagic 校验通过。首先了解如何查看内核版本以及模块版本信息，然后修改内核模块版本信息。&lt;/p&gt;
&lt;h2 id=&#34;解决-vermagic-校验错误&#34;&gt;解决 vermagic 校验错误&lt;/h2&gt;
&lt;h3 id=&#34;如何查看内核版本以及模块版本信息&#34;&gt;如何查看内核版本以及模块版本信息&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;uname&lt;/code&gt;参数功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-s, 输出 kernel 名称；&lt;/li&gt;
&lt;li&gt;-n, 输出主机名；&lt;/li&gt;
&lt;li&gt;-r, 输出 kernel 发行版本号；&lt;/li&gt;
&lt;li&gt;-v, 输出操作系统版本；&lt;/li&gt;
&lt;li&gt;-m, 输出主机的硬件架构名称；&lt;/li&gt;
&lt;li&gt;-p, 输出处理器类型；&lt;/li&gt;
&lt;li&gt;-i, 输出硬件平台；&lt;/li&gt;
&lt;li&gt;-o, 输出操作系统名称&lt;/li&gt;
&lt;li&gt;-a, 输出所有信息&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出kernel发行版本号&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uname -r
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6.4.0-10.1.0.20.oe2309.riscv64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出所有信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uname -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Linux openeuler 6.4.0-10.1.0.20.oe2309.riscv64 &lt;span class=&#34;c1&#34;&gt;#1 SMP Sat Oct  7 06:19:28 UTC 2023 riscv64 riscv64 riscv64 GNU/Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;modinfo 可以查看模块信息，包括模块vermagic信息。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;modinfo kvm.ko
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;filename:       /root/build-kernel/kernel/./arch/riscv/kvm/kvm.ko
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;license:        GPL
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;author:         Qumranet
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;srcversion:     5DA13DC0E55100B5FE1D56A
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;depends:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;intree:         Y
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;name:           kvm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vermagic:       6.4.0 SMP mod_unload modversions riscv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;parm:           halt_poll_ns:uint
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;parm:           halt_poll_ns_grow:uint
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;parm:           halt_poll_ns_grow_start:uint
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;parm:           halt_poll_ns_shrink:uint
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;vermagic&lt;/code&gt;就是&lt;code&gt;version magic&lt;/code&gt;版本信息，可以看到当前&lt;code&gt;kvm.ko&lt;/code&gt;的&lt;code&gt;version magic&lt;/code&gt;版本信息为&lt;code&gt;6.4.0&lt;/code&gt;。与前文的&lt;code&gt;uname -r&lt;/code&gt;输出的 kernel 发行版本号&lt;code&gt;6.4.0-10.1.0.20.oe2309.riscv64&lt;/code&gt;不一致。所以会报错。&lt;/p&gt;
&lt;h3 id=&#34;修改内核模块版本信息&#34;&gt;修改内核模块版本信息&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;修改基础版本信息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;打开内核源代码根目录下的 Makefile 文件。你会找到一个包含内核版本信息的地方，类似于：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Makefile&#34; data-lang=&#34;Makefile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# SPDX-License-Identifier: GPL-2.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VERSION&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PATCHLEVEL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SUBLEVEL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;EXTRAVERSION&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;表示内核版基础本号为&lt;code&gt;6.4.0&lt;/code&gt;。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;修改拓展版本信息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;kernel 引入了一些配置来增强版本信息，在内核源码的&lt;code&gt;&amp;quot;include/linux/vermagic.h&amp;quot;&lt;/code&gt;下我们可以看到模块的健全版本信息，如下默认配置的有：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* Simply sanity version stamp for modules. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifdef CONFIG_SMP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_SMP &amp;#34;SMP &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_SMP &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifdef CONFIG_PREEMPT_BUILD
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_PREEMPT &amp;#34;preempt &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#elif defined(CONFIG_PREEMPT_RT)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_PREEMPT &amp;#34;preempt_rt &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_PREEMPT &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifdef CONFIG_MODULE_UNLOAD
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_MODULE_UNLOAD &amp;#34;mod_unload &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_MODULE_UNLOAD &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifdef CONFIG_MODVERSIONS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_MODVERSIONS &amp;#34;modversions &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_VERMAGIC_MODVERSIONS &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#ifdef RANDSTRUCT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;generated/randstruct_hash.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_RANDSTRUCT &amp;#34;RANDSTRUCT_&amp;#34; RANDSTRUCT_HASHED_SEED
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MODULE_RANDSTRUCT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define VERMAGIC_STRING                                                 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        UTS_RELEASE &amp;#34; &amp;#34;                                                 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT                     \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS       \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        MODULE_ARCH_VERMAGIC                                            \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        MODULE_RANDSTRUCT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中，&lt;code&gt;&amp;quot;UTS_RELEASE&amp;quot;&lt;/code&gt;的配置在内核源码的&lt;code&gt;&amp;quot;include/generated/utsrelease.h&amp;quot;&lt;/code&gt;下可以看到，&lt;code&gt;utsrelease.h&lt;/code&gt;的内容是由&lt;code&gt;Makefile&lt;/code&gt;和``.config&lt;code&gt;的内容来生成的，当成功编译kernel以后，&lt;/code&gt;utsrelease.h`得到更新，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#defineUTS_RELEASE &amp;#34;6.4.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那么前文&amp;quot;6.4.0-d46299ae&amp;quot;中的&amp;quot;-d46299ae&amp;quot;是如何得来的呢？其实这个是开发者使用了 Git 来管理代码。当你修改或者提交代码以后，每次编译内核后，在&amp;quot;UTS_RELEASE&amp;quot;后面就会看到一串哈希值，例如&amp;quot;6.4.0-d46299ae&amp;quot;，其中&amp;quot;-d46299ae&amp;quot;这个就是 Git 版本控制而产生的哈希值。发行版本号只是各个厂商用于区别自己发布的不同时期的 kernel 版本或者产品版本而产生的编号，完全由各厂商自己定义。&lt;/p&gt;
&lt;h2 id=&#34;解决-crc-校验错误&#34;&gt;解决 CRC 校验错误&lt;/h2&gt;
&lt;p&gt;我们可以通过一些手段修改新模块的 module_layout 的 CRC 与内核CRC相同，再插入。&lt;code&gt;/boot&lt;/code&gt;目录下通常有个在&lt;code&gt;symvers-&amp;lt;kernel_version&amp;gt;.gz&lt;/code&gt; 文件通常包含了内核模块的符号版本信息。这个文件是由 Linux 内核构建时生成的，用于记录在该内核版本下构建的模块的符号信息，包括函数和变量的名称、版本号等。里面就保存了内核的 module_layout 的 CRC 值。我们可以使用&lt;code&gt;gzip -d&lt;/code&gt;命令解压这个文件，找到 module_layout 的 CRC 值，记录下来。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;方法一：使用 16 进制编辑器修改模块文件，将 module_layout 的值修改为相同的值，再插入。
在 Linux 中，可以使用 &lt;code&gt;hexdump&lt;/code&gt; 和 &lt;code&gt;xxd&lt;/code&gt; 等工具查看二进制文件的内容，并尝试编辑。下面是一种使用 &lt;code&gt;xxd&lt;/code&gt; 查看和修改二进制文件的方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;code&gt;xxd&lt;/code&gt; 将二进制文件转换为十六进制文本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;xxd /usr/src/linux-6.4.0-10.1.0.20.oe2309.riscv64/arch/riscv/kvm/kvm.ko &amp;gt; kvm_hex.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将创建一个名为 &lt;code&gt;kvm_hex.txt&lt;/code&gt; 的文本文件，其中包含 &lt;code&gt;kvm.ko&lt;/code&gt; 的十六进制表示。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用文本编辑器（如 &lt;code&gt;nano&lt;/code&gt; 或 &lt;code&gt;vim&lt;/code&gt;）打开 &lt;code&gt;kvm_hex.txt&lt;/code&gt;，找到并编辑 &lt;code&gt;module_layout&lt;/code&gt; 的值。请确保你了解所做更改的含义，并且只修改你确信的内容。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保存并关闭文本编辑器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;code&gt;xxd&lt;/code&gt; 将修改后的十六进制文本转换回二进制文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;xxd -r kvm_hex.txt &amp;gt; /usr/src/linux-6.4.0-10.1.0.20.oe2309.riscv64/arch/riscv/kvm/kvm.ko
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将覆盖原始的 &lt;code&gt;kvm.ko&lt;/code&gt; 文件。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方法二：修改 Module.symvers&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清理编译结果。不要使用 make distclean，这会删除.config 文件以及 Module.symvers 文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改 Module.symvers 文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sed -i  &lt;span class=&#34;s1&#34;&gt;&amp;#39;/module_layout/ s/0x[0-9a-f][0-9a-f]*/0xdf88831e/&amp;#39;&lt;/span&gt; Module.symvers
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 重新编译模块&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;M&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;./arch/riscv/kvm/ -j33
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查模块的 module_layout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;modprobe --dump-modversions /usr/src/linux-6.4.0-10.1.0.20.oe2309.riscv64/arch/riscv/kvm/kvm.ko &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep module_layout
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0xdf88831e
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;如何生成-modc-文件&#34;&gt;如何生成 .mod.c 文件&lt;/h1&gt;
&lt;p&gt;如果是自己写的模块，可以根据下面的命令编译模块，就可以得到&lt;code&gt;.mod.c&lt;/code&gt;文件，它是源文件到&lt;code&gt;.mod.o&lt;/code&gt;文件的一个中间文件。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -C /root/build-kernel/kernel &lt;span class=&#34;nv&#34;&gt;M&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/driver_study/ modules -j22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果是用内核自己的模块，执行&lt;code&gt;make modules&lt;/code&gt;命令，也可以得到&lt;code&gt;.mod.c&lt;/code&gt;文件。但是一般执行&lt;code&gt;make -j22&lt;/code&gt;命令就不会生成这些文件，我们可以找一个启用的内核模块，例如&lt;code&gt;kvm.ko&lt;/code&gt;，执行下面的命令，就可以得到&lt;code&gt;.mod.c&lt;/code&gt;文件。不能随便找个模块，必须在 config 中开启的模块，否则会报错&lt;code&gt;undefined!&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make  &lt;span class=&#34;nv&#34;&gt;M&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;./arch/riscv/kvm modules -j22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>初学 Linux 内核或者第一次编译使用内核模块时经常会遇到类似这样的错误：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">insmod: ERROR: could not insert module kvm.ko: Invalid module format 
</span></span></code></pre></div><p>这个报错通常由于当前插入<code>kvm.ko</code>的<code>version magic</code>版本信息与正在运行的 kernel 的<code>version magic</code>版本不一致导致的。</p>
<h1 id="内核校验模块的流程">内核校验模块的流程</h1>
<p>我们从问题出发，看看内核是如何校验模块的。搜索了内核源码，找到了在函数<code>check_version</code>中抛出了<code>disagrees about version of symbol</code>错误信息，我们根据源码来回溯一下整个过程。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// kernel/module.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">check_version</span><span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">load_info</span> <span class="o">*</span><span class="n">info</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">symname</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">module</span> <span class="o">*</span><span class="n">mod</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">s32</span> <span class="o">*</span><span class="n">crc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">Elf_Shdr</span> <span class="o">*</span><span class="n">sechdrs</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">sechdrs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">versindex</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">index</span><span class="p">.</span><span class="n">vers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">num_versions</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">modversion_info</span> <span class="o">*</span><span class="n">versions</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="cm">/* Exporting module didn&#39;t supply crcs?  OK, we&#39;re already tainted. */</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">crc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="cm">/* No versions at all?  modprobe --force does this. */</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">versindex</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nf">try_to_force_load</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span> <span class="n">symname</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">versions</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="n">sechdrs</span><span class="p">[</span><span class="n">versindex</span><span class="p">].</span><span class="n">sh_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">num_versions</span> <span class="o">=</span> <span class="n">sechdrs</span><span class="p">[</span><span class="n">versindex</span><span class="p">].</span><span class="n">sh_size</span>
</span></span><span class="line"><span class="cl">  <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">modversion_info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">num_versions</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">u32</span> <span class="n">crcval</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nf">strcmp</span><span class="p">(</span><span class="n">versions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">name</span><span class="p">,</span> <span class="n">symname</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nf">IS_ENABLED</span><span class="p">(</span><span class="n">CONFIG_MODULE_REL_CRCS</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">   <span class="n">crcval</span> <span class="o">=</span> <span class="nf">resolve_rel_crc</span><span class="p">(</span><span class="n">crc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">   <span class="n">crcval</span> <span class="o">=</span> <span class="o">*</span><span class="n">crc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">versions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">crc</span> <span class="o">==</span> <span class="n">crcval</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">pr_debug</span><span class="p">(</span><span class="s">&#34;Found checksum %X vs module %lX</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">crcval</span><span class="p">,</span> <span class="n">versions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">crc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">goto</span> <span class="n">bad_version</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="cm">/* Broken toolchain. Warn once, then let it go.. */</span>
</span></span><span class="line"><span class="cl"> <span class="nf">pr_warn_once</span><span class="p">(</span><span class="s">&#34;%s: no symbol version for %s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">symname</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">bad_version</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nf">pr_warn</span><span class="p">(</span><span class="s">&#34;%s: disagrees about version of symbol %s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">info</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">symname</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>参数说明：</p>
<ul>
<li>info: 包含正在加载信息的结构体。</li>
<li>symname: 需要查找对比的符号的名称。</li>
<li>mod: 表示模块的结构体。</li>
<li>crc: 当前正在运行的内核的模块符号的 CRC（Cyclic Redundancy Check）值</li>
</ul>
<ol>
<li>如果 CRC 为空，不检查直接 PASS</li>
<li>如果模块中没有_versions 小节，表示模块没有开启 CRC
<ol>
<li>如果开启了 CONFIG_MODULE_FORCE_LOAD，则强制加载，内核标记为 tainted，直接 PASS</li>
<li>如果没有开启 CONFIG_MODULE_FORCE_LOAD，则报错</li>
</ol>
</li>
<li>如果模块中有_versions 小节，没有找到 module_layout 符号，报 warning，直接 PASS</li>
<li>如果模块中有_versions 小节，找到 module_layout 符号
<ol>
<li>对比_versions 小节中的 CRC 和参数中的 CRC，如果一致，PASS</li>
<li>如果不一致，报错</li>
</ol>
</li>
</ol>
<blockquote>
<p>插播一句，CRC是什么？在 Linux 内核中，模块符号 CRC（Cyclic Redundancy Check）是一种校验值，用于确保模块中的符号（函数、变量等）在加载时与内核中的符号一致。当模块被构建时，针对每个符号都会计算一个 CRC 值，然后将这些 CRC 值保存在模块的符号版本表中。简单理解就是如果要保持CRC不变，需要满足两个条件：</p>
<ol>
<li>语法保持不变
遵守这个条件，说明如果模块在新内核下重新编译，那应该没有任何语法问题。 即导出符号的类型名没有变化，如果是函数，则要求参数和返回值类型没有任何变化；如果这些类型是结构体的话，结构体的成员名也没有有任何变化。</li>
<li>语义保持不变
这要求符号的类型不能有变化，如果类型本身是结构体(struct)，则它成员的类型不能有变化，成员在结构体内的位置不能有变化，以及成员本身不能增删。
如果想要深入了解如何计算CRC可以参考这篇博客：<a href="https://blog.csdn.net/linyt/article/details/42559639">Linux内核模块符号CRC检查机制-CSDN博客</a></li>
</ol>
</blockquote>
<p>分析代码我们可以知道，内核会通过遍历正在加载的模块的版本信息的数组<code>versions</code>，从中查找与给定符号名称匹配的版本信息。如果找到匹配的版本信息，则计算 CRC 值，与参数中的 CRC 值进行比较。（通过后续分析我们知道参数的 CRC 就是正在运行的内核的 module_layout 符号的 CRC）如果匹配，表示版本一致，返回 1。如果不匹配，打印调试信息，并跳转到 <code>bad_version</code>，输出警告信息。</p>
<p>这里有两个疑问，</p>
<ol>
<li><code>versions</code> 内容是怎么链接到模块的 elf 文件中的？</li>
</ol>
<p>我们找到模块的<code>mod.c</code>文件，打开可以发现以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">MODULE_INFO</span><span class="p">(</span><span class="n">vermagic</span><span class="p">,</span> <span class="n">VERMAGIC_STRING</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">MODULE_INFO</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">KBUILD_MODNAME</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">__visible</span> <span class="k">struct</span> <span class="n">module</span> <span class="n">__this_module</span>
</span></span><span class="line"><span class="cl"><span class="nf">__section</span><span class="p">(.</span><span class="n">gnu</span><span class="p">.</span><span class="n">linkonce</span><span class="p">.</span><span class="n">this_module</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">KBUILD_MODNAME</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">init</span> <span class="o">=</span> <span class="n">init_module</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="cp">#ifdef CONFIG_MODULE_UNLOAD
</span></span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">exit</span> <span class="o">=</span> <span class="n">cleanup_module</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"> <span class="p">.</span><span class="n">arch</span> <span class="o">=</span> <span class="n">MODULE_ARCH_INIT</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#ifdef CONFIG_RETPOLINE
</span></span></span><span class="line"><span class="cl"><span class="nf">MODULE_INFO</span><span class="p">(</span><span class="n">retpoline</span><span class="p">,</span> <span class="s">&#34;Y&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">modversion_info</span> <span class="n">____versions</span><span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">__used</span> <span class="nf">__section</span><span class="p">(</span><span class="n">__versions</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="mh">0x3e549f1d</span><span class="p">,</span> <span class="s">&#34;module_layout&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="mh">0x5138e6ba</span><span class="p">,</span> <span class="s">&#34;param_ops_int&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="mh">0x183b57f9</span><span class="p">,</span> <span class="s">&#34;phy_ethtool_nway_reset&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="mh">0x6e5363eb</span><span class="p">,</span> <span class="s">&#34;eth_validate_addr&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="mh">0x4df55e5b</span><span class="p">,</span> <span class="s">&#34;usb_deregister&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="mh">0x8e48f69b</span><span class="p">,</span> <span class="s">&#34;usb_register_driver&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这个文件是在编译过程中调用了<code>scripts/modpost</code>脚本生成的，它的功能是在里面增加了 2 个<code>__section</code>，<code>.gnu.linkonce.this_module</code>和<code>__versions</code>。__versions 小节的内容就是一些字符串和值组成的数组，<code>check_version</code>就是解析这个小节去做验证。</p>
<ol start="2">
<li>CRC 值哪来的？</li>
</ol>
<p>我们继续向上跟踪，找到函数<code>check_modstruct_version</code>，其中<code>find_symbol</code>会在内核符号表中查找给定符号名称的符号信息，接着调用<code>check_version</code>函数，传入符号名称、模块结构体和 CRC 值，进行版本匹配。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// kernel/module.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="kt">int</span> <span class="nf">check_modstruct_version</span><span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">load_info</span> <span class="o">*</span><span class="n">info</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">       <span class="k">struct</span> <span class="n">module</span> <span class="o">*</span><span class="n">mod</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">s32</span> <span class="o">*</span><span class="n">crc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">  * Since this should be found in kernel (which can&#39;t be removed), no
</span></span></span><span class="line"><span class="cl"><span class="cm">  * locking is necessary -- use preempt_disable() to placate lockdep.
</span></span></span><span class="line"><span class="cl"><span class="cm">  */</span>
</span></span><span class="line"><span class="cl"> <span class="nf">preempt_disable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">find_symbol</span><span class="p">(</span><span class="s">&#34;module_layout&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">crc</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="nb">false</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nf">preempt_enable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nf">BUG</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nf">preempt_enable</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">check_version</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="s">&#34;module_layout&#34;</span><span class="p">,</span> <span class="n">mod</span><span class="p">,</span> <span class="n">crc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 获取当前运行内核 module_layout 函数的 crc 值
</span></span></span><span class="line"><span class="cl"><span class="nf">find_symbol</span><span class="p">(</span><span class="s">&#34;module_layout&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">crc</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="nb">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nf">each_symbol_section</span><span class="p">(</span><span class="n">find_exported_symbol_in_section</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">fsa</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 遍历内核三个导出符号表段__start___ksymtab，__start___ksymtab_gpl 和__start___ksymtab_gpl_future，为每段调用 find_symbol_in_section
</span></span></span><span class="line"><span class="cl">        <span class="nf">each_symbol_in_section</span><span class="p">(</span><span class="n">arr</span><span class="p">,</span> <span class="nf">ARRAY_SIZE</span><span class="p">(</span><span class="n">arr</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">fn</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 遍历内核每个已加载模块的三个导出符号表段 mod-&gt;syms,mod-&gt;gpl_syms,mod-&gt;gpl_future_syms，为每段调用 find_symbol_in_section
</span></span></span><span class="line"><span class="cl">        <span class="nf">each_symbol_in_section</span><span class="p">(</span><span class="n">arr</span><span class="p">,</span> <span class="nf">ARRAY_SIZE</span><span class="p">(</span><span class="n">arr</span><span class="p">),</span> <span class="n">mod</span><span class="p">,</span> <span class="n">fn</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 对导出符号表进行二分查找，按照字符串排序，符号表的地址按照地址排序
</span></span></span><span class="line"><span class="cl">            <span class="nf">find_exported_symbol_in_section</span><span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">symsearch</span> <span class="o">*</span><span class="n">syms</span><span class="p">,</span><span class="k">struct</span> <span class="n">module</span> <span class="o">*</span><span class="n">owner</span><span class="p">,</span><span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
</span></span></code></pre></div><p><strong>Linux 对可装载模块采取了两层验证</strong>：除了上述的模块 CRC 值校验外还有 <code>vermagic</code> 的检查。模块 vermagic（即 Version Magic String）保存了模块编译时的内核版本以及 SMP 等配置信息，当模块 vermagic 与主机信息不相符时也无法加载模块。</p>
<p>在内核中<code>load_module</code>函数调用<code>check_modstruct_version</code>函数完成 CRC 校验后，就会继续调用<code>layout_and_allocate --&gt; check_modinfo</code> 完成 vermagic 校验。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// kernel/module.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">check_modinfo</span><span class="p">(</span><span class="k">struct</span> <span class="n">module</span> <span class="o">*</span><span class="n">mod</span><span class="p">,</span> <span class="k">struct</span> <span class="n">load_info</span> <span class="o">*</span><span class="n">info</span><span class="p">,</span> <span class="kt">int</span> <span class="n">flags</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">modmagic</span> <span class="o">=</span> <span class="nf">get_modinfo</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="s">&#34;vermagic&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">MODULE_INIT_IGNORE_VERMAGIC</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">modmagic</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="cm">/* This is allowed: modprobe --force will invalidate it. */</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">modmagic</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">err</span> <span class="o">=</span> <span class="nf">try_to_force_load</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span> <span class="s">&#34;bad vermagic&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="k">return</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">same_magic</span><span class="p">(</span><span class="n">modmagic</span><span class="p">,</span> <span class="n">vermagic</span><span class="p">,</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">index</span><span class="p">.</span><span class="n">vers</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nf">pr_err</span><span class="p">(</span><span class="s">&#34;%s: version magic &#39;%s&#39; should be &#39;%s&#39;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="n">info</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">modmagic</span><span class="p">,</span> <span class="n">vermagic</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">-</span><span class="n">ENOEXEC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>get_modinfo</code> 会获取内核中的 vermagic 信息，模块 vermagic 信息则被保存在了 ELF 的 <code>.modinfo</code> 小节中。这里我们说的 vermagic 就是下文提到的拓展版本信息，它就是系统配置信息组成的一个字符串。</p>
<h1 id="如何解决模块校验错误">如何解决模块校验错误</h1>
<p>Linux 对可装载模块采取了两层验证，我们需要分别从 CRC 和 vermagic 两个方面来解决模块校验错误。首先从简单的 vermagic 校验开始。我们需要保证运行的内核版本与模块编译时的内核版本一致，这样才能保证 vermagic 校验通过。首先了解如何查看内核版本以及模块版本信息，然后修改内核模块版本信息。</p>
<h2 id="解决-vermagic-校验错误">解决 vermagic 校验错误</h2>
<h3 id="如何查看内核版本以及模块版本信息">如何查看内核版本以及模块版本信息</h3>
<p><code>uname</code>参数功能：</p>
<ul>
<li>-s, 输出 kernel 名称；</li>
<li>-n, 输出主机名；</li>
<li>-r, 输出 kernel 发行版本号；</li>
<li>-v, 输出操作系统版本；</li>
<li>-m, 输出主机的硬件架构名称；</li>
<li>-p, 输出处理器类型；</li>
<li>-i, 输出硬件平台；</li>
<li>-o, 输出操作系统名称</li>
<li>-a, 输出所有信息</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 输出kernel发行版本号</span>
</span></span><span class="line"><span class="cl">uname -r
</span></span><span class="line"><span class="cl">6.4.0-10.1.0.20.oe2309.riscv64
</span></span><span class="line"><span class="cl"><span class="c1"># 输出所有信息</span>
</span></span><span class="line"><span class="cl">uname -a
</span></span><span class="line"><span class="cl">Linux openeuler 6.4.0-10.1.0.20.oe2309.riscv64 <span class="c1">#1 SMP Sat Oct  7 06:19:28 UTC 2023 riscv64 riscv64 riscv64 GNU/Linux</span>
</span></span></code></pre></div><p>modinfo 可以查看模块信息，包括模块vermagic信息。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">modinfo kvm.ko
</span></span><span class="line"><span class="cl">filename:       /root/build-kernel/kernel/./arch/riscv/kvm/kvm.ko
</span></span><span class="line"><span class="cl">license:        GPL
</span></span><span class="line"><span class="cl">author:         Qumranet
</span></span><span class="line"><span class="cl">srcversion:     5DA13DC0E55100B5FE1D56A
</span></span><span class="line"><span class="cl">depends:
</span></span><span class="line"><span class="cl">intree:         Y
</span></span><span class="line"><span class="cl">name:           kvm
</span></span><span class="line"><span class="cl">vermagic:       6.4.0 SMP mod_unload modversions riscv
</span></span><span class="line"><span class="cl">parm:           halt_poll_ns:uint
</span></span><span class="line"><span class="cl">parm:           halt_poll_ns_grow:uint
</span></span><span class="line"><span class="cl">parm:           halt_poll_ns_grow_start:uint
</span></span><span class="line"><span class="cl">parm:           halt_poll_ns_shrink:uint
</span></span></code></pre></div><p>其中<code>vermagic</code>就是<code>version magic</code>版本信息，可以看到当前<code>kvm.ko</code>的<code>version magic</code>版本信息为<code>6.4.0</code>。与前文的<code>uname -r</code>输出的 kernel 发行版本号<code>6.4.0-10.1.0.20.oe2309.riscv64</code>不一致。所以会报错。</p>
<h3 id="修改内核模块版本信息">修改内核模块版本信息</h3>
<ol>
<li>修改基础版本信息</li>
</ol>
<p>打开内核源代码根目录下的 Makefile 文件。你会找到一个包含内核版本信息的地方，类似于：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Makefile" data-lang="Makefile"><span class="line"><span class="cl"><span class="c"># SPDX-License-Identifier: GPL-2.0
</span></span></span><span class="line"><span class="cl"><span class="nv">VERSION</span> <span class="o">=</span> <span class="m">6</span>
</span></span><span class="line"><span class="cl"><span class="nv">PATCHLEVEL</span> <span class="o">=</span> <span class="m">4</span>
</span></span><span class="line"><span class="cl"><span class="nv">SUBLEVEL</span> <span class="o">=</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="nv">EXTRAVERSION</span> <span class="o">=</span>
</span></span></code></pre></div><p>表示内核版基础本号为<code>6.4.0</code>。</p>
<ol start="2">
<li>修改拓展版本信息</li>
</ol>
<p>kernel 引入了一些配置来增强版本信息，在内核源码的<code>&quot;include/linux/vermagic.h&quot;</code>下我们可以看到模块的健全版本信息，如下默认配置的有：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* Simply sanity version stamp for modules. */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#ifdef CONFIG_SMP
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_SMP &#34;SMP &#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_SMP &#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp">#ifdef CONFIG_PREEMPT_BUILD
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_PREEMPT &#34;preempt &#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#elif defined(CONFIG_PREEMPT_RT)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_PREEMPT &#34;preempt_rt &#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_PREEMPT &#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp">#ifdef CONFIG_MODULE_UNLOAD
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_MODULE_UNLOAD &#34;mod_unload &#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_MODULE_UNLOAD &#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp">#ifdef CONFIG_MODVERSIONS
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_MODVERSIONS &#34;modversions &#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_VERMAGIC_MODVERSIONS &#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp">#ifdef RANDSTRUCT
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;generated/randstruct_hash.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_RANDSTRUCT &#34;RANDSTRUCT_&#34; RANDSTRUCT_HASHED_SEED
</span></span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MODULE_RANDSTRUCT
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define VERMAGIC_STRING                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        UTS_RELEASE &#34; &#34;                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS       \
</span></span></span><span class="line"><span class="cl"><span class="cp">        MODULE_ARCH_VERMAGIC                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">        MODULE_RANDSTRUCT
</span></span></span></code></pre></div><p>其中，<code>&quot;UTS_RELEASE&quot;</code>的配置在内核源码的<code>&quot;include/generated/utsrelease.h&quot;</code>下可以看到，<code>utsrelease.h</code>的内容是由<code>Makefile</code>和``.config<code>的内容来生成的，当成功编译kernel以后，</code>utsrelease.h`得到更新，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#defineUTS_RELEASE &#34;6.4.0&#34;</span>
</span></span></code></pre></div><p>那么前文&quot;6.4.0-d46299ae&quot;中的&quot;-d46299ae&quot;是如何得来的呢？其实这个是开发者使用了 Git 来管理代码。当你修改或者提交代码以后，每次编译内核后，在&quot;UTS_RELEASE&quot;后面就会看到一串哈希值，例如&quot;6.4.0-d46299ae&quot;，其中&quot;-d46299ae&quot;这个就是 Git 版本控制而产生的哈希值。发行版本号只是各个厂商用于区别自己发布的不同时期的 kernel 版本或者产品版本而产生的编号，完全由各厂商自己定义。</p>
<h2 id="解决-crc-校验错误">解决 CRC 校验错误</h2>
<p>我们可以通过一些手段修改新模块的 module_layout 的 CRC 与内核CRC相同，再插入。<code>/boot</code>目录下通常有个在<code>symvers-&lt;kernel_version&gt;.gz</code> 文件通常包含了内核模块的符号版本信息。这个文件是由 Linux 内核构建时生成的，用于记录在该内核版本下构建的模块的符号信息，包括函数和变量的名称、版本号等。里面就保存了内核的 module_layout 的 CRC 值。我们可以使用<code>gzip -d</code>命令解压这个文件，找到 module_layout 的 CRC 值，记录下来。</p>
<ul>
<li>
<p>方法一：使用 16 进制编辑器修改模块文件，将 module_layout 的值修改为相同的值，再插入。
在 Linux 中，可以使用 <code>hexdump</code> 和 <code>xxd</code> 等工具查看二进制文件的内容，并尝试编辑。下面是一种使用 <code>xxd</code> 查看和修改二进制文件的方法：</p>
<ol>
<li>
<p>使用 <code>xxd</code> 将二进制文件转换为十六进制文本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">xxd /usr/src/linux-6.4.0-10.1.0.20.oe2309.riscv64/arch/riscv/kvm/kvm.ko &gt; kvm_hex.txt
</span></span></code></pre></div><p>这将创建一个名为 <code>kvm_hex.txt</code> 的文本文件，其中包含 <code>kvm.ko</code> 的十六进制表示。</p>
</li>
<li>
<p>使用文本编辑器（如 <code>nano</code> 或 <code>vim</code>）打开 <code>kvm_hex.txt</code>，找到并编辑 <code>module_layout</code> 的值。请确保你了解所做更改的含义，并且只修改你确信的内容。</p>
</li>
<li>
<p>保存并关闭文本编辑器。</p>
</li>
<li>
<p>使用 <code>xxd</code> 将修改后的十六进制文本转换回二进制文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">xxd -r kvm_hex.txt &gt; /usr/src/linux-6.4.0-10.1.0.20.oe2309.riscv64/arch/riscv/kvm/kvm.ko
</span></span></code></pre></div><p>这将覆盖原始的 <code>kvm.ko</code> 文件。</p>
</li>
</ol>
</li>
<li>
<p>方法二：修改 Module.symvers</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 清理编译结果。不要使用 make distclean，这会删除.config 文件以及 Module.symvers 文件</span>
</span></span><span class="line"><span class="cl">make clean
</span></span><span class="line"><span class="cl"><span class="c1"># 修改 Module.symvers 文件</span>
</span></span><span class="line"><span class="cl">sed -i  <span class="s1">&#39;/module_layout/ s/0x[0-9a-f][0-9a-f]*/0xdf88831e/&#39;</span> Module.symvers
</span></span><span class="line"><span class="cl"><span class="c1"># 重新编译模块</span>
</span></span><span class="line"><span class="cl">make <span class="nv">M</span><span class="o">=</span>./arch/riscv/kvm/ -j33
</span></span><span class="line"><span class="cl"><span class="c1"># 检查模块的 module_layout</span>
</span></span><span class="line"><span class="cl">modprobe --dump-modversions /usr/src/linux-6.4.0-10.1.0.20.oe2309.riscv64/arch/riscv/kvm/kvm.ko <span class="p">|</span> grep module_layout
</span></span><span class="line"><span class="cl">0xdf88831e
</span></span></code></pre></div></li>
</ul>
<h1 id="如何生成-modc-文件">如何生成 .mod.c 文件</h1>
<p>如果是自己写的模块，可以根据下面的命令编译模块，就可以得到<code>.mod.c</code>文件，它是源文件到<code>.mod.o</code>文件的一个中间文件。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make -C /root/build-kernel/kernel <span class="nv">M</span><span class="o">=</span>/driver_study/ modules -j22
</span></span></code></pre></div><p>如果是用内核自己的模块，执行<code>make modules</code>命令，也可以得到<code>.mod.c</code>文件。但是一般执行<code>make -j22</code>命令就不会生成这些文件，我们可以找一个启用的内核模块，例如<code>kvm.ko</code>，执行下面的命令，就可以得到<code>.mod.c</code>文件。不能随便找个模块，必须在 config 中开启的模块，否则会报错<code>undefined!</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make  <span class="nv">M</span><span class="o">=</span>./arch/riscv/kvm modules -j22
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>x86 平台使用 Gitea Actions 构建多架构应用 (binfmt_misc)</title>
      <link>https://lifeislife.cn/posts/x86%E5%B9%B3%E5%8F%B0%E4%BD%BF%E7%94%A8gitea-actions%E6%9E%84%E5%BB%BA%E5%A4%9A%E6%9E%B6%E6%9E%84%E5%BA%94%E7%94%A8/</link>
      <pubDate>Wed, 10 Jan 2024 11:10:04 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/x86%E5%B9%B3%E5%8F%B0%E4%BD%BF%E7%94%A8gitea-actions%E6%9E%84%E5%BB%BA%E5%A4%9A%E6%9E%B6%E6%9E%84%E5%BA%94%E7%94%A8/</guid>
      <description>&lt;h1 id=&#34;binfmt_misc-简介&#34;&gt;binfmt_misc 简介&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;binfmt_misc&lt;/code&gt; 是 Linux 内核提供的一个机制，它允许用户空间定义新的二进制格式，并将它们与相应的解释器关联起来。这个机制使得在 Linux 上能够动态地注册并运行不同架构的二进制可执行文件，从而支持交叉编译和多架构环境。&lt;/p&gt;
&lt;p&gt;具体来说，&lt;code&gt;binfmt_misc&lt;/code&gt; 的功能可以通过 &lt;code&gt;/proc/sys/fs/binfmt_misc/&lt;/code&gt; 目录下的文件系统接口实现。这个目录下的文件用于注册和管理二进制格式和相应解释器之间的关联关系。&lt;/p&gt;
&lt;p&gt;下面是一些与 &lt;code&gt;binfmt_misc&lt;/code&gt; 相关的重要概念和文件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;注册表文件：&lt;/strong&gt;   在 &lt;code&gt;/proc/sys/fs/binfmt_misc/&lt;/code&gt; 目录下，每个注册的二进制格式都有一个对应的注册表文件。这些文件的命名通常遵循格式 &lt;code&gt;&amp;lt;格式名称&amp;gt;&lt;/code&gt;，例如 &lt;code&gt;qemu-riscv64&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注册和注销：&lt;/strong&gt;   用户空间可以通过在注册表目录下创建文件来注册新的二进制格式。这可以通过写入注册表文件的方式完成。相反，通过删除这些文件，可以注销二进制格式的支持。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解释器：&lt;/strong&gt;   对于每种注册的二进制格式，需要指定相应的解释器，即用于执行这种格式的程序。在注册表文件中，通过 &lt;code&gt;interpreter&lt;/code&gt; 字段指定解释器的路径。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数：&lt;/strong&gt;   除了解释器，还可以为每个注册的格式指定一些参数。这些参数可以影响如何运行二进制文件。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;内核如何通过-binfmt_misc-机制添加新架构的支持&#34;&gt;内核如何通过 binfmt_misc 机制添加新架构的支持？&lt;/h1&gt;
&lt;p&gt;可以通过向 &lt;code&gt;/proc/sys/fs/binfmt_misc/register&lt;/code&gt; 文件写入注册信息来注册新的二进制格式。告诉内核某一格式的文件用什么解释器来执行。写入的格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;:name:type:offset:magic:mask:interpreter:flags
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;各个字段以冒号分隔，部分字段可以缺省，但是冒号需要保留。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;字段含义如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt;：二进制格式的名称，比如&lt;code&gt;qemu-riscv64&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt;：类型为 E 或 M。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果是 E，可执行文件格式由其文件扩展名标识：magic 是要与二进制格式相关联的文件扩展名；offset 和 mask 将被忽略。&lt;/li&gt;
&lt;li&gt;如果是 M，格式由文件中绝对偏移（默认为 0）处的魔数标识，并且 mask 是一个位掩码（默认为全 0xFF），表示数字中哪些位是有效的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;interpreter&lt;/code&gt;：是要作为匹配文件的参数运行的解释器，使用解释器的绝对路径，比如&lt;code&gt;/usr/bin/qemu-riscv64-static&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;flags&lt;/code&gt;：可选字段，控制 &lt;code&gt;interpreter&lt;/code&gt; 打开文件的行为。共支持 &lt;code&gt;POCF&lt;/code&gt; 四种 flag。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;P&lt;/code&gt; 表示 preserve-argv[0]，保留原始的 &lt;code&gt;argv[0]&lt;/code&gt; 参数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O&lt;/code&gt; 表示 open-binary，&lt;code&gt;binfmt-misc&lt;/code&gt; 默认会传递文件的路径，而启用这个参数时，&lt;code&gt;binfmt-misc&lt;/code&gt; 会打开文件，传递文件描述符。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C&lt;/code&gt; 表示 credentials，即会传递文件的 &lt;code&gt;setuid&lt;/code&gt; 等权限，这个选项也隐含了 &lt;code&gt;O&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F&lt;/code&gt; 表示 fix binary，&lt;code&gt;binfmt-misc&lt;/code&gt; 默认的行为在 spwan 进程时会延迟，这种方式可能会受到 &lt;code&gt;mount&lt;/code&gt; 命名空间和 &lt;code&gt;chroot&lt;/code&gt; 的影响，设置 &lt;code&gt;F&lt;/code&gt; 时会立刻打开二进制文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;举个例子，如果要在 x86_64 架构的系统上运行 RISC-V 架构的二进制文件，可以通过以下方式注册 RISC-V 二进制格式：&lt;/p&gt;
&lt;p&gt;首先需要&lt;strong&gt;添加解释器&lt;/strong&gt;，通常使用 QEMU 的静态二进制文件作为解释器，在 Ubuntu 系统中我们可以使用以下命令安装：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install qemu-user-static
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;注册二进制格式&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;:qemu-riscv64:M:0:7f454c460201010000000000000000000200f300::/usr/libexec/qemu-binfmt/riscv64-binfmt-P:POCF&amp;#39;&lt;/span&gt; &amp;gt; /proc/sys/fs/binfmt_misc/register
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;表示将 RISC-V 二进制格式注册到 &lt;code&gt;/proc/sys/fs/binfmt_misc/qemu-riscv64&lt;/code&gt; 文件中，使用 &lt;code&gt;/usr/libexec/qemu-binfmt/riscv64-binfmt-P&lt;/code&gt; 作为解释器，同时传递 &lt;code&gt;POCF&lt;/code&gt; 参数。执行了以上命令，内核会自动创建一个 &lt;code&gt;/proc/sys/fs/binfmt_misc/qemu-riscv64&lt;/code&gt; 文件，内容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo cat  /proc/sys/fs/binfmt_misc/qemu-riscv64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enabled
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;interpreter /usr/libexec/qemu-binfmt/riscv64-binfmt-P
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;flags: POCF
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;offset &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;magic 7f454c460201010000000000000000000200f300
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mask ffffffffffffff00fffffffffffffffffeffffff
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这就完成了 RISC-V 二进制格式的注册。此时，你就可以在 x86_64 架构的系统上运行 RISC-V 架构的二进制文件了。&lt;/p&gt;
&lt;p&gt;使用 Docker 运行 RISC-V 的容器：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ docker run --rm -it devops/openeuler-builder:23.09-riscv64  uname -m
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;riscv64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;使用封装好的程序简化注册过程&#34;&gt;使用封装好的程序简化注册过程&lt;/h2&gt;
&lt;p&gt;以上的写入 register 文件的方式比较繁琐，可以使用封装好的程序来简化注册过程。&lt;/p&gt;
&lt;p&gt;方式一：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install qemu-user-binfmt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以安装所有 QEMU 支持的架构。&lt;/p&gt;
&lt;p&gt;方式二：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装解释器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install qemu-user-static
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装binfmt操作支持&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install binfmt-support
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 开启异构支持&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo update-binfmts --package&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;qemu-user-static --enable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同上。&lt;/p&gt;
&lt;h2 id=&#34;注销&#34;&gt;注销&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; -1 &amp;gt;/proc/sys/fs/binfmt_misc/status &lt;span class=&#34;c1&#34;&gt;# 注销所有注册的条目&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; -1 &amp;gt;/proc/sys/fs/binfmt_misc/qemu-riscv64 &lt;span class=&#34;c1&#34;&gt;# 注销单个条目&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者通过命令行工具完成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装binfmt操作支持&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install binfmt-support
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 禁用qemu-riscv64，再次查看/proc/sys/fs/binfmt_misc/发现qemu-riscv64已被删除&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo update-binfmts --disable qemu-riscv64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;gitea-如何实现多架构应用构建&#34;&gt;Gitea 如何实现多架构应用构建？&lt;/h1&gt;
&lt;p&gt;Gitea 不会自己运行 Job，而是将 Job 委托给 Runner。Gitea Actions 的 Runner 被称为 act runner，它是一个独立的程序。在接收到 Job 后，act runner 会根据 Job 的内容，启用不同的 Container 来运行 Job。&lt;/p&gt;
&lt;p&gt;为了避免消耗过多资源并影响 Gitea 实例，Gitea 和 Runner 一般运行在不同的机器上。但是同一个 Runner 启动的容器一定在同一台机器上。我这里演示的统一都在同一台 x86 架构的机器上。&lt;/p&gt;
&lt;p&gt;因为都运行在 x86 架构的机器上，所有执行任务的 Container 也一定是 x86 架构的。但是我们了解了上面的 binfmt_misc 机制后，就可以在容器内部通过注册不同架构的二进制格式，从而在 x86 架构的机器上运行不同架构的二进制文件。就可以实现多架构应用构建。&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/17-49-15-4cd035ef3794eae7de157dd25af6148e-giteaactionsrunner-5cf8e3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-49-15-4cd035ef3794eae7de157dd25af6148e-giteaactionsrunner-5cf8e3.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;打开 Gitea 的 tea 的项目源码找到它的 release &lt;a href=&#34;https://gitea.com/gitea/tea/src/commit/c74177556b8e63252491003f1cbcd3882bfff15d/.gitea/workflows/release-nightly.yml#L53&#34;&gt;workflow 文件&lt;/a&gt;，可以看到它使用了&lt;code&gt;docker/setup-qemu-action@v3&lt;/code&gt;这个 action 来实现多架构构建。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Set up QEMU&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;docker/setup-qemu-action@v3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查阅&lt;a href=&#34;https://github.com/tonistiigi/binfmt/blob/a92dbabe8fddb096b8cb307aa1c6bbe65dc0884f/cmd/binfmt/main.go#L63C1-L103C2&#34;&gt;action 的源码&lt;/a&gt;，可以发现底层实现是通过&lt;code&gt;binfmt_misc&lt;/code&gt;来实现的。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义了一些全局变量&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StringVar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;mount&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/proc/sys/fs/binfmt_misc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;binfmt_misc mount point&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StringVar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toInstall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;install&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;architectures to install&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StringVar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toUninstall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;uninstall&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;architectures to uninstall&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;BoolVar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;display version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;install&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;configs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 拼接路径为/proc/sys/fs/binfmt_misc/register，打开这个文件检查是否能够打开成功&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;filepath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;register&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;OpenFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;O_WRONLY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;binaryBasename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;binaryFullpath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getBinaryNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;		&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 向/proc/sys/fs/binfmt_misc/register 写入 line，注册二进制格式&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;:%s:M:0:%s:%s:%s:%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;binaryBasename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;magic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;binaryFullpath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;		&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;PathError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;		&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EEXIST&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;			&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%s already registered&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;binaryBasename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;		&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;		&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cannot register %q to %s: %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;binaryFullpath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;q--a&#34;&gt;Q &amp;amp; A&lt;/h1&gt;
&lt;h2 id=&#34;为何procsysfsbinfmt_miscregister-文件是只读的&#34;&gt;为何/proc/sys/fs/binfmt_misc/register 文件是只读的？&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/proc/sys/fs/binfmt_misc/register&lt;/code&gt; 文件是只写的，这是因为在 Linux 中，&lt;code&gt;/proc&lt;/code&gt; 文件系统下的很多文件都是通过对文件进行写入来进行配置和控制的。这些文件通常&lt;strong&gt;代表内核参数或控制接口&lt;/strong&gt;，提供了一种方便的方式来与内核进行交互。&lt;/p&gt;
&lt;p&gt;对于 &lt;code&gt;/proc/sys/fs/binfmt_misc/register&lt;/code&gt; 文件来说，通过写入注册信息，用户可以向内核注册新的二进制格式，告知内核如何执行特定的二进制文件。这种只写的设计是为了保持简单性和安全性。允许用户在运行时动态地注册新的格式，而不是从文件中读取注册信息，可以提供更大的灵活性。&lt;/p&gt;
&lt;p&gt;虽然 &lt;code&gt;/proc/sys/fs/binfmt_misc/register&lt;/code&gt; 文件是只写的，但是通过向文件中写入正确格式的注册信息，用户仍然能够有效地配置新的二进制格式。这种设计符合 Linux 中的文件系统和权限模型。&lt;/p&gt;
&lt;h1 id=&#34;参考资料&#34;&gt;参考资料&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/torvalds/linux/pulse&#34;&gt;Kernel Support for miscellaneous Binary Formats (binfmt_misc)&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="binfmt_misc-简介">binfmt_misc 简介</h1>
<p><code>binfmt_misc</code> 是 Linux 内核提供的一个机制，它允许用户空间定义新的二进制格式，并将它们与相应的解释器关联起来。这个机制使得在 Linux 上能够动态地注册并运行不同架构的二进制可执行文件，从而支持交叉编译和多架构环境。</p>
<p>具体来说，<code>binfmt_misc</code> 的功能可以通过 <code>/proc/sys/fs/binfmt_misc/</code> 目录下的文件系统接口实现。这个目录下的文件用于注册和管理二进制格式和相应解释器之间的关联关系。</p>
<p>下面是一些与 <code>binfmt_misc</code> 相关的重要概念和文件：</p>
<ol>
<li><strong>注册表文件：</strong>   在 <code>/proc/sys/fs/binfmt_misc/</code> 目录下，每个注册的二进制格式都有一个对应的注册表文件。这些文件的命名通常遵循格式 <code>&lt;格式名称&gt;</code>，例如 <code>qemu-riscv64</code>。</li>
<li><strong>注册和注销：</strong>   用户空间可以通过在注册表目录下创建文件来注册新的二进制格式。这可以通过写入注册表文件的方式完成。相反，通过删除这些文件，可以注销二进制格式的支持。</li>
<li><strong>解释器：</strong>   对于每种注册的二进制格式，需要指定相应的解释器，即用于执行这种格式的程序。在注册表文件中，通过 <code>interpreter</code> 字段指定解释器的路径。</li>
<li><strong>参数：</strong>   除了解释器，还可以为每个注册的格式指定一些参数。这些参数可以影响如何运行二进制文件。</li>
</ol>
<h1 id="内核如何通过-binfmt_misc-机制添加新架构的支持">内核如何通过 binfmt_misc 机制添加新架构的支持？</h1>
<p>可以通过向 <code>/proc/sys/fs/binfmt_misc/register</code> 文件写入注册信息来注册新的二进制格式。告诉内核某一格式的文件用什么解释器来执行。写入的格式如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">:name:type:offset:magic:mask:interpreter:flags
</span></span></code></pre></div><blockquote>
<p>各个字段以冒号分隔，部分字段可以缺省，但是冒号需要保留。</p>
</blockquote>
<p>字段含义如下：</p>
<ul>
<li>
<p><code>name</code>：二进制格式的名称，比如<code>qemu-riscv64</code>。</p>
</li>
<li>
<p><code>type</code>：类型为 E 或 M。</p>
<ul>
<li>如果是 E，可执行文件格式由其文件扩展名标识：magic 是要与二进制格式相关联的文件扩展名；offset 和 mask 将被忽略。</li>
<li>如果是 M，格式由文件中绝对偏移（默认为 0）处的魔数标识，并且 mask 是一个位掩码（默认为全 0xFF），表示数字中哪些位是有效的。</li>
</ul>
</li>
<li>
<p><code>interpreter</code>：是要作为匹配文件的参数运行的解释器，使用解释器的绝对路径，比如<code>/usr/bin/qemu-riscv64-static</code>。</p>
</li>
<li>
<p><code>flags</code>：可选字段，控制 <code>interpreter</code> 打开文件的行为。共支持 <code>POCF</code> 四种 flag。</p>
<ul>
<li><code>P</code> 表示 preserve-argv[0]，保留原始的 <code>argv[0]</code> 参数。</li>
<li><code>O</code> 表示 open-binary，<code>binfmt-misc</code> 默认会传递文件的路径，而启用这个参数时，<code>binfmt-misc</code> 会打开文件，传递文件描述符。</li>
<li><code>C</code> 表示 credentials，即会传递文件的 <code>setuid</code> 等权限，这个选项也隐含了 <code>O</code>。</li>
<li><code>F</code> 表示 fix binary，<code>binfmt-misc</code> 默认的行为在 spwan 进程时会延迟，这种方式可能会受到 <code>mount</code> 命名空间和 <code>chroot</code> 的影响，设置 <code>F</code> 时会立刻打开二进制文件。</li>
</ul>
</li>
</ul>
<p>举个例子，如果要在 x86_64 架构的系统上运行 RISC-V 架构的二进制文件，可以通过以下方式注册 RISC-V 二进制格式：</p>
<p>首先需要<strong>添加解释器</strong>，通常使用 QEMU 的静态二进制文件作为解释器，在 Ubuntu 系统中我们可以使用以下命令安装：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install qemu-user-static
</span></span></code></pre></div><p><strong>注册二进制格式</strong>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;:qemu-riscv64:M:0:7f454c460201010000000000000000000200f300::/usr/libexec/qemu-binfmt/riscv64-binfmt-P:POCF&#39;</span> &gt; /proc/sys/fs/binfmt_misc/register
</span></span></code></pre></div><p>表示将 RISC-V 二进制格式注册到 <code>/proc/sys/fs/binfmt_misc/qemu-riscv64</code> 文件中，使用 <code>/usr/libexec/qemu-binfmt/riscv64-binfmt-P</code> 作为解释器，同时传递 <code>POCF</code> 参数。执行了以上命令，内核会自动创建一个 <code>/proc/sys/fs/binfmt_misc/qemu-riscv64</code> 文件，内容如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo cat  /proc/sys/fs/binfmt_misc/qemu-riscv64
</span></span><span class="line"><span class="cl">enabled
</span></span><span class="line"><span class="cl">interpreter /usr/libexec/qemu-binfmt/riscv64-binfmt-P
</span></span><span class="line"><span class="cl">flags: POCF
</span></span><span class="line"><span class="cl">offset <span class="m">0</span>
</span></span><span class="line"><span class="cl">magic 7f454c460201010000000000000000000200f300
</span></span><span class="line"><span class="cl">mask ffffffffffffff00fffffffffffffffffeffffff
</span></span></code></pre></div><p>这就完成了 RISC-V 二进制格式的注册。此时，你就可以在 x86_64 架构的系统上运行 RISC-V 架构的二进制文件了。</p>
<p>使用 Docker 运行 RISC-V 的容器：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ docker run --rm -it devops/openeuler-builder:23.09-riscv64  uname -m
</span></span><span class="line"><span class="cl">riscv64
</span></span></code></pre></div><h2 id="使用封装好的程序简化注册过程">使用封装好的程序简化注册过程</h2>
<p>以上的写入 register 文件的方式比较繁琐，可以使用封装好的程序来简化注册过程。</p>
<p>方式一：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install qemu-user-binfmt
</span></span></code></pre></div><p>可以安装所有 QEMU 支持的架构。</p>
<p>方式二：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装解释器</span>
</span></span><span class="line"><span class="cl">sudo apt install qemu-user-static
</span></span><span class="line"><span class="cl"><span class="c1"># 安装binfmt操作支持</span>
</span></span><span class="line"><span class="cl">sudo apt install binfmt-support
</span></span><span class="line"><span class="cl"><span class="c1"># 开启异构支持</span>
</span></span><span class="line"><span class="cl">sudo update-binfmts --package<span class="o">=</span>qemu-user-static --enable
</span></span></code></pre></div><p>同上。</p>
<h2 id="注销">注销</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> -1 &gt;/proc/sys/fs/binfmt_misc/status <span class="c1"># 注销所有注册的条目</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -1 &gt;/proc/sys/fs/binfmt_misc/qemu-riscv64 <span class="c1"># 注销单个条目</span>
</span></span></code></pre></div><p>或者通过命令行工具完成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装binfmt操作支持</span>
</span></span><span class="line"><span class="cl">sudo apt install binfmt-support
</span></span><span class="line"><span class="cl"><span class="c1"># 禁用qemu-riscv64，再次查看/proc/sys/fs/binfmt_misc/发现qemu-riscv64已被删除</span>
</span></span><span class="line"><span class="cl">sudo update-binfmts --disable qemu-riscv64
</span></span></code></pre></div><h1 id="gitea-如何实现多架构应用构建">Gitea 如何实现多架构应用构建？</h1>
<p>Gitea 不会自己运行 Job，而是将 Job 委托给 Runner。Gitea Actions 的 Runner 被称为 act runner，它是一个独立的程序。在接收到 Job 后，act runner 会根据 Job 的内容，启用不同的 Container 来运行 Job。</p>
<p>为了避免消耗过多资源并影响 Gitea 实例，Gitea 和 Runner 一般运行在不同的机器上。但是同一个 Runner 启动的容器一定在同一台机器上。我这里演示的统一都在同一台 x86 架构的机器上。</p>
<p>因为都运行在 x86 架构的机器上，所有执行任务的 Container 也一定是 x86 架构的。但是我们了解了上面的 binfmt_misc 机制后，就可以在容器内部通过注册不同架构的二进制格式，从而在 x86 架构的机器上运行不同架构的二进制文件。就可以实现多架构应用构建。</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/17-49-15-4cd035ef3794eae7de157dd25af6148e-giteaactionsrunner-5cf8e3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-49-15-4cd035ef3794eae7de157dd25af6148e-giteaactionsrunner-5cf8e3.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>打开 Gitea 的 tea 的项目源码找到它的 release <a href="https://gitea.com/gitea/tea/src/commit/c74177556b8e63252491003f1cbcd3882bfff15d/.gitea/workflows/release-nightly.yml#L53">workflow 文件</a>，可以看到它使用了<code>docker/setup-qemu-action@v3</code>这个 action 来实现多架构构建。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up QEMU</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">docker/setup-qemu-action@v3</span><span class="w">
</span></span></span></code></pre></div><p>查阅<a href="https://github.com/tonistiigi/binfmt/blob/a92dbabe8fddb096b8cb307aa1c6bbe65dc0884f/cmd/binfmt/main.go#L63C1-L103C2">action 的源码</a>，可以发现底层实现是通过<code>binfmt_misc</code>来实现的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">init</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 定义了一些全局变量</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">flag</span><span class="p">.</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">mount</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;mount&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;/proc/sys/fs/binfmt_misc&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;binfmt_misc mount point&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">flag</span><span class="p">.</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">toInstall</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;install&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;architectures to install&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">flag</span><span class="p">.</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">toUninstall</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;uninstall&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;architectures to uninstall&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">flag</span><span class="p">.</span><span class="nf">BoolVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">flVersion</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;version&#34;</span><span class="p">,</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;display version&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">install</span><span class="p">(</span><span class="nx">arch</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">cfg</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">configs</span><span class="p">[</span><span class="nx">arch</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 拼接路径为/proc/sys/fs/binfmt_misc/register，打开这个文件检查是否能够打开成功</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">register</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">filepath</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">mount</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;register&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">file</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nf">OpenFile</span><span class="p">(</span><span class="nx">register</span><span class="p">,</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">O_WRONLY</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">binaryBasename</span><span class="p">,</span><span class="w"> </span><span class="nx">binaryFullpath</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">getBinaryNames</span><span class="p">(</span><span class="nx">cfg</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// 向/proc/sys/fs/binfmt_misc/register 写入 line，注册二进制格式</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">line</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;:%s:M:0:%s:%s:%s:%s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">binaryBasename</span><span class="p">,</span><span class="w"> </span><span class="nx">cfg</span><span class="p">.</span><span class="nx">magic</span><span class="p">,</span><span class="w"> </span><span class="nx">cfg</span><span class="p">.</span><span class="nx">mask</span><span class="p">,</span><span class="w"> </span><span class="nx">binaryFullpath</span><span class="p">,</span><span class="w"> </span><span class="nx">flags</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">file</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">line</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">e</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">err</span><span class="p">.(</span><span class="o">*</span><span class="nx">os</span><span class="p">.</span><span class="nx">PathError</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">Err</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">syscall</span><span class="p">.</span><span class="nx">EEXIST</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;%s already registered&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">binaryBasename</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;cannot register %q to %s: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">binaryFullpath</span><span class="p">,</span><span class="w"> </span><span class="nx">register</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h1 id="q--a">Q &amp; A</h1>
<h2 id="为何procsysfsbinfmt_miscregister-文件是只读的">为何/proc/sys/fs/binfmt_misc/register 文件是只读的？</h2>
<p><code>/proc/sys/fs/binfmt_misc/register</code> 文件是只写的，这是因为在 Linux 中，<code>/proc</code> 文件系统下的很多文件都是通过对文件进行写入来进行配置和控制的。这些文件通常<strong>代表内核参数或控制接口</strong>，提供了一种方便的方式来与内核进行交互。</p>
<p>对于 <code>/proc/sys/fs/binfmt_misc/register</code> 文件来说，通过写入注册信息，用户可以向内核注册新的二进制格式，告知内核如何执行特定的二进制文件。这种只写的设计是为了保持简单性和安全性。允许用户在运行时动态地注册新的格式，而不是从文件中读取注册信息，可以提供更大的灵活性。</p>
<p>虽然 <code>/proc/sys/fs/binfmt_misc/register</code> 文件是只写的，但是通过向文件中写入正确格式的注册信息，用户仍然能够有效地配置新的二进制格式。这种设计符合 Linux 中的文件系统和权限模型。</p>
<h1 id="参考资料">参考资料</h1>
<p><a href="https://github.com/torvalds/linux/pulse">Kernel Support for miscellaneous Binary Formats (binfmt_misc)</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>解决Armoury Crate(华硕奥创中心）安装驱动卡在55%</title>
      <link>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3armoury-crate-%E5%8D%8E%E7%A1%95%E5%A5%A5%E5%88%9B%E4%B8%AD%E5%BF%83%E5%AE%89%E8%A3%85%E9%A9%B1%E5%8A%A8%E5%8D%A1%E5%9C%A855/</link>
      <pubDate>Sun, 07 Jan 2024 10:37:28 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3armoury-crate-%E5%8D%8E%E7%A1%95%E5%A5%A5%E5%88%9B%E4%B8%AD%E5%BF%83%E5%AE%89%E8%A3%85%E9%A9%B1%E5%8A%A8%E5%8D%A1%E5%9C%A855/</guid>
      <description>&lt;p&gt;卡在55%的基本上驱动已经下载完成，但是还没有安装。这时我们找到下载的文件手动安装即可。一般保存的路径为&lt;code&gt;C:\Users\Administrator\AppData\Local\Packages\B9ECED6F.ArmouryCrate_qmba6cd70vzyy\LocalState\SupportTemp&lt;/code&gt;，找到需要安装的驱动文件夹，双击&lt;code&gt;setup.exe&lt;/code&gt;即可。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>卡在55%的基本上驱动已经下载完成，但是还没有安装。这时我们找到下载的文件手动安装即可。一般保存的路径为<code>C:\Users\Administrator\AppData\Local\Packages\B9ECED6F.ArmouryCrate_qmba6cd70vzyy\LocalState\SupportTemp</code>，找到需要安装的驱动文件夹，双击<code>setup.exe</code>即可。</p>
]]></content:encoded>
    </item>
    <item>
      <title>迁移WSL2到非系统盘</title>
      <link>https://lifeislife.cn/posts/%E8%BF%81%E7%A7%BBwsl2%E5%88%B0%E9%9D%9E%E7%B3%BB%E7%BB%9F%E7%9B%98/</link>
      <pubDate>Sat, 06 Jan 2024 16:47:57 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%BF%81%E7%A7%BBwsl2%E5%88%B0%E9%9D%9E%E7%B3%BB%E7%BB%9F%E7%9B%98/</guid>
      <description>&lt;h1 id=&#34;迁移过程&#34;&gt;迁移过程&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 关闭WSL2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看WSL2的状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl -l -v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME                   STATE           VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Stopped         &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Ubuntu         Stopped         &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导出WSL2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --export Ubuntu D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\u&lt;/span&gt;buntu.tar
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;正在导出，这可能需要几分钟时间。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;操作成功完成。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除WSL2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --unregister Ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;正在注销。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;操作成功完成。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导入WSL2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --import Ubuntu D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2 D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\u&lt;/span&gt;buntu.tar --version &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;正在导入，这可能需要几分钟时间。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;操作成功完成。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置默认用户，如果不设置将会使用root用户，因为之前我一直使用自己创建的用户，所以需要设置，否则zsh配置文件会找不到&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ubuntu.exe config --default-user nic
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启动WSL2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可能有人和我一样安装了Docker，启用WSL后，docker运行数据都在WSL发行版中，文件位置都只能由WSL管理！&lt;/p&gt;
&lt;p&gt;安装docker后，docker会自动创建2个发行版：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl -l -v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME                   STATE           VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Stopped         &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  docker-desktop         Stopped         &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  docker-desktop-data    Stopped         &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;和上面一样需要到导出，删除，导入。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 关闭docker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;将Docker程序退出即可
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导出docker-desktop&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --export docker-desktop D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop.tar
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除docker-desktop&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --unregister docker-desktop
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导入docker-desktop&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --import docker-desktop D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop.tar --version &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导出docker-desktop-data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --export docker-desktop-data D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop-data.tar
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除docker-desktop-data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --unregister docker-desktop-data
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导入docker-desktop-data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --import docker-desktop-data D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop-data D:&lt;span class=&#34;se&#34;&gt;\w&lt;/span&gt;sl2&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop-data&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;ocker-desktop-data.tar --version &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="迁移过程">迁移过程</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 关闭WSL2</span>
</span></span><span class="line"><span class="cl">wsl --shutdown
</span></span><span class="line"><span class="cl"><span class="c1"># 查看WSL2的状态</span>
</span></span><span class="line"><span class="cl">wsl -l -v
</span></span><span class="line"><span class="cl">NAME                   STATE           VERSION
</span></span><span class="line"><span class="cl">Stopped         <span class="m">2</span>
</span></span><span class="line"><span class="cl">  Ubuntu         Stopped         <span class="m">2</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 导出WSL2</span>
</span></span><span class="line"><span class="cl">wsl --export Ubuntu D:<span class="se">\w</span>sl2<span class="se">\u</span>buntu.tar
</span></span><span class="line"><span class="cl">正在导出，这可能需要几分钟时间。
</span></span><span class="line"><span class="cl">操作成功完成。
</span></span><span class="line"><span class="cl"><span class="c1"># 删除WSL2</span>
</span></span><span class="line"><span class="cl">wsl --unregister Ubuntu
</span></span><span class="line"><span class="cl">正在注销。
</span></span><span class="line"><span class="cl">操作成功完成。
</span></span><span class="line"><span class="cl"><span class="c1"># 导入WSL2</span>
</span></span><span class="line"><span class="cl">wsl --import Ubuntu D:<span class="se">\w</span>sl2 D:<span class="se">\w</span>sl2<span class="se">\u</span>buntu.tar --version <span class="m">2</span>
</span></span><span class="line"><span class="cl">正在导入，这可能需要几分钟时间。
</span></span><span class="line"><span class="cl">操作成功完成。
</span></span><span class="line"><span class="cl"><span class="c1"># 设置默认用户，如果不设置将会使用root用户，因为之前我一直使用自己创建的用户，所以需要设置，否则zsh配置文件会找不到</span>
</span></span><span class="line"><span class="cl">ubuntu.exe config --default-user nic
</span></span><span class="line"><span class="cl"><span class="c1"># 启动WSL2</span>
</span></span><span class="line"><span class="cl">wsl
</span></span></code></pre></div><p>可能有人和我一样安装了Docker，启用WSL后，docker运行数据都在WSL发行版中，文件位置都只能由WSL管理！</p>
<p>安装docker后，docker会自动创建2个发行版：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wsl -l -v
</span></span><span class="line"><span class="cl">NAME                   STATE           VERSION
</span></span><span class="line"><span class="cl">Stopped         <span class="m">2</span>
</span></span><span class="line"><span class="cl">  docker-desktop         Stopped         <span class="m">2</span>
</span></span><span class="line"><span class="cl">  docker-desktop-data    Stopped         <span class="m">2</span>
</span></span></code></pre></div><p>和上面一样需要到导出，删除，导入。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 关闭docker</span>
</span></span><span class="line"><span class="cl">将Docker程序退出即可
</span></span><span class="line"><span class="cl"><span class="c1"># 导出docker-desktop</span>
</span></span><span class="line"><span class="cl">wsl --export docker-desktop D:<span class="se">\w</span>sl2<span class="se">\d</span>ocker-desktop<span class="se">\d</span>ocker-desktop.tar
</span></span><span class="line"><span class="cl"><span class="c1"># 删除docker-desktop</span>
</span></span><span class="line"><span class="cl">wsl --unregister docker-desktop
</span></span><span class="line"><span class="cl"><span class="c1"># 导入docker-desktop</span>
</span></span><span class="line"><span class="cl">wsl --import docker-desktop D:<span class="se">\w</span>sl2<span class="se">\d</span>ocker-desktop D:<span class="se">\w</span>sl2<span class="se">\d</span>ocker-desktop<span class="se">\d</span>ocker-desktop.tar --version <span class="m">2</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 导出docker-desktop-data</span>
</span></span><span class="line"><span class="cl">wsl --export docker-desktop-data D:<span class="se">\w</span>sl2<span class="se">\d</span>ocker-desktop-data.tar
</span></span><span class="line"><span class="cl"><span class="c1"># 删除docker-desktop-data</span>
</span></span><span class="line"><span class="cl">wsl --unregister docker-desktop-data
</span></span><span class="line"><span class="cl"><span class="c1"># 导入docker-desktop-data</span>
</span></span><span class="line"><span class="cl">wsl --import docker-desktop-data D:<span class="se">\w</span>sl2<span class="se">\d</span>ocker-desktop-data D:<span class="se">\w</span>sl2<span class="se">\d</span>ocker-desktop-data<span class="se">\d</span>ocker-desktop-data.tar --version <span class="m">2</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>耳机换电池复活耳机小记</title>
      <link>https://lifeislife.cn/posts/%E8%80%B3%E6%9C%BA%E6%8D%A2%E7%94%B5%E6%B1%A0%E5%A4%8D%E6%B4%BB%E8%80%B3%E6%9C%BA%E5%B0%8F%E8%AE%B0/</link>
      <pubDate>Sat, 06 Jan 2024 10:34:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%80%B3%E6%9C%BA%E6%8D%A2%E7%94%B5%E6%B1%A0%E5%A4%8D%E6%B4%BB%E8%80%B3%E6%9C%BA%E5%B0%8F%E8%AE%B0/</guid>
      <description>&lt;p&gt;半月前给用了三年的小米 10 换了一块二手电池，手机又可以再战三年了。想着自己还有两个耳机，也没有严重故障，只是电池亏点用不了半小时，于是也想着换个电池，看看能不能复活。本来以为耳机太小，太精密，怕自己修不了，但是想想如果不换电池，这个耳机也还是死路一条，不如自己尝试尝试，也能积攒一点经验。实操下来，发现并没有想象中那么难，只要有耐心，还是能修好的。甚至比换手机电池还要容易些，没有那么多螺丝需要拆，也没有那么多胶需要撬。&lt;/p&gt;
&lt;h1 id=&#34;小米-flipbuds-pro&#34;&gt;小米 FlipBuds Pro&lt;/h1&gt;
&lt;h2 id=&#34;配件和工具准备&#34;&gt;配件和工具准备&lt;/h2&gt;
&lt;p&gt;配件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CR1154 3V 锂电池（可以直接淘宝搜索 FlipBuds Pro 电池，也可以直接搜这个型号，最好买带线的，不带线焊接会比较麻烦）&lt;/li&gt;
&lt;/ul&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;h2 id=&#34;拆机&#34;&gt;拆机&lt;/h2&gt;
&lt;p&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//2024/01/06/c97ee9c173d49027e1c145e7748f6b4e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/c97ee9c173d49027e1c145e7748f6b4e.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;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//2024/01/06/17af1dbe363d6214d480c9fe2aa7519b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/17af1dbe363d6214d480c9fe2aa7519b.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;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//2024/01/06/943150df37aebd615bb13f44495a620a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/943150df37aebd615bb13f44495a620a.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;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//2024/01/06/10486c9c31baf01807387577005fde05.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/10486c9c31baf01807387577005fde05.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;p&gt;没有微距镜头，用了&lt;a href=&#34;https://www.52audio.com/archives/81082.html&#34;&gt;我爱音频网的图片&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;装机&#34;&gt;装机&lt;/h2&gt;
&lt;p&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//2024/01/06/3009a9fd386dfa462ed8670f86d589cb.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/3009a9fd386dfa462ed8670f86d589cb.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//2024/01/06/f4c49ecc202e7f2d1aeba87877ad6411.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/f4c49ecc202e7f2d1aeba87877ad6411.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;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//2024/01/06/2aebcb22ce86092bc3fe9c6404f0dcfd.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/2aebcb22ce86092bc3fe9c6404f0dcfd.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;h2 id=&#34;测试&#34;&gt;测试&lt;/h2&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//2024/01/06/574b07106665802d5f64677b6f1fb4d9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/574b07106665802d5f64677b6f1fb4d9.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;h1 id=&#34;索尼-wf-1000xm3&#34;&gt;索尼 WF-1000XM3、&lt;/h1&gt;
&lt;h2 id=&#34;配件和工具准备-1&#34;&gt;配件和工具准备&lt;/h2&gt;
&lt;p&gt;配件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CR1254 3.7V 锂电池，原装的是 VARTA 品牌的，我买的是 ZeniPower，只要型号一样，品牌自己选。甚至可以选 3.85V，75mAh 的续航更好。&lt;/li&gt;
&lt;/ul&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;h2 id=&#34;拆机-1&#34;&gt;拆机&lt;/h2&gt;
&lt;p&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//2024/01/08/4a441a4377b1a34b8cf32615a75284ee.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/08/4a441a4377b1a34b8cf32615a75284ee.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;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//2024/01/08/a76356a4a815618316219d9cf652d9fb.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/08/a76356a4a815618316219d9cf652d9fb.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;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;p&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//2024/01/08/a2c39abaab296e00d56e4677efc472fb.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/08/a2c39abaab296e00d56e4677efc472fb.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;
</description>
      <content:encoded><![CDATA[<p>半月前给用了三年的小米 10 换了一块二手电池，手机又可以再战三年了。想着自己还有两个耳机，也没有严重故障，只是电池亏点用不了半小时，于是也想着换个电池，看看能不能复活。本来以为耳机太小，太精密，怕自己修不了，但是想想如果不换电池，这个耳机也还是死路一条，不如自己尝试尝试，也能积攒一点经验。实操下来，发现并没有想象中那么难，只要有耐心，还是能修好的。甚至比换手机电池还要容易些，没有那么多螺丝需要拆，也没有那么多胶需要撬。</p>
<h1 id="小米-flipbuds-pro">小米 FlipBuds Pro</h1>
<h2 id="配件和工具准备">配件和工具准备</h2>
<p>配件</p>
<ul>
<li>CR1154 3V 锂电池（可以直接淘宝搜索 FlipBuds Pro 电池，也可以直接搜这个型号，最好买带线的，不带线焊接会比较麻烦）</li>
</ul>
<p>工具</p>
<ul>
<li>热风枪（吹风机即可）</li>
<li>电烙铁（小米的这个耳机是需要焊接电池线的）</li>
<li>镊子（清理耳机里面的胶水）</li>
</ul>
<h2 id="拆机">拆机</h2>
<p>用吹风机对准耳机吹一两分钟，沿着合模线徒手就能掰开。打开腔体时需要慢一点，有地方有胶水粘粘，不是一打开就是图中的样子，需要把胶水清理干净。</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//2024/01/06/c97ee9c173d49027e1c145e7748f6b4e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/c97ee9c173d49027e1c145e7748f6b4e.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>
<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//2024/01/06/17af1dbe363d6214d480c9fe2aa7519b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/17af1dbe363d6214d480c9fe2aa7519b.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>
<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//2024/01/06/943150df37aebd615bb13f44495a620a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/943150df37aebd615bb13f44495a620a.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>
<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//2024/01/06/10486c9c31baf01807387577005fde05.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/10486c9c31baf01807387577005fde05.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>
<p>没有微距镜头，用了<a href="https://www.52audio.com/archives/81082.html">我爱音频网的图片</a></p>
</blockquote>
<h2 id="装机">装机</h2>
<p>将电池红线连接到正极，黑线连接到负极，然后将电池放回原位，合模即可。如果不知道正负极，可以在耳机线路板上有标识。</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//2024/01/06/3009a9fd386dfa462ed8670f86d589cb.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/3009a9fd386dfa462ed8670f86d589cb.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//2024/01/06/f4c49ecc202e7f2d1aeba87877ad6411.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/f4c49ecc202e7f2d1aeba87877ad6411.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>
<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//2024/01/06/2aebcb22ce86092bc3fe9c6404f0dcfd.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/2aebcb22ce86092bc3fe9c6404f0dcfd.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>
<h2 id="测试">测试</h2>
<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//2024/01/06/574b07106665802d5f64677b6f1fb4d9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/06/574b07106665802d5f64677b6f1fb4d9.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>
<h1 id="索尼-wf-1000xm3">索尼 WF-1000XM3、</h1>
<h2 id="配件和工具准备-1">配件和工具准备</h2>
<p>配件</p>
<ul>
<li>CR1254 3.7V 锂电池，原装的是 VARTA 品牌的，我买的是 ZeniPower，只要型号一样，品牌自己选。甚至可以选 3.85V，75mAh 的续航更好。</li>
</ul>
<p>工具</p>
<ul>
<li>热风枪（吹风机即可）</li>
<li>镊子（清理耳机里面的胶水）</li>
<li>螺丝刀</li>
</ul>
<h2 id="拆机-1">拆机</h2>
<p>用吹风机对着耳机的触摸原盘吹一两分钟胶水只在这块区域，边缘是卡扣不需要加热，指甲长的话直接用指甲掰开即可。打开后的样子如下图所示。</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//2024/01/08/4a441a4377b1a34b8cf32615a75284ee.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/08/4a441a4377b1a34b8cf32615a75284ee.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>
<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//2024/01/08/a76356a4a815618316219d9cf652d9fb.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/08/a76356a4a815618316219d9cf652d9fb.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>
<h2 id="安装">安装</h2>
<p>将电池放回原位，盖板合上，拧上螺丝即可。在安装回耳机盖时需要注意耳机盖是分左右的，如果左右颠倒了是合不上的。参考图中的位置。</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//2024/01/08/a2c39abaab296e00d56e4677efc472fb.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/08/a2c39abaab296e00d56e4677efc472fb.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>
]]></content:encoded>
    </item>
    <item>
      <title>解决Adobe Photoshop正版升级弹窗</title>
      <link>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3adobe-photoshop%E6%AD%A3%E7%89%88%E5%8D%87%E7%BA%A7%E5%BC%B9%E7%AA%97/</link>
      <pubDate>Sat, 23 Dec 2023 12:08:36 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3adobe-photoshop%E6%AD%A3%E7%89%88%E5%8D%87%E7%BA%A7%E5%BC%B9%E7%AA%97/</guid>
      <description>&lt;h2 id=&#34;问题背景&#34;&gt;问题背景&lt;/h2&gt;
&lt;p&gt;不知什么原因，一直正常使用的Adobe Photoshop 2021突然开始弹窗提示升级，还是日文的（应该是代理到到日本了），可能根据地区不同，弹窗的内容也不同，但是都是提示升级，倒计时10天，不知道十天之后啥情况。&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//2023/12/23/a8b83c2f3e0916e9b2fd92dc7332dc89.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/23/a8b83c2f3e0916e9b2fd92dc7332dc89.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;h2 id=&#34;解决方法&#34;&gt;解决方法&lt;/h2&gt;
&lt;p&gt;Adobe的其他软件同理，比如我在用的Lightroom也是这样，解决方法也是一样的。以下方式基于Windows 11，其他版本的Windows也是类似的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果没有用代理&lt;/strong&gt;，可以直接将Photoshop程序禁止联网。可以通过配置Windows的防火墙来实现，具体方法如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;打开设置搜索“防火墙”，打开Windows防火墙，选择“高级设置”。&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//2023/12/23/a38bd63b6c184481c797cd3143e3b21d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/23/a38bd63b6c184481c797cd3143e3b21d.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;/li&gt;
&lt;li&gt;
&lt;p&gt;选择“出站规则”，点击“新建规则”，选择规则类型为“程序”，程序选择为Photoshop的安装目录下的Photoshop.exe，然后选择“阻止连接”，一路下一步，最后命名规则为“禁止Photoshop联网”。


&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//2023/12/23/31498beb037fc282838480a3624850cc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/23/31498beb037fc282838480a3624850cc.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;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;如果使用了代理&lt;/strong&gt;，防火墙可能会被绕过，Photoshop可能仍然会通过代理访问更新服务器。需要通过配置代理将Photoshop禁止代理。如果不配置，可以在&lt;strong&gt;每次打开Adobe相关软件前关闭代理&lt;/strong&gt;，如果配置，这里以Clash for Windows为例，可以通过配置Clash的规则来实现，不用关闭代理。具体方法如下：&lt;/p&gt;
&lt;p&gt;点击“Profiles”，选择自己的订阅，右击选择“Edit”，将以下这些规则添加到配置文件中保存即可，把配置放到最前面，这样优先级最高。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Final
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- MATCH,Proxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,Lightroom.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,lightroom.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,Photoshop.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,photoshop.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,Adobe Lightroom CEF Helper.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,AdobeIPCBroker.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,dynamiclinkmanager.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- PROCESS-NAME,dynamiclinkmediaserver.exe,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobe.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobelogin.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobesc.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobe.io,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobecc.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobemarketingcloud.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobe.net,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,adobeexchange.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,photoshop.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-SUFFIX,photoshop.adobe.com,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- DOMAIN-KEYWORD,photoshop,REJECT
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其他程序你可以自己添加，格式为&lt;code&gt;PROCESS-NAME,程序名,REJECT&lt;/code&gt;。表示拒绝这些程序访问网络。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="问题背景">问题背景</h2>
<p>不知什么原因，一直正常使用的Adobe Photoshop 2021突然开始弹窗提示升级，还是日文的（应该是代理到到日本了），可能根据地区不同，弹窗的内容也不同，但是都是提示升级，倒计时10天，不知道十天之后啥情况。</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//2023/12/23/a8b83c2f3e0916e9b2fd92dc7332dc89.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/23/a8b83c2f3e0916e9b2fd92dc7332dc89.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>
<h2 id="解决方法">解决方法</h2>
<p>Adobe的其他软件同理，比如我在用的Lightroom也是这样，解决方法也是一样的。以下方式基于Windows 11，其他版本的Windows也是类似的。</p>
<p><strong>如果没有用代理</strong>，可以直接将Photoshop程序禁止联网。可以通过配置Windows的防火墙来实现，具体方法如下：</p>
<ol>
<li>
<p>打开设置搜索“防火墙”，打开Windows防火墙，选择“高级设置”。</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//2023/12/23/a38bd63b6c184481c797cd3143e3b21d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/23/a38bd63b6c184481c797cd3143e3b21d.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>
</li>
<li>
<p>选择“出站规则”，点击“新建规则”，选择规则类型为“程序”，程序选择为Photoshop的安装目录下的Photoshop.exe，然后选择“阻止连接”，一路下一步，最后命名规则为“禁止Photoshop联网”。


<!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//2023/12/23/31498beb037fc282838480a3624850cc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/23/31498beb037fc282838480a3624850cc.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>
</li>
</ol>
<p><strong>如果使用了代理</strong>，防火墙可能会被绕过，Photoshop可能仍然会通过代理访问更新服务器。需要通过配置代理将Photoshop禁止代理。如果不配置，可以在<strong>每次打开Adobe相关软件前关闭代理</strong>，如果配置，这里以Clash for Windows为例，可以通过配置Clash的规则来实现，不用关闭代理。具体方法如下：</p>
<p>点击“Profiles”，选择自己的订阅，右击选择“Edit”，将以下这些规则添加到配置文件中保存即可，把配置放到最前面，这样优先级最高。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># Final
</span></span><span class="line"><span class="cl">- MATCH,Proxy
</span></span><span class="line"><span class="cl">- PROCESS-NAME,Lightroom.exe,REJECT
</span></span><span class="line"><span class="cl">- PROCESS-NAME,lightroom.exe,REJECT
</span></span><span class="line"><span class="cl">- PROCESS-NAME,Photoshop.exe,REJECT
</span></span><span class="line"><span class="cl">- PROCESS-NAME,photoshop.exe,REJECT
</span></span><span class="line"><span class="cl">- PROCESS-NAME,Adobe Lightroom CEF Helper.exe,REJECT
</span></span><span class="line"><span class="cl">- PROCESS-NAME,AdobeIPCBroker.exe,REJECT
</span></span><span class="line"><span class="cl">- PROCESS-NAME,dynamiclinkmanager.exe,REJECT
</span></span><span class="line"><span class="cl">- PROCESS-NAME,dynamiclinkmediaserver.exe,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobe.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobelogin.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobesc.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobe.io,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobecc.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobemarketingcloud.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobe.net,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,adobeexchange.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,photoshop.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-SUFFIX,photoshop.adobe.com,REJECT
</span></span><span class="line"><span class="cl">- DOMAIN-KEYWORD,photoshop,REJECT
</span></span></code></pre></div><p>其他程序你可以自己添加，格式为<code>PROCESS-NAME,程序名,REJECT</code>。表示拒绝这些程序访问网络。</p>
]]></content:encoded>
    </item>
    <item>
      <title>ZWIFT 使用 Tips</title>
      <link>https://lifeislife.cn/posts/zwift%E4%BD%BF%E7%94%A8tips/</link>
      <pubDate>Sun, 17 Dec 2023 13:24:55 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/zwift%E4%BD%BF%E7%94%A8tips/</guid>
      <description>&lt;h1 id=&#34;安装与配置&#34;&gt;安装与配置&lt;/h1&gt;
&lt;p&gt;登录 Zwift 官网下载安装包，安装完成后打开，输入邮箱和密码登录。但是我的电脑离骑行台较远，蓝牙很不稳定，Zwift 还有一个配套的手机 App，叫 Companion，可以在谷歌商店安装。安装后打开登录。Zwift PC 客户端登录后在配对装置页面左上角选择“透过手机进行配对”，正常情况下手机端会自动弹出配对页面。记得打开手机的蓝牙开关。&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//2023/12/17/0573bf51587d55e549ac5fd4719733b4.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/17/0573bf51587d55e549ac5fd4719733b4.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//2023/12/17/6fb1f8bb10562bb55eb8b7eddb97a0ce.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/17/6fb1f8bb10562bb55eb8b7eddb97a0ce.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;h1 id=&#34;道具功能介绍&#34;&gt;道具功能介绍&lt;/h1&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//2024/06/13/9f7b084ae99d7eed0ba7e01aea33d59c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/9f7b084ae99d7eed0ba7e01aea33d59c.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//2024/06/13/5d91493b1d349bbbcc46b16d65699d36.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/5d91493b1d349bbbcc46b16d65699d36.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//2024/06/13/ba3a70faede05b47fffab9e13e6139ff.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/ba3a70faede05b47fffab9e13e6139ff.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;h1 id=&#34;常见问题&#34;&gt;常见问题&lt;/h1&gt;
&lt;h2 id=&#34;启动后闪退&#34;&gt;启动后闪退&lt;/h2&gt;
&lt;p&gt;将输入法切换为英文模式再重新启动。&lt;/p&gt;
&lt;h2 id=&#34;没有其他玩家只有自己&#34;&gt;没有其他玩家，只有自己&lt;/h2&gt;
&lt;p&gt;挂全局代理重新启动。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="安装与配置">安装与配置</h1>
<p>登录 Zwift 官网下载安装包，安装完成后打开，输入邮箱和密码登录。但是我的电脑离骑行台较远，蓝牙很不稳定，Zwift 还有一个配套的手机 App，叫 Companion，可以在谷歌商店安装。安装后打开登录。Zwift PC 客户端登录后在配对装置页面左上角选择“透过手机进行配对”，正常情况下手机端会自动弹出配对页面。记得打开手机的蓝牙开关。</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//2023/12/17/0573bf51587d55e549ac5fd4719733b4.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/17/0573bf51587d55e549ac5fd4719733b4.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//2023/12/17/6fb1f8bb10562bb55eb8b7eddb97a0ce.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/17/6fb1f8bb10562bb55eb8b7eddb97a0ce.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>
<h1 id="道具功能介绍">道具功能介绍</h1>
<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//2024/06/13/9f7b084ae99d7eed0ba7e01aea33d59c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/9f7b084ae99d7eed0ba7e01aea33d59c.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//2024/06/13/5d91493b1d349bbbcc46b16d65699d36.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/5d91493b1d349bbbcc46b16d65699d36.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//2024/06/13/ba3a70faede05b47fffab9e13e6139ff.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/06/13/ba3a70faede05b47fffab9e13e6139ff.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>
<h1 id="常见问题">常见问题</h1>
<h2 id="启动后闪退">启动后闪退</h2>
<p>将输入法切换为英文模式再重新启动。</p>
<h2 id="没有其他玩家只有自己">没有其他玩家，只有自己</h2>
<p>挂全局代理重新启动。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Nexus搭建内部镜像</title>
      <link>https://lifeislife.cn/posts/nexus-%E9%95%9C%E5%83%8F%E4%BB%A3%E7%90%86/</link>
      <pubDate>Sat, 16 Dec 2023 12:16:34 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/nexus-%E9%95%9C%E5%83%8F%E4%BB%A3%E7%90%86/</guid>
      <description>&lt;h1 id=&#34;docker-compose&#34;&gt;Docker-Compose&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nexus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;sonatype/nexus3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nexus&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;8081&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8081&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/srv/nexus/data:/nexus-data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;修改/srv/nexus目录的所有者为当前用户：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chown -R username:username /srv/nexus
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;修改data目录有最高权限，否则无法启动成功：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod -R &lt;span class=&#34;m&#34;&gt;777&lt;/span&gt; /srv/nexus/data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;代理docker-hub&#34;&gt;代理Docker Hub&lt;/h1&gt;
&lt;h2 id=&#34;登录web页面&#34;&gt;登录WEB页面&lt;/h2&gt;
&lt;p&gt;登录WEB页面，地址为：&lt;a href=&#34;http://192.168.1.9:8081%E3%80%82&#34;&gt;http://192.168.1.9:8081。&lt;/a&gt;
用户名为：admin，密码通过命令获取：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; nexus3 cat /nexus-data/admin.password
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;创建blob&#34;&gt;创建Blob&lt;/h2&gt;
&lt;p&gt;在 Nexus Repository Manager 中，Blob Store（二进制大对象存储）是一个用于存储仓库数据的核心组件。Blob Store 主要用于存储各种二进制文件，例如软件包、依赖库、构建产物等，这些文件通常被称为“blob”。&lt;/p&gt;
&lt;p&gt;Blob Store 的作用包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;存储二进制文件：&lt;/strong&gt; Blob Store 被设计用来安全、可靠地存储二进制文件。这些文件可以是各种形式的构建产物、软件包、依赖库等。Blob Store 是 Nexus 仓库管理系统的核心，它为这些文件提供了一个中央存储位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;支持不同类型的存储后端：&lt;/strong&gt; Nexus 支持不同类型的 Blob Store，例如本地文件系统、云存储（如Amazon S3、Google Cloud Storage）等。这使得用户可以根据需求选择不同的存储后端，并根据实际情况进行扩展或迁移。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;提供存储策略：&lt;/strong&gt; Blob Store 允许你定义存储策略，以确定何时以及如何清理或删除不再需要的文件。这对于管理仓库的存储空间非常重要，可以根据策略自动清理不再需要的快照或旧版本。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;支持代理和缓存：&lt;/strong&gt; 在 Maven Repository 的场景下，Blob Store 还可以用于代理远程 Maven 仓库，并缓存远程仓库中的文件。这有助于提高构建性能，减少对远程仓库的依赖。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们缓存的镜像需要存储为blob，所以需要创建一个Blob store。点击左侧菜单栏的Blob Stores，然后点击Create blob store，选择Type为File，Name填写为dockerhub。&lt;/p&gt;
&lt;h2 id=&#34;创建repository&#34;&gt;创建Repository&lt;/h2&gt;
&lt;p&gt;在 Nexus Repository Manager 中，有三种主要的仓库类型：Hosted Repository、Proxy Repository、和 Group Repository。每种类型都有不同的作用和用途：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Hosted Repository（托管仓库）:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;作用： 用于存储和管理本地创建的部署（deploy）的二进制文件。这包括你自己或你的团队创建的库，例如 Maven 构件、npm 包、Docker 镜像等。&lt;/li&gt;
&lt;li&gt;使用场景： 当你需要在内部存储和分享自己创建的构建产物时，你可以使用 Hosted Repository。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Proxy Repository（代理仓库）:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;作用： 用于代理和缓存远程仓库的二进制文件。当你从远程仓库获取构建依赖时，Proxy Repository 会将这些文件缓存在本地，从而提高构建性能并减少对远程仓库的依赖。&lt;/li&gt;
&lt;li&gt;使用场景： 在构建过程中，你通常会依赖于一些公共的远程仓库，例如 Maven Central、npm registry、Docker Hub等。使用 Proxy Repository 可以有效地管理这些依赖并减少对远程仓库的直接访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Group Repository（组合仓库）:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;作用： 允许你将多个仓库组合成一个逻辑单元。当你需要在构建中同时使用多个仓库的内容时，Group Repository 可以将这些仓库组合在一起，使它们在应用程序中看起来像一个单一的仓库。&lt;/li&gt;
&lt;li&gt;使用场景： 当你有多个 Proxy Repository 或 Hosted Repository 时，你可以使用 Group Repository 将它们组合在一起。这对于简化构建配置、统一依赖管理等非常有用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们一般创建三个仓库，proxy代理公共镜像，hosted保存自己的镜像，group将proxy和hosted组合在一起。我们分别创建三个仓库。&lt;/p&gt;
&lt;h3 id=&#34;proxy&#34;&gt;proxy&lt;/h3&gt;
&lt;p&gt;点击左侧菜单栏的Repositories，然后点击Create repository，选择Docker (proxy)，按照下图填写：&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//2023/12/16/65fc511b83db1d0af9a6559da6a80162.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/65fc511b83db1d0af9a6559da6a80162.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;Remote storage 图中填写为&lt;code&gt;https://registry-1.docker.io&lt;/code&gt;，这是dockerhub的地址。但是实测会很慢，所以我们使用加速地址&lt;code&gt;https://dockerproxy.com&lt;/code&gt;。需要注意修改一下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;还有一些国内镜像源可选，可以参考这个项目。&lt;a href=&#34;https://github.com/docker-practice/docker-registry-cn-mirror-test/actions/runs/7228669255&#34;&gt;Test Registry · docker-practice/docker-registry-cn-mirror-test@83a4dd4&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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//2023/12/16/c41a82504deb2a41eab15e4674199445.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/c41a82504deb2a41eab15e4674199445.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;Blob store选择为刚刚创建的&lt;code&gt;dockerhub&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//2023/12/16/2a4e0d1685e2d3e8a984bc46c0f039d2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/2a4e0d1685e2d3e8a984bc46c0f039d2.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;hosted&#34;&gt;hosted&lt;/h3&gt;
&lt;p&gt;hosted比较简单，只需要填个名称就行了，这里填写为&lt;code&gt;docker-hosted&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;group&#34;&gt;group&lt;/h3&gt;
&lt;p&gt;group 需要注意以下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;HTTP需要单独设置端口号。我们设置与WEB页面不同的端口号，8082即可。&lt;/li&gt;
&lt;li&gt;需要将proxy和hosted都添加到group中，这样才能将两个仓库组合在一起。&lt;/li&gt;
&lt;/ol&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//2023/12/16/922dcd44e678c2e22df359100fe32d53.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/922dcd44e678c2e22df359100fe32d53.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//2023/12/16/8c4d65a43860e9b921eafc6898276123.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/8c4d65a43860e9b921eafc6898276123.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;h2 id=&#34;启用realms&#34;&gt;启用Realms&lt;/h2&gt;
&lt;p&gt;这里要在 Security-Realms 里面启用 Docker Bearer Token Realm。&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//2023/12/16/6dadf22bfabdf994b394e93a5beb6001.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/6dadf22bfabdf994b394e93a5beb6001.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;h2 id=&#34;拉取镜像&#34;&gt;拉取镜像&lt;/h2&gt;
&lt;p&gt;如果直接使用docker pull拉取镜像，会报错：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ docker pull 192.168.1.9:8082/redis
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Using default tag: latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Error response from daemon: Get &amp;#34;https://192.168.1.9:8082/v2/&amp;#34;: http: server gave HTTP response to HTTPS client
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编辑&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;文件，添加以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;insecure-registries&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;192.168.1.9:8082&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;registry-mirrors&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://192.168.1.9:8082&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;注意：registry-mirrors中只保留一个Nexus的地址，这样默认就会从Nexus拉取镜像。如果有多个地址，就可能会从其他地址拉取镜像。因为我们在Nexus中配置的URL就是docker-proxy的地址，他就是一个国内代理地址，所以这里我们只保留一个Nexus的地址就行。
注意： insecure-registries 拼写，不要写成 insecure-registry。最好直接复制，json格式很严格。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;重启Docker服务：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl restart docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;登录Docker Registry：&lt;/p&gt;
&lt;p&gt;需要注意的shi，这里的用户名和密码是Nexus的用户名和密码，不是Docker Hub的用户名和密码。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker login 192.168.1.9:8082 -u admin -p admin123
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;拉取镜像：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ docker pull redis                      
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Using default tag: latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;latest: Pulling from redis
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1f7ce2fa46ab: Already exists 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4827e9d1e197: Pull &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5845062cfda9: Pull &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;44d659adcf8b: Pull &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;b6962d83313d: Pull &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5d29cf86ecab: Pull &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4f4fb700ef54: Pull &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3a2d9f90268c: Pull &lt;span class=&#34;nb&#34;&gt;complete&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Digest: sha256:249e1bfb9448ae9e76807748f8cb3c5cc73e55441b7b36364c61a7428c9e814c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Status: Downloaded newer image &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; 192.168.1.9:8082/redis:latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;192.168.1.9:8082/redis:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以在首页的Browse Docker中看到镜像已经被缓存了。&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//2023/12/16/151588f16856a1e6e2d5215d230ed0de.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/151588f16856a1e6e2d5215d230ed0de.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;h2 id=&#34;推送镜像&#34;&gt;推送镜像&lt;/h2&gt;
&lt;p&gt;如果你需要上传自己修改的镜像，那么就需要修改之前的docker-hosted，其中HTTP中该为8083，和docker-proxy的端口号区分。这样我们就可以从8082下载镜像，从8083推送镜像。&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//2024/01/17/be0df03ad283819763b6a9f97ce88a59.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/17/be0df03ad283819763b6a9f97ce88a59.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;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker tag &amp;lt;imageId or imageName&amp;gt; &amp;lt;nexus-hostname&amp;gt;:&amp;lt;repository-port&amp;gt;/&amp;lt;image&amp;gt;:&amp;lt;tag&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker tag af340544ed62 192.168.1.9:8083/hello-world:mytag
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker push 192.168.1.9:8083/hello-world:mytag
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;拉取这个镜像：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker pull 192.168.1.9:8083/hello-world:mytag
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;代理-yum-源&#34;&gt;代理 YUM 源&lt;/h1&gt;
&lt;h1 id=&#34;代理-apt-源&#34;&gt;代理 APT 源&lt;/h1&gt;
&lt;p&gt;和 YUM 源代理稍有不同的是，APT 源代理时没有 Group 仓库，但是使用APT源的系统如Ubuntu也是有多个版本的，我们只需要在 Distribution 参数里填写需要代理的版本即可，每隔版本用逗号分隔。&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//2024/03/01/11753bea79901711a31f9e435d31fc91.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/01/11753bea79901711a31f9e435d31fc91.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;Ubuntu 16.04 Xenial Xerus&lt;/li&gt;
&lt;li&gt;Ubuntu 18.04 Bionic Beaver&lt;/li&gt;
&lt;li&gt;Ubuntu 20.04 Focal Fossa&lt;/li&gt;
&lt;li&gt;Ubuntu 21.04 Hirsute Hippo&lt;/li&gt;
&lt;li&gt;Ubuntu 21.10 Impish Indri&lt;/li&gt;
&lt;li&gt;Ubuntu 22.04 Jammy Jellyfish&lt;/li&gt;
&lt;li&gt;Ubuntu 22.10 Kinetic Kudu&lt;/li&gt;
&lt;li&gt;Ubuntu 23.04 Lunar Lobster&lt;/li&gt;
&lt;li&gt;Ubuntu 23.10 Mantic Minotau&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代理地址为：http://192.168.1.9:8081/repository/apt-proxy/，我们可以在Ubuntu中使用这个地址来代理APT源。修改&lt;code&gt;/etc/apt/sources.list&lt;/code&gt;文件，将原来的源地址替换为Nexus的地址即可。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 默认注释了源码镜像以提高 apt update 速度，如有需要可自行取消注释&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb http://192.168.1.9:8081/repository/apt-proxy/ jammy main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-updates main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-backports main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-security main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 预发布软件源，不建议启用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-proposed main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# # deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-proposed main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;代理-pip-源&#34;&gt;代理 pip 源&lt;/h1&gt;
&lt;p&gt;使用阿里云作为代理，我们需要在Nexus中创建一个PyPI代理仓库。点击左侧菜单栏的Repositories，然后点击Create repository，选择PyPI (proxy)，按照下图填写：&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/%2F2024%2F03%2F15%2Fc88f9c65dceb08b3b1b701a6625a4764.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F15%2Fc88f9c65dceb08b3b1b701a6625a4764.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;再创建一个PyPI Group仓库，将刚刚创建的PyPI代理仓库和PyPI Hosted仓库添加到Group仓库中。&lt;/p&gt;
&lt;p&gt;编辑&lt;code&gt;/etc/pip.conf&lt;/code&gt;文件，添加以下内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;或者在用户目录下的&lt;code&gt;~/.pip/pip.conf&lt;/code&gt;文件中添加&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[global]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;index = http://192.168.1.9:8081/repository/pypi/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;index-url = http://192.168.1.9:8081/repository/pypi/simple
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;trusted-host =  192.168.1.9
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;在-wsl2-中使用-nexus-代理&#34;&gt;在 WSL2 中使用 Nexus 代理&lt;/h1&gt;
&lt;p&gt;因为 WSL2 使用的是宿主机Windows的Docker desktop作为Docker引擎，所以我们需要在Windows中配置Docker的代理。打开Docker Desktop，点击Settings，然后选择Docker Engine，添加以下内容：&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/%2F2024%2F03%2F15%2Ffa63bec3941423faa870438fc14b06e5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F15%2Ffa63bec3941423faa870438fc14b06e5.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 class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;builder&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;gc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;defaultKeepStorage&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;20GB&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;enabled&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;experimental&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;insecure-registries&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://192.168.1.9:8082&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;registry-mirrors&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://192.168.1.9:8082&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重启Docker Desktop。然后在WSL2中使用 &lt;code&gt;docker info&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/%2F2024%2F03%2F15%2Fa4bf4946f10cce7d25cd5f25ce104fc5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F15%2Fa4bf4946f10cce7d25cd5f25ce104fc5.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;h1 id=&#34;常见错误&#34;&gt;常见错误&lt;/h1&gt;
&lt;h2 id=&#34;docker-login-nexus-unauthorized-authentication-required&#34;&gt;docker login nexus unauthorized authentication required&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;确认登录密码是否正确，密码为Nexus登录密码&lt;/li&gt;
&lt;li&gt;却是否启用Realms&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;docker-login-nexus-connection-refused&#34;&gt;docker login nexus connection refused&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker login 192.168.1.9:8082 -u admin -p admin123
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;确认Docker compose配置文件中已经将端口8082暴露，如果新增需要重启Nexus&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确认防火墙关闭或者已经打开端口&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; sudo ufw allow &lt;span class=&#34;m&#34;&gt;8082&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; sudo ufw allow 8082/tcp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确认已经将私有仓库添加到了&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;，并且及时重启了docker服务&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; sudo systemctl restart docker.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确认已经开启了Http connector&lt;/p&gt;
&lt;ol&gt;
&lt;li&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//2023/12/16/922dcd44e678c2e22df359100fe32d53.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/922dcd44e678c2e22df359100fe32d53.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;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <content:encoded><![CDATA[<h1 id="docker-compose">Docker-Compose</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3.8&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">nexus</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">sonatype/nexus3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">nexus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">8081</span><span class="p">:</span><span class="m">8081</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/srv/nexus/data:/nexus-data</span><span class="w">
</span></span></span></code></pre></div><p>修改/srv/nexus目录的所有者为当前用户：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo chown -R username:username /srv/nexus
</span></span></code></pre></div><p>修改data目录有最高权限，否则无法启动成功：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo chmod -R <span class="m">777</span> /srv/nexus/data
</span></span></code></pre></div><h1 id="代理docker-hub">代理Docker Hub</h1>
<h2 id="登录web页面">登录WEB页面</h2>
<p>登录WEB页面，地址为：<a href="http://192.168.1.9:8081%E3%80%82">http://192.168.1.9:8081。</a>
用户名为：admin，密码通过命令获取：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker <span class="nb">exec</span> nexus3 cat /nexus-data/admin.password
</span></span></code></pre></div><h2 id="创建blob">创建Blob</h2>
<p>在 Nexus Repository Manager 中，Blob Store（二进制大对象存储）是一个用于存储仓库数据的核心组件。Blob Store 主要用于存储各种二进制文件，例如软件包、依赖库、构建产物等，这些文件通常被称为“blob”。</p>
<p>Blob Store 的作用包括：</p>
<ol>
<li>
<p><strong>存储二进制文件：</strong> Blob Store 被设计用来安全、可靠地存储二进制文件。这些文件可以是各种形式的构建产物、软件包、依赖库等。Blob Store 是 Nexus 仓库管理系统的核心，它为这些文件提供了一个中央存储位置。</p>
</li>
<li>
<p><strong>支持不同类型的存储后端：</strong> Nexus 支持不同类型的 Blob Store，例如本地文件系统、云存储（如Amazon S3、Google Cloud Storage）等。这使得用户可以根据需求选择不同的存储后端，并根据实际情况进行扩展或迁移。</p>
</li>
<li>
<p><strong>提供存储策略：</strong> Blob Store 允许你定义存储策略，以确定何时以及如何清理或删除不再需要的文件。这对于管理仓库的存储空间非常重要，可以根据策略自动清理不再需要的快照或旧版本。</p>
</li>
<li>
<p><strong>支持代理和缓存：</strong> 在 Maven Repository 的场景下，Blob Store 还可以用于代理远程 Maven 仓库，并缓存远程仓库中的文件。这有助于提高构建性能，减少对远程仓库的依赖。</p>
</li>
</ol>
<p>我们缓存的镜像需要存储为blob，所以需要创建一个Blob store。点击左侧菜单栏的Blob Stores，然后点击Create blob store，选择Type为File，Name填写为dockerhub。</p>
<h2 id="创建repository">创建Repository</h2>
<p>在 Nexus Repository Manager 中，有三种主要的仓库类型：Hosted Repository、Proxy Repository、和 Group Repository。每种类型都有不同的作用和用途：</p>
<ul>
<li>
<p>Hosted Repository（托管仓库）:</p>
<ul>
<li>作用： 用于存储和管理本地创建的部署（deploy）的二进制文件。这包括你自己或你的团队创建的库，例如 Maven 构件、npm 包、Docker 镜像等。</li>
<li>使用场景： 当你需要在内部存储和分享自己创建的构建产物时，你可以使用 Hosted Repository。</li>
</ul>
</li>
<li>
<p>Proxy Repository（代理仓库）:</p>
<ul>
<li>作用： 用于代理和缓存远程仓库的二进制文件。当你从远程仓库获取构建依赖时，Proxy Repository 会将这些文件缓存在本地，从而提高构建性能并减少对远程仓库的依赖。</li>
<li>使用场景： 在构建过程中，你通常会依赖于一些公共的远程仓库，例如 Maven Central、npm registry、Docker Hub等。使用 Proxy Repository 可以有效地管理这些依赖并减少对远程仓库的直接访问。</li>
</ul>
</li>
<li>
<p>Group Repository（组合仓库）:</p>
<ul>
<li>作用： 允许你将多个仓库组合成一个逻辑单元。当你需要在构建中同时使用多个仓库的内容时，Group Repository 可以将这些仓库组合在一起，使它们在应用程序中看起来像一个单一的仓库。</li>
<li>使用场景： 当你有多个 Proxy Repository 或 Hosted Repository 时，你可以使用 Group Repository 将它们组合在一起。这对于简化构建配置、统一依赖管理等非常有用。</li>
</ul>
</li>
</ul>
<p>我们一般创建三个仓库，proxy代理公共镜像，hosted保存自己的镜像，group将proxy和hosted组合在一起。我们分别创建三个仓库。</p>
<h3 id="proxy">proxy</h3>
<p>点击左侧菜单栏的Repositories，然后点击Create repository，选择Docker (proxy)，按照下图填写：</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//2023/12/16/65fc511b83db1d0af9a6559da6a80162.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/65fc511b83db1d0af9a6559da6a80162.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>Remote storage 图中填写为<code>https://registry-1.docker.io</code>，这是dockerhub的地址。但是实测会很慢，所以我们使用加速地址<code>https://dockerproxy.com</code>。需要注意修改一下。</p>
<blockquote>
<p>还有一些国内镜像源可选，可以参考这个项目。<a href="https://github.com/docker-practice/docker-registry-cn-mirror-test/actions/runs/7228669255">Test Registry · docker-practice/docker-registry-cn-mirror-test@83a4dd4</a></p>
</blockquote>
<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//2023/12/16/c41a82504deb2a41eab15e4674199445.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/c41a82504deb2a41eab15e4674199445.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>Blob store选择为刚刚创建的<code>dockerhub</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//2023/12/16/2a4e0d1685e2d3e8a984bc46c0f039d2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/2a4e0d1685e2d3e8a984bc46c0f039d2.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="hosted">hosted</h3>
<p>hosted比较简单，只需要填个名称就行了，这里填写为<code>docker-hosted</code>。</p>
<h3 id="group">group</h3>
<p>group 需要注意以下：</p>
<ol>
<li>HTTP需要单独设置端口号。我们设置与WEB页面不同的端口号，8082即可。</li>
<li>需要将proxy和hosted都添加到group中，这样才能将两个仓库组合在一起。</li>
</ol>
<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//2023/12/16/922dcd44e678c2e22df359100fe32d53.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/922dcd44e678c2e22df359100fe32d53.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//2023/12/16/8c4d65a43860e9b921eafc6898276123.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/8c4d65a43860e9b921eafc6898276123.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>
<h2 id="启用realms">启用Realms</h2>
<p>这里要在 Security-Realms 里面启用 Docker Bearer Token Realm。</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//2023/12/16/6dadf22bfabdf994b394e93a5beb6001.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/6dadf22bfabdf994b394e93a5beb6001.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>
<h2 id="拉取镜像">拉取镜像</h2>
<p>如果直接使用docker pull拉取镜像，会报错：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ docker pull 192.168.1.9:8082/redis
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Using default tag: latest
</span></span><span class="line"><span class="cl">Error response from daemon: Get &#34;https://192.168.1.9:8082/v2/&#34;: http: server gave HTTP response to HTTPS client
</span></span></code></pre></div><p>编辑<code>/etc/docker/daemon.json</code>文件，添加以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;insecure-registries&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;192.168.1.9:8082&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;registry-mirrors&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;http://192.168.1.9:8082&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p>注意：registry-mirrors中只保留一个Nexus的地址，这样默认就会从Nexus拉取镜像。如果有多个地址，就可能会从其他地址拉取镜像。因为我们在Nexus中配置的URL就是docker-proxy的地址，他就是一个国内代理地址，所以这里我们只保留一个Nexus的地址就行。
注意： insecure-registries 拼写，不要写成 insecure-registry。最好直接复制，json格式很严格。</p>
</blockquote>
<p>重启Docker服务：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl restart docker
</span></span></code></pre></div><p>登录Docker Registry：</p>
<p>需要注意的shi，这里的用户名和密码是Nexus的用户名和密码，不是Docker Hub的用户名和密码。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker login 192.168.1.9:8082 -u admin -p admin123
</span></span></code></pre></div><p>拉取镜像：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ docker pull redis                      
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Using default tag: latest
</span></span><span class="line"><span class="cl">latest: Pulling from redis
</span></span><span class="line"><span class="cl">1f7ce2fa46ab: Already exists 
</span></span><span class="line"><span class="cl">4827e9d1e197: Pull <span class="nb">complete</span> 
</span></span><span class="line"><span class="cl">5845062cfda9: Pull <span class="nb">complete</span> 
</span></span><span class="line"><span class="cl">44d659adcf8b: Pull <span class="nb">complete</span> 
</span></span><span class="line"><span class="cl">b6962d83313d: Pull <span class="nb">complete</span> 
</span></span><span class="line"><span class="cl">5d29cf86ecab: Pull <span class="nb">complete</span> 
</span></span><span class="line"><span class="cl">4f4fb700ef54: Pull <span class="nb">complete</span> 
</span></span><span class="line"><span class="cl">3a2d9f90268c: Pull <span class="nb">complete</span> 
</span></span><span class="line"><span class="cl">Digest: sha256:249e1bfb9448ae9e76807748f8cb3c5cc73e55441b7b36364c61a7428c9e814c
</span></span><span class="line"><span class="cl">Status: Downloaded newer image <span class="k">for</span> 192.168.1.9:8082/redis:latest
</span></span><span class="line"><span class="cl">192.168.1.9:8082/redis:latest
</span></span></code></pre></div><p>可以在首页的Browse Docker中看到镜像已经被缓存了。</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//2023/12/16/151588f16856a1e6e2d5215d230ed0de.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/151588f16856a1e6e2d5215d230ed0de.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>
<h2 id="推送镜像">推送镜像</h2>
<p>如果你需要上传自己修改的镜像，那么就需要修改之前的docker-hosted，其中HTTP中该为8083，和docker-proxy的端口号区分。这样我们就可以从8082下载镜像，从8083推送镜像。</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//2024/01/17/be0df03ad283819763b6a9f97ce88a59.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/01/17/be0df03ad283819763b6a9f97ce88a59.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>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker tag &lt;imageId or imageName&gt; &lt;nexus-hostname&gt;:&lt;repository-port&gt;/&lt;image&gt;:&lt;tag&gt;
</span></span><span class="line"><span class="cl">docker tag af340544ed62 192.168.1.9:8083/hello-world:mytag
</span></span><span class="line"><span class="cl">docker push 192.168.1.9:8083/hello-world:mytag
</span></span></code></pre></div><p>拉取这个镜像：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker pull 192.168.1.9:8083/hello-world:mytag
</span></span></code></pre></div><h1 id="代理-yum-源">代理 YUM 源</h1>
<h1 id="代理-apt-源">代理 APT 源</h1>
<p>和 YUM 源代理稍有不同的是，APT 源代理时没有 Group 仓库，但是使用APT源的系统如Ubuntu也是有多个版本的，我们只需要在 Distribution 参数里填写需要代理的版本即可，每隔版本用逗号分隔。</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//2024/03/01/11753bea79901711a31f9e435d31fc91.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2024/03/01/11753bea79901711a31f9e435d31fc91.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>Ubuntu 16.04 Xenial Xerus</li>
<li>Ubuntu 18.04 Bionic Beaver</li>
<li>Ubuntu 20.04 Focal Fossa</li>
<li>Ubuntu 21.04 Hirsute Hippo</li>
<li>Ubuntu 21.10 Impish Indri</li>
<li>Ubuntu 22.04 Jammy Jellyfish</li>
<li>Ubuntu 22.10 Kinetic Kudu</li>
<li>Ubuntu 23.04 Lunar Lobster</li>
<li>Ubuntu 23.10 Mantic Minotau</li>
</ul>
<p>代理地址为：http://192.168.1.9:8081/repository/apt-proxy/，我们可以在Ubuntu中使用这个地址来代理APT源。修改<code>/etc/apt/sources.list</code>文件，将原来的源地址替换为Nexus的地址即可。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 默认注释了源码镜像以提高 apt update 速度，如有需要可自行取消注释</span>
</span></span><span class="line"><span class="cl">deb http://192.168.1.9:8081/repository/apt-proxy/ jammy main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl">deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-updates main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-updates main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl">deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-backports main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-backports main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-security main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-security main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 预发布软件源，不建议启用</span>
</span></span><span class="line"><span class="cl"><span class="c1"># deb http://192.168.1.9:8081/repository/apt-proxy/ jammy-proposed main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl"><span class="c1"># # deb-src http://192.168.1.9:8081/repository/apt-proxy/ jammy-proposed main restricted universe multiverse</span>
</span></span></code></pre></div><h1 id="代理-pip-源">代理 pip 源</h1>
<p>使用阿里云作为代理，我们需要在Nexus中创建一个PyPI代理仓库。点击左侧菜单栏的Repositories，然后点击Create repository，选择PyPI (proxy)，按照下图填写：</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/%2F2024%2F03%2F15%2Fc88f9c65dceb08b3b1b701a6625a4764.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F15%2Fc88f9c65dceb08b3b1b701a6625a4764.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>再创建一个PyPI Group仓库，将刚刚创建的PyPI代理仓库和PyPI Hosted仓库添加到Group仓库中。</p>
<p>编辑<code>/etc/pip.conf</code>文件，添加以下内容：</p>
<blockquote>
<p>或者在用户目录下的<code>~/.pip/pip.conf</code>文件中添加</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[global]
</span></span><span class="line"><span class="cl">index = http://192.168.1.9:8081/repository/pypi/
</span></span><span class="line"><span class="cl">index-url = http://192.168.1.9:8081/repository/pypi/simple
</span></span><span class="line"><span class="cl">trusted-host =  192.168.1.9
</span></span></code></pre></div><h1 id="在-wsl2-中使用-nexus-代理">在 WSL2 中使用 Nexus 代理</h1>
<p>因为 WSL2 使用的是宿主机Windows的Docker desktop作为Docker引擎，所以我们需要在Windows中配置Docker的代理。打开Docker Desktop，点击Settings，然后选择Docker Engine，添加以下内容：</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/%2F2024%2F03%2F15%2Ffa63bec3941423faa870438fc14b06e5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F15%2Ffa63bec3941423faa870438fc14b06e5.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 class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;builder&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;gc&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;defaultKeepStorage&#34;</span><span class="p">:</span> <span class="s2">&#34;20GB&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;enabled&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;experimental&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;insecure-registries&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;http://192.168.1.9:8082&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;registry-mirrors&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;http://192.168.1.9:8082&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>重启Docker Desktop。然后在WSL2中使用 <code>docker info</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/%2F2024%2F03%2F15%2Fa4bf4946f10cce7d25cd5f25ce104fc5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F15%2Fa4bf4946f10cce7d25cd5f25ce104fc5.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>
<h1 id="常见错误">常见错误</h1>
<h2 id="docker-login-nexus-unauthorized-authentication-required">docker login nexus unauthorized authentication required</h2>
<ol>
<li>确认登录密码是否正确，密码为Nexus登录密码</li>
<li>却是否启用Realms</li>
</ol>
<h2 id="docker-login-nexus-connection-refused">docker login nexus connection refused</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker login 192.168.1.9:8082 -u admin -p admin123
</span></span></code></pre></div><ol>
<li>
<p>确认Docker compose配置文件中已经将端口8082暴露，如果新增需要重启Nexus</p>
</li>
<li>
<p>确认防火墙关闭或者已经打开端口</p>
<ol>
<li>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> sudo ufw allow <span class="m">8082</span>
</span></span><span class="line"><span class="cl"> sudo ufw allow 8082/tcp
</span></span></code></pre></div></li>
</ol>
</li>
<li>
<p>确认已经将私有仓库添加到了<code>/etc/docker/daemon.json</code>，并且及时重启了docker服务</p>
<ol>
<li>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> sudo systemctl restart docker.service
</span></span></code></pre></div></li>
</ol>
</li>
<li>
<p>确认已经开启了Http connector</p>
<ol>
<li>如图：


<!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//2023/12/16/922dcd44e678c2e22df359100fe32d53.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/12/16/922dcd44e678c2e22df359100fe32d53.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></li>
</ol>
</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>Tunasync 搭建私有镜像站</title>
      <link>https://lifeislife.cn/posts/tunasync%E6%90%AD%E5%BB%BA%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E7%AB%99/</link>
      <pubDate>Thu, 14 Dec 2023 14:15:54 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/tunasync%E6%90%AD%E5%BB%BA%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E7%AB%99/</guid>
      <description>&lt;h1 id=&#34;tunasync-项目简介&#34;&gt;Tunasync 项目简介&lt;/h1&gt;
&lt;p&gt;Tunasync 是一个开源的镜像站点镜像工具，可以帮助你快速搭建一个镜像站点，也可以帮助你快速的同步镜像站点的镜像。我们所熟知的清华大学镜像站就是使用 Tunasync 来同步镜像的。&lt;/p&gt;
&lt;h1 id=&#34;准备-workspace&#34;&gt;准备 workspace&lt;/h1&gt;
&lt;p&gt;创建目录用于存放 Tunasync 的程序、配置文件和数据库文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /home/username/tunasync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /home/username/tunasync/conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /home/username/tunasync/db
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建目录用于存放镜像文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mkdir /srv/mirrors
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;srv 目录需要 root 权限，将 mirrors 目录的所有者改为当前用户：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chown -R username:username /srv/mirrors
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;下载-tunasync&#34;&gt;下载 Tunasync&lt;/h1&gt;
&lt;p&gt;可以从 &lt;a href=&#34;https://github.com/tuna/tunasync&#34;&gt;Tunasync 项目&lt;/a&gt;的 Github releases 编译好的程序直接使用。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /home/username/tunasync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://github.com/tuna/tunasync/releases/download/v0.8.0/tunasync-linux-amd64-bin.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -zxvf tunasync-linux-amd64-bin.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;配置-tunasync&#34;&gt;配置 Tunasync&lt;/h1&gt;
&lt;h2 id=&#34;manager-配置&#34;&gt;Manager 配置&lt;/h2&gt;
&lt;p&gt;创建配置文件&lt;code&gt;/home/username/tunasync/conf/manager.conf&lt;/code&gt;，并添加以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;debug&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;server&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;port&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;12345&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ssl_cert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ssl_key&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;files&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;db_type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;bolt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;db_file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/username/tunasync/db/manager.db&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ca_cert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;worker-配置&#34;&gt;Worker 配置&lt;/h2&gt;
&lt;p&gt;创建配置文件&lt;code&gt;/home/username/tunasync/conf/worker-openeuler.conf&lt;/code&gt;，并添加以下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;global&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;openeuler_worker&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;log_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/srv/mirrors/log/tunasync/{{.Name}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;mirror_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/srv/mirrors&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;concurrent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;interval&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1440&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;manager&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;api_base&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://localhost:12345&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;some_token&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ca_cert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;cgroup&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;base_path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/sys/fs/cgroup&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;group&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tunasync&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;server&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;hostname&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;listen_addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;listen_port&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;16010&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ssl_cert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ssl_key&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt;mirrors&lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;centos&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;provider&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rsync&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;upstream&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rsync://mirrors.tuna.tsinghua.edu.cn/openeuler/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;use_ipv6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;启动-tunasync&#34;&gt;启动 Tunasync&lt;/h1&gt;
&lt;h2 id=&#34;启动-manager&#34;&gt;启动 Manager&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /home/username/tunasync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./tunasync manager -c conf/manager.conf &amp;gt;&amp;gt; /srv/mirrors/log/plog/manager.log &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;启动-worker&#34;&gt;启动 Worker&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /home/username/tunasync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./tunasync worker -c conf/worker-openeuler.conf &amp;gt;&amp;gt; /srv/mirrors/log/plog/worker-openeuler.log &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通常可能同步不止一个镜像站点，可以创建多个 Worker 配置文件，然后启动多个 Worker。&lt;/p&gt;
&lt;h1 id=&#34;创建-web-服务&#34;&gt;创建 web 服务&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安装 Apache2：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install apache2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修改配置文件：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ubuntu 中的 Apache2 主要配置文件是 &lt;code&gt;/etc/apache2/apache2.conf&lt;/code&gt;。可以在此文件中进行全局配置，也可以使用专门的配置文件，例如 &lt;code&gt;/etc/apache2/sites-available/your-site.conf&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo nano /etc/apache2/apache2.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;apache2.conf&lt;/code&gt; 文件中，添加以下行，设置 &lt;code&gt;DocumentRoot&lt;/code&gt; 和目录访问权限：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-apache&#34; data-lang=&#34;apache&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;DocumentRoot&lt;/span&gt; &lt;span class=&#34;sx&#34;&gt;/mirrors&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/mirrors&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;Options&lt;/span&gt; Indexes FollowSymLinks
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;AllowOverride&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;Require&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;all&lt;/span&gt; granted
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请确保将 &lt;code&gt;&amp;lt;Directory&amp;gt;&lt;/code&gt; 部分添加到正确的位置。可以在文件中找到 &lt;code&gt;&amp;lt;Directory /var/www/&amp;gt;&lt;/code&gt; 部分，然后在该部分下添加配置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;重新启动 Apache2 服务：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在进行配置更改后，需要重新启动 Apache2 服务以使更改生效：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl restart apache2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;补充文件：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;将 &lt;code&gt;index.html&lt;/code&gt; 文件和其他需要的文件添加到 &lt;code&gt;/srv/mirrors&lt;/code&gt; 目录中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;测试：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;打开 Web 浏览器，访问 &lt;code&gt;http://your-server-ip&lt;/code&gt; 或 &lt;code&gt;http://localhost&lt;/code&gt;，应该能够看到 &lt;code&gt;/srv/mirrors&lt;/code&gt; 目录中的文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如打不开，需要开启防火墙&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ufw allow http
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ufw allow https
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修改 Web 服务端口&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编辑 Apache2 配置文件：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo nano /etc/apache2/ports.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**在文件中找到 &lt;strong&gt;​&lt;/strong&gt;&lt;code&gt;Listen&lt;/code&gt;&lt;strong&gt;​ ** 行，修改端口：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-apache&#34; data-lang=&#34;apache&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Listen&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2081&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编辑虚拟主机配置（如果有）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果你有虚拟主机配置文件（通常在 &lt;code&gt;/etc/apache2/sites-available/&lt;/code&gt; 中），确保其中的 &lt;code&gt;&amp;lt;VirtualHost&amp;gt;&lt;/code&gt; 部分中的端口也被修改为 2081。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;保存并退出配置文件。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;重启 Apache 服务：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl restart apache2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;打开防火墙端口&#34;&gt;打开防火墙端口&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1. **打开 2081 端口：**

    ```bash
    sudo ufw allow 2081
    ```

2. **检查配置：**

    ```bash
    sudo ufw status
    ```

    确保 2081 端口已经正确添加。
3. **重启防火墙（可选）：**

    ```bash
    sudo ufw reload
    ```

    或者

    ```bash
    sudo systemctl restart ufw
    ```

这样，你就将 Apache2 Web 服务的端口修改为 2081，并且只开放了 2081 端口。确保修改了防火墙规则后，仍能够通过新的端口访问你的网站。
&lt;/code&gt;&lt;/pre&gt;
</description>
      <content:encoded><![CDATA[<h1 id="tunasync-项目简介">Tunasync 项目简介</h1>
<p>Tunasync 是一个开源的镜像站点镜像工具，可以帮助你快速搭建一个镜像站点，也可以帮助你快速的同步镜像站点的镜像。我们所熟知的清华大学镜像站就是使用 Tunasync 来同步镜像的。</p>
<h1 id="准备-workspace">准备 workspace</h1>
<p>创建目录用于存放 Tunasync 的程序、配置文件和数据库文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir /home/username/tunasync
</span></span><span class="line"><span class="cl">mkdir /home/username/tunasync/conf
</span></span><span class="line"><span class="cl">mkdir /home/username/tunasync/db
</span></span></code></pre></div><p>创建目录用于存放镜像文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo mkdir /srv/mirrors
</span></span></code></pre></div><p>srv 目录需要 root 权限，将 mirrors 目录的所有者改为当前用户：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo chown -R username:username /srv/mirrors
</span></span></code></pre></div><h1 id="下载-tunasync">下载 Tunasync</h1>
<p>可以从 <a href="https://github.com/tuna/tunasync">Tunasync 项目</a>的 Github releases 编译好的程序直接使用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /home/username/tunasync
</span></span><span class="line"><span class="cl">wget https://github.com/tuna/tunasync/releases/download/v0.8.0/tunasync-linux-amd64-bin.tar.gz
</span></span><span class="line"><span class="cl">tar -zxvf tunasync-linux-amd64-bin.tar.gz
</span></span></code></pre></div><h1 id="配置-tunasync">配置 Tunasync</h1>
<h2 id="manager-配置">Manager 配置</h2>
<p>创建配置文件<code>/home/username/tunasync/conf/manager.conf</code>，并添加以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">debug</span> <span class="o">=</span> <span class="nb">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>server<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">addr</span> <span class="o">=</span> <span class="s2">&#34;127.0.0.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">port</span> <span class="o">=</span> <span class="m">12345</span>
</span></span><span class="line"><span class="cl"><span class="nv">ssl_cert</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">ssl_key</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>files<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">db_type</span> <span class="o">=</span> <span class="s2">&#34;bolt&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">db_file</span> <span class="o">=</span> <span class="s2">&#34;/home/username/tunasync/db/manager.db&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">ca_cert</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span></code></pre></div><h2 id="worker-配置">Worker 配置</h2>
<p>创建配置文件<code>/home/username/tunasync/conf/worker-openeuler.conf</code>，并添加以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>global<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">name</span> <span class="o">=</span> <span class="s2">&#34;openeuler_worker&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">log_dir</span> <span class="o">=</span> <span class="s2">&#34;/srv/mirrors/log/tunasync/{{.Name}}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">mirror_dir</span> <span class="o">=</span> <span class="s2">&#34;/srv/mirrors&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">concurrent</span> <span class="o">=</span> <span class="m">10</span>
</span></span><span class="line"><span class="cl"><span class="nv">interval</span> <span class="o">=</span> <span class="m">1440</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>manager<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">api_base</span> <span class="o">=</span> <span class="s2">&#34;http://localhost:12345&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">token</span> <span class="o">=</span> <span class="s2">&#34;some_token&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">ca_cert</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>cgroup<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nb">enable</span> <span class="o">=</span> <span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="nv">base_path</span> <span class="o">=</span> <span class="s2">&#34;/sys/fs/cgroup&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">group</span> <span class="o">=</span> <span class="s2">&#34;tunasync&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>server<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">hostname</span> <span class="o">=</span> <span class="s2">&#34;localhost&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">listen_addr</span> <span class="o">=</span> <span class="s2">&#34;127.0.0.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">listen_port</span> <span class="o">=</span> <span class="m">16010</span>
</span></span><span class="line"><span class="cl"><span class="nv">ssl_cert</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">ssl_key</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[[</span>mirrors<span class="o">]]</span>
</span></span><span class="line"><span class="cl"><span class="nv">name</span> <span class="o">=</span> <span class="s2">&#34;centos&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">provider</span> <span class="o">=</span> <span class="s2">&#34;rsync&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">upstream</span> <span class="o">=</span> <span class="s2">&#34;rsync://mirrors.tuna.tsinghua.edu.cn/openeuler/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">use_ipv6</span> <span class="o">=</span> <span class="nb">false</span>
</span></span></code></pre></div><h1 id="启动-tunasync">启动 Tunasync</h1>
<h2 id="启动-manager">启动 Manager</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /home/username/tunasync
</span></span><span class="line"><span class="cl">./tunasync manager -c conf/manager.conf &gt;&gt; /srv/mirrors/log/plog/manager.log <span class="p">&amp;</span>
</span></span></code></pre></div><h2 id="启动-worker">启动 Worker</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /home/username/tunasync
</span></span><span class="line"><span class="cl">./tunasync worker -c conf/worker-openeuler.conf &gt;&gt; /srv/mirrors/log/plog/worker-openeuler.log <span class="p">&amp;</span>
</span></span></code></pre></div><p>通常可能同步不止一个镜像站点，可以创建多个 Worker 配置文件，然后启动多个 Worker。</p>
<h1 id="创建-web-服务">创建 web 服务</h1>
<ol>
<li>
<p><strong>安装 Apache2：</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install apache2
</span></span></code></pre></div></li>
<li>
<p><strong>修改配置文件：</strong></p>
<p>Ubuntu 中的 Apache2 主要配置文件是 <code>/etc/apache2/apache2.conf</code>。可以在此文件中进行全局配置，也可以使用专门的配置文件，例如 <code>/etc/apache2/sites-available/your-site.conf</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /etc/apache2/apache2.conf
</span></span></code></pre></div><p>在 <code>apache2.conf</code> 文件中，添加以下行，设置 <code>DocumentRoot</code> 和目录访问权限：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-apache" data-lang="apache"><span class="line"><span class="cl"><span class="nb">DocumentRoot</span> <span class="sx">/mirrors</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;Directory</span> <span class="s">&#34;/mirrors&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Options</span> Indexes FollowSymLinks
</span></span><span class="line"><span class="cl">    <span class="nb">AllowOverride</span> <span class="k">None</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Require</span> <span class="k">all</span> granted
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/Directory&gt;</span>
</span></span></code></pre></div><p>请确保将 <code>&lt;Directory&gt;</code> 部分添加到正确的位置。可以在文件中找到 <code>&lt;Directory /var/www/&gt;</code> 部分，然后在该部分下添加配置。</p>
</li>
<li>
<p><strong>重新启动 Apache2 服务：</strong></p>
<p>在进行配置更改后，需要重新启动 Apache2 服务以使更改生效：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl restart apache2
</span></span></code></pre></div></li>
<li>
<p><strong>补充文件：</strong></p>
<p>将 <code>index.html</code> 文件和其他需要的文件添加到 <code>/srv/mirrors</code> 目录中。</p>
</li>
<li>
<p><strong>测试：</strong></p>
<p>打开 Web 浏览器，访问 <code>http://your-server-ip</code> 或 <code>http://localhost</code>，应该能够看到 <code>/srv/mirrors</code> 目录中的文件。</p>
</li>
<li>
<p>如打不开，需要开启防火墙</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw allow http
</span></span><span class="line"><span class="cl">sudo ufw allow https
</span></span></code></pre></div></li>
<li>
<p>修改 Web 服务端口</p>
<ol>
<li>
<p><strong>编辑 Apache2 配置文件：</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /etc/apache2/ports.conf
</span></span></code></pre></div></li>
<li>
<p>**在文件中找到 <strong>​</strong><code>Listen</code><strong>​ ** 行，修改端口：</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-apache" data-lang="apache"><span class="line"><span class="cl"><span class="nb">Listen</span> <span class="m">2081</span>
</span></span></code></pre></div></li>
<li>
<p><strong>编辑虚拟主机配置（如果有）：</strong></p>
<p>如果你有虚拟主机配置文件（通常在 <code>/etc/apache2/sites-available/</code> 中），确保其中的 <code>&lt;VirtualHost&gt;</code> 部分中的端口也被修改为 2081。</p>
</li>
<li>
<p><strong>保存并退出配置文件。</strong></p>
</li>
<li>
<p><strong>重启 Apache 服务：</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl restart apache2
</span></span></code></pre></div></li>
</ol>
</li>
</ol>
<h3 id="打开防火墙端口">打开防火墙端口</h3>
<pre><code>1. **打开 2081 端口：**

    ```bash
    sudo ufw allow 2081
    ```

2. **检查配置：**

    ```bash
    sudo ufw status
    ```

    确保 2081 端口已经正确添加。
3. **重启防火墙（可选）：**

    ```bash
    sudo ufw reload
    ```

    或者

    ```bash
    sudo systemctl restart ufw
    ```

这样，你就将 Apache2 Web 服务的端口修改为 2081，并且只开放了 2081 端口。确保修改了防火墙规则后，仍能够通过新的端口访问你的网站。
</code></pre>
]]></content:encoded>
    </item>
    <item>
      <title>VSCode Linux内核源码阅读环境</title>
      <link>https://lifeislife.cn/posts/vscode-linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E7%8E%AF%E5%A2%83/</link>
      <pubDate>Thu, 14 Dec 2023 10:02:32 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/vscode-linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E7%8E%AF%E5%A2%83/</guid>
      <description>&lt;h2 id=&#34;安装clangd插件&#34;&gt;安装Clangd插件&lt;/h2&gt;
&lt;p&gt;在VSCode中，你可以通过以下步骤安装Clangd插件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;打开VSCode；&lt;/li&gt;
&lt;li&gt;点击左侧的插件图标（Ctrl+Shift+X）；&lt;/li&gt;
&lt;li&gt;搜索“Clangd”插件；&lt;/li&gt;
&lt;li&gt;点击“安装”按钮。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;生成compile_commandsjson&#34;&gt;生成compile_commands.json&lt;/h2&gt;
&lt;p&gt;对于make项目来说，常规来讲，可以使用Bear来对源码生成compile_commands.json。首先安装Bear：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install bear
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在项目根目录下执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bear make -j32
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编译完成后，会在项目根目录下生成compile_commands.json文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于CMAKE项目来说，可以在CMakeLists.txt中添加以下语句，然后重新编译项目即可生成compile_commands.json文件：
set(CMAKE_EXPORT_COMPILE_COMMANDS True)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;配置clangd插件&#34;&gt;配置Clangd插件&lt;/h2&gt;
&lt;p&gt;进入到项目目录下，下载配置文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone --depth &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; https://github.com/Dunky-Z/dot-vscode.git .vscode
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;主要修改&lt;code&gt;--compile-commands-dir&lt;/code&gt;参数，将其修改为自己的路径。&lt;/p&gt;
&lt;p&gt;随便打开内核源码文件，clangd将会自动生成索引，并将索引文件.idx保存在项目根目录下的.cache目录中。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Linux-5.4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── .cache
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── clangd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── .config
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── .git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果生成了索引文件，那么说明配置成功，可以打开源码文件看看是否能够正常跳转。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="安装clangd插件">安装Clangd插件</h2>
<p>在VSCode中，你可以通过以下步骤安装Clangd插件：</p>
<ul>
<li>打开VSCode；</li>
<li>点击左侧的插件图标（Ctrl+Shift+X）；</li>
<li>搜索“Clangd”插件；</li>
<li>点击“安装”按钮。</li>
</ul>
<h2 id="生成compile_commandsjson">生成compile_commands.json</h2>
<p>对于make项目来说，常规来讲，可以使用Bear来对源码生成compile_commands.json。首先安装Bear：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install bear
</span></span></code></pre></div><p>然后在项目根目录下执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bear make -j32
</span></span></code></pre></div><p>编译完成后，会在项目根目录下生成compile_commands.json文件。</p>
<blockquote>
<p>对于CMAKE项目来说，可以在CMakeLists.txt中添加以下语句，然后重新编译项目即可生成compile_commands.json文件：
set(CMAKE_EXPORT_COMPILE_COMMANDS True)</p>
</blockquote>
<h2 id="配置clangd插件">配置Clangd插件</h2>
<p>进入到项目目录下，下载配置文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone --depth <span class="m">1</span> https://github.com/Dunky-Z/dot-vscode.git .vscode
</span></span></code></pre></div><p>主要修改<code>--compile-commands-dir</code>参数，将其修改为自己的路径。</p>
<p>随便打开内核源码文件，clangd将会自动生成索引，并将索引文件.idx保存在项目根目录下的.cache目录中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Linux-5.4
</span></span><span class="line"><span class="cl">├── .cache
</span></span><span class="line"><span class="cl">│   └── clangd
</span></span><span class="line"><span class="cl">├── .config
</span></span><span class="line"><span class="cl">├── .git
</span></span></code></pre></div><p>如果生成了索引文件，那么说明配置成功，可以打开源码文件看看是否能够正常跳转。</p>
]]></content:encoded>
    </item>
    <item>
      <title>嵌入式Linux驱动开发环境搭建踩坑</title>
      <link>https://lifeislife.cn/posts/%E5%B5%8C%E5%85%A5%E5%BC%8Flinux%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E8%B8%A9%E5%9D%91/</link>
      <pubDate>Wed, 13 Dec 2023 22:59:23 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%B5%8C%E5%85%A5%E5%BC%8Flinux%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E8%B8%A9%E5%9D%91/</guid>
      <description>&lt;p&gt;通过&lt;a href=&#34;https://pan.baidu.com/s/1Gnh9G8a05LSgHYlohyl93Q?pwd=root#list/path=%2F&#34;&gt;文档资料百度网盘&lt;/a&gt;下载配套资料，学习手册目录中有嵌入式Linux应用开发完全手册V5.1_STM32MP157_Pro开发板.pdf，里面有详细的开发环境搭建步骤，但是在搭建过程中还是遇到了一些问题，记录如下：&lt;/p&gt;
&lt;h2 id=&#34;modulenotfounderror-no-module-named-requests&#34;&gt;ModuleNotFoundError: No module named &amp;lsquo;requests&amp;rsquo;&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;../repo/repo init -u https://gitee.com/weidongshan/manifests.git -b linux-sdk -m stm32mp1/100ask_stm32mp157_pro_release-v2.0.xml --no-repo-verify
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Traceback &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;most recent call last&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  File &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/nic/develop/repo/main.py&amp;#34;&lt;/span&gt;, line 56, in &amp;lt;module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    from subcmds.version import Version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  File &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/nic/develop/repo/subcmds/__init__.py&amp;#34;&lt;/span&gt;, line 35, in &amp;lt;module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;mod&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; __import__&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;__name__,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  File &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/nic/develop/repo/subcmds/selfupdate.py&amp;#34;&lt;/span&gt;, line 22, in &amp;lt;module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    from subcmds.sync import _PostRepoUpgrade
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  File &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/nic/develop/repo/subcmds/sync.py&amp;#34;&lt;/span&gt;, line 74, in &amp;lt;module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    from project import Project
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  File &lt;span class=&#34;s2&#34;&gt;&amp;#34;/home/nic/develop/repo/project.py&amp;#34;&lt;/span&gt;, line 33, in &amp;lt;module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    import requests
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ModuleNotFoundError: No module named &lt;span class=&#34;s1&#34;&gt;&amp;#39;requests&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;解决方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python -m pip install requests
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;modulenotfounderror-no-module-named-formatter&#34;&gt;ModuleNotFoundError: No module named &amp;lsquo;formatter&amp;rsquo;&lt;/h2&gt;
&lt;p&gt;formatter已经在python3.4标记成废弃接口，在python3.10已经正式删除，并且其依赖的cStringIO也已经删除。所以不能简单的安装formatter模块，可以通过两种方式解决：&lt;/p&gt;
&lt;h3 id=&#34;方法一降低python版本到39以下&#34;&gt;方法一：降低python版本到3.9以下&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;添加 ppa 源，此源可安装多个 python 版本&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo add-apt-repository ppa:deadsnakes/ppa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;安装 python3.9&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install python3.9
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;建立 python 的组,并添加 Python3.9 的可选项，优先级为 1&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.9 &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;验证当前 python 是否是 3.9 版本&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;如果不是，用以下命令手动配置一下&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo update-alternatives --config python
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;方法二修改repo源码&#34;&gt;方法二：修改repo源码&lt;/h3&gt;
&lt;p&gt;将&lt;code&gt;repo/repo/subcmds/help.py&lt;/code&gt;文件替换为如下内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;也可以参考&lt;a href=&#34;https://gerrit-review.googlesource.com/c/git-repo/+/303282&#34;&gt;Google修复后的代码&lt;/a&gt;，根据diff修改文件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -*- coding:utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Copyright (C) 2008 The Android Open Source Project&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# you may not use this file except in compliance with the License.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# You may obtain a copy of the License at&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#      http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Unless required by applicable law or agreed to in writing, software&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# See the License for the specific language governing permissions and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# limitations under the License.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;__future__&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;print_function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;re&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;textwrap&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;subcmds&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;all_commands&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Coloring&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;command&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PagedCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MirrorSafeCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GitcAvailableCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GitcClientCommand&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;gitc_utils&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Help&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PagedCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MirrorSafeCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;common&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;helpSummary&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Display detailed help on a command&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;helpUsage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;%prog [--all|command]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;helpDescription&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;Displays detailed usage information about a command.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_PrintCommands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;commandNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Helper to display |commandNames| summaries.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;maxlen&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;commandNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;maxlen&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;maxlen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fmt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;  &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%d&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;s  &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;s&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;maxlen&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;commandNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;command&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;all_commands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;summary&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;helpSummary&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;summary&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fmt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;summary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_PrintAllCommands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;usage: repo COMMAND [ARGS]&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;The complete list of recognized repo commands are:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;commandNames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;sorted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;all_commands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintCommands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;commandNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;See &amp;#39;repo help &amp;lt;command&amp;gt;&amp;#39; for more information on a &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;s1&#34;&gt;&amp;#39;specific command.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_PrintCommonCommands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;usage: repo COMMAND [ARGS]&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;The most commonly used repo commands are:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;gitc_supported&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;isinstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GitcAvailableCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;isinstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GitcClientCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;manifest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isGitcClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;isinstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GitcClientCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gitc_utils&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_gitc_manifest_dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;commandNames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;sorted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;command&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;all_commands&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;common&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gitc_supported&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintCommands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;commandNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;See &amp;#39;repo help &amp;lt;command&amp;gt;&amp;#39; for more information on a specific command.&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;See &amp;#39;repo help --all&amp;#39; for a complete list of recognized commands.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_PrintCommandHelp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;header_prefix&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;_Out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Coloring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Coloring&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;help&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heading&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;printer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;heading&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;bold&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_PrintSection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;heading&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bodyAttr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bodyAttr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heading&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;header_prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;heading&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;me&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;repo &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NAME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%prog&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;me&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Extract the title, but skip any trailing {#anchors}.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;asciidoc_hdr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;compile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;^\n?#+ ([^{]+)(\{#.+\})?$&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;para&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;para&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startswith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;para&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;asciidoc_hdr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;para&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heading&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;header_prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;textwrap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wrap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;para&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;  &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;n&#34;&gt;break_long_words&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;break_on_hyphens&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_Out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;manifest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;globalConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintSection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Summary&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;helpSummary&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OptionParser&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;print_help&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintSection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Description&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;helpDescription&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_PrintAllCommandHelp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sorted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;all_commands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;all_commands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;manifest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;manifest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintCommandHelp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;header_prefix&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;] &amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_Options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add_option&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;-a&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;--all&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;n&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;show_all&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;store_true&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;n&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;show the complete list of commands&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add_option&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;--help-all&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;n&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;show_all_help&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;store_true&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;n&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;show the --help of all commands&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;show_all_help&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintAllCommandHelp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;show_all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintAllCommands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintCommonCommands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;all_commands&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;KeyError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;repo: &amp;#39;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39; is not a repo command.&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;manifest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;manifest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintCommandHelp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_PrintCommandHelp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>通过<a href="https://pan.baidu.com/s/1Gnh9G8a05LSgHYlohyl93Q?pwd=root#list/path=%2F">文档资料百度网盘</a>下载配套资料，学习手册目录中有嵌入式Linux应用开发完全手册V5.1_STM32MP157_Pro开发板.pdf，里面有详细的开发环境搭建步骤，但是在搭建过程中还是遇到了一些问题，记录如下：</p>
<h2 id="modulenotfounderror-no-module-named-requests">ModuleNotFoundError: No module named &lsquo;requests&rsquo;</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">../repo/repo init -u https://gitee.com/weidongshan/manifests.git -b linux-sdk -m stm32mp1/100ask_stm32mp157_pro_release-v2.0.xml --no-repo-verify
</span></span><span class="line"><span class="cl">Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
</span></span><span class="line"><span class="cl">  File <span class="s2">&#34;/home/nic/develop/repo/main.py&#34;</span>, line 56, in &lt;module&gt;
</span></span><span class="line"><span class="cl">    from subcmds.version import Version
</span></span><span class="line"><span class="cl">  File <span class="s2">&#34;/home/nic/develop/repo/subcmds/__init__.py&#34;</span>, line 35, in &lt;module&gt;
</span></span><span class="line"><span class="cl">    <span class="nv">mod</span> <span class="o">=</span> __import__<span class="o">(</span>__name__,
</span></span><span class="line"><span class="cl">  File <span class="s2">&#34;/home/nic/develop/repo/subcmds/selfupdate.py&#34;</span>, line 22, in &lt;module&gt;
</span></span><span class="line"><span class="cl">    from subcmds.sync import _PostRepoUpgrade
</span></span><span class="line"><span class="cl">  File <span class="s2">&#34;/home/nic/develop/repo/subcmds/sync.py&#34;</span>, line 74, in &lt;module&gt;
</span></span><span class="line"><span class="cl">    from project import Project
</span></span><span class="line"><span class="cl">  File <span class="s2">&#34;/home/nic/develop/repo/project.py&#34;</span>, line 33, in &lt;module&gt;
</span></span><span class="line"><span class="cl">    import requests
</span></span><span class="line"><span class="cl">ModuleNotFoundError: No module named <span class="s1">&#39;requests&#39;</span>
</span></span></code></pre></div><p>解决方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python -m pip install requests
</span></span></code></pre></div><h2 id="modulenotfounderror-no-module-named-formatter">ModuleNotFoundError: No module named &lsquo;formatter&rsquo;</h2>
<p>formatter已经在python3.4标记成废弃接口，在python3.10已经正式删除，并且其依赖的cStringIO也已经删除。所以不能简单的安装formatter模块，可以通过两种方式解决：</p>
<h3 id="方法一降低python版本到39以下">方法一：降低python版本到3.9以下</h3>
<ol>
<li>添加 ppa 源，此源可安装多个 python 版本</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo add-apt-repository ppa:deadsnakes/ppa
</span></span></code></pre></div><ol start="2">
<li>安装 python3.9</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install python3.9
</span></span></code></pre></div><ol start="4">
<li>建立 python 的组,并添加 Python3.9 的可选项，优先级为 1</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.9 <span class="m">1</span>
</span></span></code></pre></div><ol start="5">
<li>验证当前 python 是否是 3.9 版本</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python --version
</span></span></code></pre></div><ol start="6">
<li>如果不是，用以下命令手动配置一下</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo update-alternatives --config python
</span></span></code></pre></div><h3 id="方法二修改repo源码">方法二：修改repo源码</h3>
<p>将<code>repo/repo/subcmds/help.py</code>文件替换为如下内容：</p>
<blockquote>
<p>也可以参考<a href="https://gerrit-review.googlesource.com/c/git-repo/+/303282">Google修复后的代码</a>，根据diff修改文件。</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># -*- coding:utf-8 -*-</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Copyright (C) 2008 The Android Open Source Project</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Licensed under the Apache License, Version 2.0 (the &#34;License&#34;);</span>
</span></span><span class="line"><span class="cl"><span class="c1"># you may not use this file except in compliance with the License.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># You may obtain a copy of the License at</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1">#      http://www.apache.org/licenses/LICENSE-2.0</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Unless required by applicable law or agreed to in writing, software</span>
</span></span><span class="line"><span class="cl"><span class="c1"># distributed under the License is distributed on an &#34;AS IS&#34; BASIS,</span>
</span></span><span class="line"><span class="cl"><span class="c1"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># See the License for the specific language governing permissions and</span>
</span></span><span class="line"><span class="cl"><span class="c1"># limitations under the License.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">print_function</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">textwrap</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">subcmds</span> <span class="kn">import</span> <span class="n">all_commands</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">color</span> <span class="kn">import</span> <span class="n">Coloring</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">command</span> <span class="kn">import</span> <span class="n">PagedCommand</span><span class="p">,</span> <span class="n">MirrorSafeCommand</span><span class="p">,</span> <span class="n">GitcAvailableCommand</span><span class="p">,</span> <span class="n">GitcClientCommand</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">gitc_utils</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Help</span><span class="p">(</span><span class="n">PagedCommand</span><span class="p">,</span> <span class="n">MirrorSafeCommand</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">common</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">  <span class="n">helpSummary</span> <span class="o">=</span> <span class="s2">&#34;Display detailed help on a command&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">helpUsage</span> <span class="o">=</span> <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">%prog [--all|command]
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">helpDescription</span> <span class="o">=</span> <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">Displays detailed usage information about a command.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">_PrintCommands</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">commandNames</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Helper to display |commandNames| summaries.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">maxlen</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">commandNames</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">maxlen</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">maxlen</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">name</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">fmt</span> <span class="o">=</span> <span class="s1">&#39;  </span><span class="si">%%</span><span class="s1">-</span><span class="si">%d</span><span class="s1">s  </span><span class="si">%%</span><span class="s1">s&#39;</span> <span class="o">%</span> <span class="n">maxlen</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">commandNames</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">command</span> <span class="o">=</span> <span class="n">all_commands</span><span class="p">[</span><span class="n">name</span><span class="p">]()</span>
</span></span><span class="line"><span class="cl">      <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">summary</span> <span class="o">=</span> <span class="n">command</span><span class="o">.</span><span class="n">helpSummary</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">summary</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="nb">print</span><span class="p">(</span><span class="n">fmt</span> <span class="o">%</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">summary</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">_PrintAllCommands</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;usage: repo COMMAND [ARGS]&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;The complete list of recognized repo commands are:&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">commandNames</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="n">all_commands</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">_PrintCommands</span><span class="p">(</span><span class="n">commandNames</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;See &#39;repo help &lt;command&gt;&#39; for more information on a &#34;</span>
</span></span><span class="line"><span class="cl">          <span class="s1">&#39;specific command.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">_PrintCommonCommands</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;usage: repo COMMAND [ARGS]&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;The most commonly used repo commands are:&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">gitc_supported</span><span class="p">(</span><span class="n">cmd</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">GitcAvailableCommand</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">GitcClientCommand</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">manifest</span><span class="o">.</span><span class="n">isGitcClient</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">GitcClientCommand</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">gitc_utils</span><span class="o">.</span><span class="n">get_gitc_manifest_dir</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">commandNames</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">sorted</span><span class="p">([</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">                                <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">command</span> <span class="ow">in</span> <span class="n">all_commands</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                                <span class="k">if</span> <span class="n">command</span><span class="o">.</span><span class="n">common</span> <span class="ow">and</span> <span class="n">gitc_supported</span><span class="p">(</span><span class="n">command</span><span class="p">)]))</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">_PrintCommands</span><span class="p">(</span><span class="n">commandNames</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;See &#39;repo help &lt;command&gt;&#39; for more information on a specific command.</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;See &#39;repo help --all&#39; for a complete list of recognized commands.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">_PrintCommandHelp</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cmd</span><span class="p">,</span> <span class="n">header_prefix</span><span class="o">=</span><span class="s1">&#39;&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">_Out</span><span class="p">(</span><span class="n">Coloring</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">      <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">gc</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">Coloring</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">gc</span><span class="p">,</span> <span class="s1">&#39;help&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">heading</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">printer</span><span class="p">(</span><span class="s1">&#39;heading&#39;</span><span class="p">,</span> <span class="n">attr</span><span class="o">=</span><span class="s1">&#39;bold&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_first</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="k">def</span> <span class="nf">_PrintSection</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">heading</span><span class="p">,</span> <span class="n">bodyAttr</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">          <span class="n">body</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">bodyAttr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">body</span> <span class="o">==</span> <span class="s1">&#39;&#39;</span> <span class="ow">or</span> <span class="n">body</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_first</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">          <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_first</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">heading</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s%s</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">header_prefix</span><span class="p">,</span> <span class="n">heading</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">me</span> <span class="o">=</span> <span class="s1">&#39;repo </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">cmd</span><span class="o">.</span><span class="n">NAME</span>
</span></span><span class="line"><span class="cl">        <span class="n">body</span> <span class="o">=</span> <span class="n">body</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">body</span> <span class="o">=</span> <span class="n">body</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39;%prog&#39;</span><span class="p">,</span> <span class="n">me</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Extract the title, but skip any trailing {#anchors}.</span>
</span></span><span class="line"><span class="cl">        <span class="n">asciidoc_hdr</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^\n?#+ ([^{]+)(\{#.+\})?$&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">para</span> <span class="ow">in</span> <span class="n">body</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n\n</span><span class="s2">&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="n">para</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">para</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">m</span> <span class="o">=</span> <span class="n">asciidoc_hdr</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">para</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="n">m</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">heading</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s%s</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">header_prefix</span><span class="p">,</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">lines</span> <span class="o">=</span> <span class="n">textwrap</span><span class="o">.</span><span class="n">wrap</span><span class="p">(</span><span class="n">para</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39;  &#39;</span><span class="p">,</span> <span class="s1">&#39; &#39;</span><span class="p">),</span> <span class="n">width</span><span class="o">=</span><span class="mi">80</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="n">break_long_words</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">break_on_hyphens</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">          <span class="bp">self</span><span class="o">.</span><span class="n">nl</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">out</span> <span class="o">=</span> <span class="n">_Out</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">manifest</span><span class="o">.</span><span class="n">globalConfig</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">out</span><span class="o">.</span><span class="n">_PrintSection</span><span class="p">(</span><span class="s1">&#39;Summary&#39;</span><span class="p">,</span> <span class="s1">&#39;helpSummary&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmd</span><span class="o">.</span><span class="n">OptionParser</span><span class="o">.</span><span class="n">print_help</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">out</span><span class="o">.</span><span class="n">_PrintSection</span><span class="p">(</span><span class="s1">&#39;Description&#39;</span><span class="p">,</span> <span class="s1">&#39;helpDescription&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">_PrintAllCommandHelp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">all_commands</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">      <span class="n">cmd</span> <span class="o">=</span> <span class="n">all_commands</span><span class="p">[</span><span class="n">name</span><span class="p">]()</span>
</span></span><span class="line"><span class="cl">      <span class="n">cmd</span><span class="o">.</span><span class="n">manifest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">manifest</span>
</span></span><span class="line"><span class="cl">      <span class="bp">self</span><span class="o">.</span><span class="n">_PrintCommandHelp</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">header_prefix</span><span class="o">=</span><span class="s1">&#39;[</span><span class="si">%s</span><span class="s1">] &#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">name</span><span class="p">,))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">_Options</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">p</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">.</span><span class="n">add_option</span><span class="p">(</span><span class="s1">&#39;-a&#39;</span><span class="p">,</span> <span class="s1">&#39;--all&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="n">dest</span><span class="o">=</span><span class="s1">&#39;show_all&#39;</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s1">&#39;store_true&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="n">help</span><span class="o">=</span><span class="s1">&#39;show the complete list of commands&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span><span class="o">.</span><span class="n">add_option</span><span class="p">(</span><span class="s1">&#39;--help-all&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="n">dest</span><span class="o">=</span><span class="s1">&#39;show_all_help&#39;</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s1">&#39;store_true&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                 <span class="n">help</span><span class="o">=</span><span class="s1">&#39;show the --help of all commands&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">Execute</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">opt</span><span class="p">,</span> <span class="n">args</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">opt</span><span class="o">.</span><span class="n">show_all_help</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_PrintAllCommandHelp</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="k">elif</span> <span class="n">opt</span><span class="o">.</span><span class="n">show_all</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_PrintAllCommands</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_PrintCommonCommands</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="n">name</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">cmd</span> <span class="o">=</span> <span class="n">all_commands</span><span class="p">[</span><span class="n">name</span><span class="p">]()</span>
</span></span><span class="line"><span class="cl">      <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;repo: &#39;</span><span class="si">%s</span><span class="s2">&#39; is not a repo command.&#34;</span> <span class="o">%</span> <span class="n">name</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">cmd</span><span class="o">.</span><span class="n">manifest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">manifest</span>
</span></span><span class="line"><span class="cl">      <span class="bp">self</span><span class="o">.</span><span class="n">_PrintCommandHelp</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">      <span class="bp">self</span><span class="o">.</span><span class="n">_PrintCommandHelp</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>解决Ubuntu更新源报错Clearsigned file isnt valid, got NOSPLIT</title>
      <link>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3ubuntu%E6%9B%B4%E6%96%B0%E6%BA%90%E6%8A%A5%E9%94%99clearsigned-file-isn-t-valid-got-nosplit/</link>
      <pubDate>Sun, 19 Nov 2023 21:18:09 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3ubuntu%E6%9B%B4%E6%96%B0%E6%BA%90%E6%8A%A5%E9%94%99clearsigned-file-isn-t-valid-got-nosplit/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在更新源时报错 Clearsigned file isn&amp;rsquo;t valid, got &amp;lsquo;NOSPLIT&amp;rsquo; (does the network require authentication?)。&lt;/p&gt;
&lt;p&gt;换中科大的源解决，其余源无法解决。&lt;/p&gt;
&lt;p&gt;如要用于其他版本，把 jammy 换成其他版本代号即可: 22.04：jammy；20.04：focal；18.04：bionic；16.04：xenial；14.04：trusty。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 默认注释了源码仓库，如有需要可自行取消注释&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span></code></pre></div><p>在更新源时报错 Clearsigned file isn&rsquo;t valid, got &lsquo;NOSPLIT&rsquo; (does the network require authentication?)。</p>
<p>换中科大的源解决，其余源无法解决。</p>
<p>如要用于其他版本，把 jammy 换成其他版本代号即可: 22.04：jammy；20.04：focal；18.04：bionic；16.04：xenial；14.04：trusty。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 默认注释了源码仓库，如有需要可自行取消注释</span>
</span></span><span class="line"><span class="cl">deb https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>使用Gitea部署个人代码仓库</title>
      <link>https://lifeislife.cn/posts/%E4%BD%BF%E7%94%A8gitea%E9%83%A8%E7%BD%B2%E4%B8%AA%E4%BA%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93/</link>
      <pubDate>Sat, 18 Nov 2023 18:17:42 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E4%BD%BF%E7%94%A8gitea%E9%83%A8%E7%BD%B2%E4%B8%AA%E4%BA%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93/</guid>
      <description>&lt;h1 id=&#34;docker-compose-部署&#34;&gt;docker-compose 部署&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;3.7&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;5432&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5432&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;networks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;br-net-gitea&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;123456&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_DB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;gitea&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./postgresql:/var/lib/postgresql&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./data:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;gitea&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;gitea/gitea:1.20.5&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;gitea&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;USER_UID=1000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;USER_GID=1000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA__database__DB_TYPE=postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA__database__HOST=192.168.1.9:5432&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA__database__NAME=gitea&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA__database__USER=user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA__database__PASSWD=123456&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;networks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;br-net-gitea&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./data:/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/etc/timezone:/etc/timezone:ro&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/etc/localtime:/etc/localtime:ro&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/home/git/.ssh/:/data/git/.ssh&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;127.0.0.1:2222:22&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;depends_on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;act_runner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;gitea/act_runner:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA_INSTANCE_URL=http://192.168.1.9:3000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA_RUNNER_REGISTRATION_TOKEN=Qw5Qf4A1bTENfIOQlc1NSNyFYMLp7TAtSujb5ihF&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GITEA_RUNNER_NAME=docker_runner&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./act_runner/act_data:/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./act_runner/act_cache:/root/.cache&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;常见问题&#34;&gt;常见问题&lt;/h1&gt;
&lt;h2 id=&#34;首次登录web时没有创建管理员账号如何登录&#34;&gt;首次登录web时没有创建管理员账号，如何登录&lt;/h2&gt;
&lt;p&gt;打开Gitea网页注册的第一个账号就是管理员账号。无需特殊设置。也无需找回密码。&lt;/p&gt;
&lt;h2 id=&#34;从github导入仓库时报错从不允许的主机导入&#34;&gt;从Github导入仓库时报错：从不允许的主机导入&lt;/h2&gt;
&lt;p&gt;打开配置文件&lt;code&gt;gitea/conf/app.ini&lt;/code&gt;，修改以下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[migrations]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ALLOW_LOCALNETWORKS    = true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ALLOWED_DOMAINS = 127.0.0.1,192.168.31.100,github.com,*.github.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;IMPORT_LOCAL_PATHS = true  ;; 导入本地仓库开关，false：设置为false，防止所有用户（包括admin）导入服务器上的本地路径。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;docker-compose restart gitea&lt;/code&gt;重启容器。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以下修改配置文件后，需要重启容器才能生效，不再赘述。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;如何开启软件包&#34;&gt;如何开启软件包&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[packages]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ENABLED = true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;开启action&#34;&gt;开启action&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[actions]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ENABLED=true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;error-response-from-daemon-server-gave-http-response-to-https-client&#34;&gt;error response from daemon server gave http response to https client&lt;/h2&gt;
&lt;p&gt;docker login 报错&lt;/p&gt;
&lt;p&gt;配置/etc/docker/daemon.json&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#34;insecure-registries&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;#34;192.168.1.9:2010&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;如何上传docker镜像到gitea制品库&#34;&gt;如何上传docker镜像到gitea制品库&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# 登录你的镜像仓库，也就是你的 Gitea 服务器地址
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker login 192.168.1.9:2010
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# 从官方仓库拉取一个 nginx:latest 镜像，并改名
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker pull nginx:latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker tag nginx:latest 192.168.1.9:2010/zhangsan/nginx:latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# 推送镜像到 Gitea 服务器
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker push 192.168.1.9:2010/zhangsan/nginx:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="docker-compose-部署">docker-compose 部署</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3.7&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">postgres</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">postgres:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">postgres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">5432</span><span class="p">:</span><span class="m">5432</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">br-net-gitea</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_USER</span><span class="p">:</span><span class="w"> </span><span class="l">user</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="m">123456</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_DB</span><span class="p">:</span><span class="w"> </span><span class="l">gitea</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./postgresql:/var/lib/postgresql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./data:/var/lib/postgresql/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">gitea</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">gitea/gitea:1.20.5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">gitea</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">USER_UID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">USER_GID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA__database__DB_TYPE=postgres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA__database__HOST=192.168.1.9:5432</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA__database__NAME=gitea</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA__database__USER=user</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA__database__PASSWD=123456</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">br-net-gitea</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./data:/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/etc/timezone:/etc/timezone:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/etc/localtime:/etc/localtime:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/home/git/.ssh/:/data/git/.ssh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">3000</span><span class="p">:</span><span class="m">3000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;127.0.0.1:2222:22&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">postgres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">act_runner</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">gitea/act_runner:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA_INSTANCE_URL=http://192.168.1.9:3000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA_RUNNER_REGISTRATION_TOKEN=Qw5Qf4A1bTENfIOQlc1NSNyFYMLp7TAtSujb5ihF</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">GITEA_RUNNER_NAME=docker_runner</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/var/run/docker.sock:/var/run/docker.sock</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./act_runner/act_data:/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./act_runner/act_cache:/root/.cache</span><span class="w">
</span></span></span></code></pre></div><h1 id="常见问题">常见问题</h1>
<h2 id="首次登录web时没有创建管理员账号如何登录">首次登录web时没有创建管理员账号，如何登录</h2>
<p>打开Gitea网页注册的第一个账号就是管理员账号。无需特殊设置。也无需找回密码。</p>
<h2 id="从github导入仓库时报错从不允许的主机导入">从Github导入仓库时报错：从不允许的主机导入</h2>
<p>打开配置文件<code>gitea/conf/app.ini</code>，修改以下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[migrations]
</span></span><span class="line"><span class="cl">ALLOW_LOCALNETWORKS    = true
</span></span><span class="line"><span class="cl">ALLOWED_DOMAINS = 127.0.0.1,192.168.31.100,github.com,*.github.com
</span></span><span class="line"><span class="cl">IMPORT_LOCAL_PATHS = true  ;; 导入本地仓库开关，false：设置为false，防止所有用户（包括admin）导入服务器上的本地路径。
</span></span></code></pre></div><p><code>docker-compose restart gitea</code>重启容器。</p>
<blockquote>
<p>以下修改配置文件后，需要重启容器才能生效，不再赘述。</p>
</blockquote>
<h2 id="如何开启软件包">如何开启软件包</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[packages]
</span></span><span class="line"><span class="cl">ENABLED = true
</span></span></code></pre></div><h2 id="开启action">开启action</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[actions]
</span></span><span class="line"><span class="cl">ENABLED=true
</span></span></code></pre></div><h2 id="error-response-from-daemon-server-gave-http-response-to-https-client">error response from daemon server gave http response to https client</h2>
<p>docker login 报错</p>
<p>配置/etc/docker/daemon.json</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&#34;insecure-registries&#34;: [
</span></span><span class="line"><span class="cl">    &#34;192.168.1.9:2010&#34;
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><h2 id="如何上传docker镜像到gitea制品库">如何上传docker镜像到gitea制品库</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># 登录你的镜像仓库，也就是你的 Gitea 服务器地址
</span></span><span class="line"><span class="cl">docker login 192.168.1.9:2010
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># 从官方仓库拉取一个 nginx:latest 镜像，并改名
</span></span><span class="line"><span class="cl">docker pull nginx:latest
</span></span><span class="line"><span class="cl">docker tag nginx:latest 192.168.1.9:2010/zhangsan/nginx:latest
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># 推送镜像到 Gitea 服务器
</span></span><span class="line"><span class="cl">docker push 192.168.1.9:2010/zhangsan/nginx:latest
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>ocrmypdf 让 PDF 可搜索</title>
      <link>https://lifeislife.cn/posts/ocrmypdf-%E8%AE%A9pdf%E5%8F%AF%E6%90%9C%E7%B4%A2/</link>
      <pubDate>Tue, 19 Sep 2023 19:51:18 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ocrmypdf-%E8%AE%A9pdf%E5%8F%AF%E6%90%9C%E7%B4%A2/</guid>
      <description>&lt;p&gt;买的一些课程配套资料都是 PDF 格式的，为了防止盗版都事先用的图片转成的 PDF，这样 PDF 里的内容既没法复制也没法搜索，在查找资料里的关键词的时候就很不方便，所以就想着把这些 PDF 转成可搜索的 PDF。找到了一款工具叫做 ocrmypdf，可以把 PDF 转成可搜索的 PDF，而且还支持中文，这里记录一下使用方法。详细使用文档可以参考官方文档&lt;a href=&#34;https://ocrmypdf.readthedocs.io/en/latest/installation.html&#34;&gt; OCRmyPDF documentation&lt;/a&gt;。&lt;/p&gt;
&lt;h1 id=&#34;安装&#34;&gt;安装&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install ocrmypdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;使用&#34;&gt;使用&lt;/h1&gt;
&lt;h2 id=&#34;指定-ocr-的语言&#34;&gt;指定 OCR 的语言&lt;/h2&gt;
&lt;p&gt;安装语言包&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install tesseract-ocr-chi-sim
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看是否安装成功&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ tesseract --list-langs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;List of available languages &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;3&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chi_sim
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eng
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;osd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意参数 &lt;code&gt;-l&lt;/code&gt; 后面的语言包名称是下划线，而不是短横线。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ocrmypdf -l chi_sim input.pdf output.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$  ocrmypdf -l chi_sim  --redo-ocr  input.pdf output.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Scanning contents: 100%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;██████████████████████████████████████████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 752/752 &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;00:14&amp;lt;00:00, 51.36page/s&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Start processing &lt;span class=&#34;m&#34;&gt;24&lt;/span&gt; pages concurrently
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;m&#34;&gt;33&lt;/span&gt; redoing OCR                                                                                                                                                 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;m&#34;&gt;26&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;m&#34;&gt;54&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;m&#34;&gt;88&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;119&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;203&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;256&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;265&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;347&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;376&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;383&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;386&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;402&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;404&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;403&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;412&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;415&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;410&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;439&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;519&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;526&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;587&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;591&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;595&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;607&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;644&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;661&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;682&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;720&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;742&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;tesseract&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; lots of diacritics - possibly poor OCR                                                                                                          
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OCR: 100%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;████████████████████████████████████████████████████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 752.0/752.0 &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;03:41&amp;lt;00:00,  3.40page/s&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Postprocessing...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Some input metadata could not be copied because it is not permitted in PDF/A. You may wish to examine the output PDF&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;s XMP metadata.                              
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PDF/A conversion: 100%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;███████████████████████████████████████████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 752/752 &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;01:09&amp;lt;00:00, 10.80page/s&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Recompressing JPEGs: 0image &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;00:00, ?image/s&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Deflating JPEGs: 100%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;██████████████████████████████████████████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 756/756 &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;00:00&amp;lt;00:00, 920.21image/s&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;JBIG2: 0item &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;00:00, ?item/s&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Optimize ratio: 1.20 savings: 17.0%
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Output file is okay but is not PDF/A &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;seems to be No PDF/A metadata in XMP&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;转换的结果还不错，页面排版不会改变，保持原样，但是搜索文字时可能需要用空格分开。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>买的一些课程配套资料都是 PDF 格式的，为了防止盗版都事先用的图片转成的 PDF，这样 PDF 里的内容既没法复制也没法搜索，在查找资料里的关键词的时候就很不方便，所以就想着把这些 PDF 转成可搜索的 PDF。找到了一款工具叫做 ocrmypdf，可以把 PDF 转成可搜索的 PDF，而且还支持中文，这里记录一下使用方法。详细使用文档可以参考官方文档<a href="https://ocrmypdf.readthedocs.io/en/latest/installation.html"> OCRmyPDF documentation</a>。</p>
<h1 id="安装">安装</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install ocrmypdf
</span></span></code></pre></div><h1 id="使用">使用</h1>
<h2 id="指定-ocr-的语言">指定 OCR 的语言</h2>
<p>安装语言包</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install tesseract-ocr-chi-sim
</span></span></code></pre></div><p>查看是否安装成功</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ tesseract --list-langs
</span></span><span class="line"><span class="cl">List of available languages <span class="o">(</span>3<span class="o">)</span>:
</span></span><span class="line"><span class="cl">chi_sim
</span></span><span class="line"><span class="cl">eng
</span></span><span class="line"><span class="cl">osd
</span></span></code></pre></div><p>注意参数 <code>-l</code> 后面的语言包名称是下划线，而不是短横线。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ocrmypdf -l chi_sim input.pdf output.pdf
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$  ocrmypdf -l chi_sim  --redo-ocr  input.pdf output.pdf
</span></span><span class="line"><span class="cl">Scanning contents: 100%<span class="p">|</span>██████████████████████████████████████████████████████████████████████████████████████████████████████<span class="p">|</span> 752/752 <span class="o">[</span>00:14&lt;00:00, 51.36page/s<span class="o">]</span>
</span></span><span class="line"><span class="cl">Start processing <span class="m">24</span> pages concurrently
</span></span><span class="line"><span class="cl">   <span class="m">33</span> redoing OCR                                                                                                                                                 
</span></span><span class="line"><span class="cl">   <span class="m">26</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">   <span class="m">54</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">   <span class="m">88</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">119</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">203</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">256</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">265</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">347</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">376</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">383</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">386</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">402</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">404</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">403</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">412</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">415</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">410</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">439</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">519</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">526</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">587</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">591</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">595</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">607</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">644</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">661</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">682</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">720</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">  <span class="m">742</span> <span class="o">[</span>tesseract<span class="o">]</span> lots of diacritics - possibly poor OCR                                                                                                          
</span></span><span class="line"><span class="cl">OCR: 100%<span class="p">|</span>████████████████████████████████████████████████████████████████████████████████████████████████████████████████<span class="p">|</span> 752.0/752.0 <span class="o">[</span>03:41&lt;00:00,  3.40page/s<span class="o">]</span>
</span></span><span class="line"><span class="cl">Postprocessing...
</span></span><span class="line"><span class="cl">Some input metadata could not be copied because it is not permitted in PDF/A. You may wish to examine the output PDF<span class="err">&#39;</span>s XMP metadata.                              
</span></span><span class="line"><span class="cl">PDF/A conversion: 100%<span class="p">|</span>███████████████████████████████████████████████████████████████████████████████████████████████████████<span class="p">|</span> 752/752 <span class="o">[</span>01:09&lt;00:00, 10.80page/s<span class="o">]</span>
</span></span><span class="line"><span class="cl">Recompressing JPEGs: 0image <span class="o">[</span>00:00, ?image/s<span class="o">]</span>
</span></span><span class="line"><span class="cl">Deflating JPEGs: 100%<span class="p">|</span>██████████████████████████████████████████████████████████████████████████████████████████████████████<span class="p">|</span> 756/756 <span class="o">[</span>00:00&lt;00:00, 920.21image/s<span class="o">]</span>
</span></span><span class="line"><span class="cl">JBIG2: 0item <span class="o">[</span>00:00, ?item/s<span class="o">]</span>
</span></span><span class="line"><span class="cl">Optimize ratio: 1.20 savings: 17.0%
</span></span><span class="line"><span class="cl">Output file is okay but is not PDF/A <span class="o">(</span>seems to be No PDF/A metadata in XMP<span class="o">)</span>
</span></span></code></pre></div><p>转换的结果还不错，页面排版不会改变，保持原样，但是搜索文字时可能需要用空格分开。</p>
]]></content:encoded>
    </item>
    <item>
      <title>uCore-实验第 0 章 - 实验环境搭建</title>
      <link>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC0%E7%AB%A0-%E5%AE%9E%E9%AA%8C%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/</link>
      <pubDate>Fri, 08 Sep 2023 10:46:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC0%E7%AB%A0-%E5%AE%9E%E9%AA%8C%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本次实验是清华大学操作系统课程的课程实验，实验内容是基于 RISC-V 架构的 uCore 操作系统。本次实验的目的是搭建实验环境，为后续实验做准备。指导书参考&lt;a href=&#34;https://learningos.github.io/uCore-Tutorial-Guide-2023S/index.html&#34;&gt;uCore-Tutorial-Guide-2023S 文档&lt;/a&gt;。本系列文章内容主要是指导书的补充以及我在实验过程的一些理解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本章没有什么需要特别说明的，指导手册十分详细，按照指导手册的步骤一步步来就可以了。因为平时也在用 WSL2 开发，所以配置十分顺利，没有遇到什么问题。这篇文章就当占坑了，如果后续有什么需要补充的再来更新。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<blockquote>
<p>本次实验是清华大学操作系统课程的课程实验，实验内容是基于 RISC-V 架构的 uCore 操作系统。本次实验的目的是搭建实验环境，为后续实验做准备。指导书参考<a href="https://learningos.github.io/uCore-Tutorial-Guide-2023S/index.html">uCore-Tutorial-Guide-2023S 文档</a>。本系列文章内容主要是指导书的补充以及我在实验过程的一些理解。</p>
</blockquote>
<p>本章没有什么需要特别说明的，指导手册十分详细，按照指导手册的步骤一步步来就可以了。因为平时也在用 WSL2 开发，所以配置十分顺利，没有遇到什么问题。这篇文章就当占坑了，如果后续有什么需要补充的再来更新。</p>
]]></content:encoded>
    </item>
    <item>
      <title>uCore-实验第 1 章 - 应用程序与基本执行环境</title>
      <link>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC1%E7%AB%A0-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E4%B8%8E%E5%9F%BA%E6%9C%AC%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83/</link>
      <pubDate>Fri, 08 Sep 2023 10:45:14 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC1%E7%AB%A0-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E4%B8%8E%E5%9F%BA%E6%9C%AC%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83/</guid>
      <description>&lt;h1 id=&#34;了解系统调用&#34;&gt;了解系统调用&lt;/h1&gt;
&lt;p&gt;操作系统的系统调用（syscall）是操作系统提供给应用程序使用的一种接口。它允许应用程序通过向操作系统发送请求，来执行一些必须由操作系统来完成的任务，例如读取文件、创建进程、分配内存等。&lt;/p&gt;
&lt;p&gt;通俗地说，可以把操作系统看作一个巨大的服务员，而应用程序就像是顾客。应用程序不能直接访问硬件或执行特权操作，因为这样可能会导致系统不稳定或不安全。所以，应用程序需要通过系统调用来与操作系统进行交互，请求操作系统代表它完成某些任务。&lt;/p&gt;
&lt;p&gt;当应用程序需要操作系统执行特定的功能时，它会调用适当的系统调用函数，并传递参数给它。然后操作系统会接收到这个请求，并根据请求的类型和参数来执行相应的操作。完成后，操作系统会将执行结果返回给应用程序。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在 RISC-V 架构中&lt;/strong&gt;，系统调用是通过使用特定的指令来实现的。具体来说，RISC-V 架构提供了一个称为 &lt;code&gt;ecall&lt;/code&gt;（environment call）的指令来触发系统调用。&lt;/p&gt;
&lt;p&gt;要使用 syscall，在 RISC-V 汇编代码中可以通过以下步骤来完成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将系统调用编号（syscall number）放入寄存器 a7 中，该编号对应于所需的系统调用功能。&lt;/li&gt;
&lt;li&gt;将系统调用所需的参数放入其他相应的寄存器中。例如，参数传递给文件读取系统调用可能需要将文件描述符放入 a0 寄存器，缓冲区地址放入 a1 寄存器，以及读取的字节数放入 a2 寄存器。&lt;/li&gt;
&lt;li&gt;执行 ecall 指令。这会触发操作系统处理当前运行的程序的系统调用请求。&lt;/li&gt;
&lt;li&gt;操作系统接收到系统调用请求后，根据寄存器 a7 中的系统调用编号和其他寄存器中的参数来执行相应的操作。&lt;/li&gt;
&lt;li&gt;当操作系统完成系统调用请求时，它将结果放入适当的寄存器中，通常是 a0 寄存器。&lt;/li&gt;
&lt;li&gt;程序继续执行，可以检查结果并进行后续的处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;需要注意的是，具体的系统调用编号以及参数的传递方式会根据操作系统的实现而有所不同。所以在编写 RISC-V 汇编代码时，需要参考操作系统的相关文档来了解具体的系统调用接口和参数传递方式。&lt;/p&gt;
&lt;h1 id=&#34;makr-run-之后发生了什么&#34;&gt;makr run 之后发生了什么？&lt;/h1&gt;
&lt;p&gt;当执行&lt;code&gt;make run&lt;/code&gt;命令后，以下是运行流程的概述：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;内核代码编译：执行&lt;code&gt;make run&lt;/code&gt;会触发 Makefile 中的相应规则，从而编译生成内核（kernel）二进制文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;加载 kernel 并启动 QEMU：根据 QEMUOPTS 变量指定的参数，QEMU 加载生成的 kernel 二进制文件，并启动模拟器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引导代码执行：在模拟器启动后，CPU 的通用寄存器被清零，程序计数器（PC）指向 0x1000 的位置，这里有硬件固化的一小段引导代码。该引导代码会迅速跳转到 0x80000000 处的 RustSBI（Rust Supervisor Binary Interface）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RustSBI 完成硬件初始化：RustSBI 是一个用于与操作系统进行交互的接口层。在跳转到 RustSBI 之后，它会完成必要的硬件初始化工作。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;执行操作系统第一条指令：RustSBI 在完成硬件初始化后，会跳转到 kernel 二进制文件所在内存位置 0x80200000 处，并开始执行我们操作系统的第一条指令。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;综上所述，执行&lt;code&gt;make run&lt;/code&gt;命令会完成内核的编译和加载，启动 QEMU 虚拟机，并经过引导代码和 RustSBI 的处理，最终开始执行操作系统的第一条指令。&lt;/p&gt;
&lt;h1 id=&#34;了解链接脚本&#34;&gt;了解链接脚本&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# kernel.ld
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BASE_ADDRESS = 0x80200000;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SECTIONS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   . = BASE_ADDRESS;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   skernel = .;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   stext = .;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   .text : {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      *(.text.entry)   # 第一行代码
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      *(.text .text.*)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;kernelld-中的-base_address--0x80200000-指定了内核的加载地址这个地址哪来的&#34;&gt;kernel.ld 中的 &lt;code&gt;BASE_ADDRESS = 0x80200000&lt;/code&gt; 指定了内核的加载地址，这个地址哪来的？&lt;/h2&gt;
&lt;p&gt;以下内容摘自参考&lt;a href=&#34;https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/3first-instruction-in-kernel1.html&#34;&gt;rCore-Tutorial-Book-v3 3.6.0-alpha.1 文档&lt;/a&gt;：&lt;/p&gt;
&lt;p&gt;在 Qemu 模拟的 virt 硬件平台上，物理内存的起始物理地址为 &lt;code&gt;0x80000000&lt;/code&gt;，物理内存的默认大小为 128MiB，它可以通过 &lt;code&gt;-m&lt;/code&gt; 选项进行配置。如果使用默认配置的 128MiB 物理内存则对应的物理地址区间为 &lt;code&gt;[0x80000000,0x88000000)&lt;/code&gt; 。如果使用上面给出的命令启动 Qemu，那么在 Qemu 开始执行任何指令之前，首先把两个文件加载到 Qemu 的物理内存中：即作把作为 bootloader 的 rustsbi-qemu.bin 加载到物理内存以物理地址 &lt;code&gt;0x80000000&lt;/code&gt; 开头的区域上，同时把内核镜像 &lt;code&gt;os.bin&lt;/code&gt; 加载到以物理地址 &lt;code&gt;0x80200000&lt;/code&gt; 开头的区域上。&lt;/p&gt;
&lt;p&gt;为什么加载到这两个位置呢？这与 Qemu 模拟计算机加电启动后的运行流程有关。一般来说，计算机加电之后的启动流程可以分成若干个阶段，每个阶段均由一层软件或 固件 负责，每一层软件或固件的功能是进行它应当承担的初始化工作，并在此之后跳转到下一层软件或固件的入口地址，也就是将计算机的控制权移交给了下一层软件或固件。Qemu 模拟的启动流程则可以分为三个阶段：第一个阶段由固化在 Qemu 内的一小段汇编程序负责；第二个阶段由 bootloader 负责；第三个阶段则由内核镜像负责。&lt;/p&gt;
&lt;p&gt;第一阶段：将必要的文件载入到 Qemu 物理内存之后，Qemu CPU 的程序计数器（PC, Program Counter）会被初始化为 0x1000，因此 Qemu 实际执行的第一条指令位于物理地址 0x1000，接下来它将执行寥寥数条指令并跳转到物理地址 &lt;code&gt;0x80000000&lt;/code&gt; 对应的指令处并进入第二阶段。从后面的调试过程可以看出，该地址 &lt;code&gt;0x80000000&lt;/code&gt; 被固化在 Qemu 中，作为 Qemu 的使用者，我们在不触及 Qemu 源代码的情况下无法进行更改。&lt;/p&gt;
&lt;p&gt;第二阶段：由于 Qemu 的第一阶段固定跳转到 &lt;code&gt;0x80000000&lt;/code&gt;，我们需要将负责第二阶段的 bootloader rustsbi-qemu.bin 放在以物理地址 &lt;code&gt;0x80000000&lt;/code&gt; 开头的物理内存中，这样就能保证 &lt;code&gt;0x80000000&lt;/code&gt; 处正好保存 bootloader 的第一条指令。在这一阶段，bootloader 负责对计算机进行一些初始化工作，并跳转到下一阶段软件的入口，在 Qemu 上即可实现将计算机控制权移交给我们的内核镜像 os.bin。这里需要注意的是，对于不同的 bootloader 而言，下一阶段软件的入口不一定相同，而且获取这一信息的方式和时间点也不同：入口地址可能是一个预先约定好的固定的值，也有可能是在 bootloader 运行期间才动态获取到的值。我们选用的 RustSBI 则是将下一阶段的入口地址预先约定为固定的 0x80200000，在 RustSBI 的初始化工作完成之后，它会跳转到该地址并将计算机控制权移交给下一阶段的软件——也即我们的内核镜像。&lt;/p&gt;
&lt;p&gt;第三阶段：为了正确地和上一阶段的 RustSBI 对接，我们需要保证内核的第一条指令位于物理地址 &lt;code&gt;0x80200000&lt;/code&gt; 处。为此，我们需要将内核镜像预先加载到 Qemu 物理内存以地址 0x80200000 开头的区域上。一旦 CPU 开始执行内核的第一条指令，证明计算机的控制权已经被移交给我们的内核，也就达到了本节的目标。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以上过程是 QEMU 中的启动流程，真实计算机的加电启动流程大致如下：
第一阶段：加电后 CPU 的 PC 寄存器被设置为计算机内部只读存储器（ROM，Read-only Memory）的物理地址，随后 CPU 开始运行 ROM 内的软件。我们一般将该软件称为固件（Firmware），它的功能是对 CPU 进行一些初始化操作，将后续阶段的 bootloader 的代码、数据从硬盘载入到物理内存，最后跳转到适当的地址将计算机控制权转移给 bootloader。它大致对应于 Qemu 启动的第一阶段，即在物理地址 0x1000 处放置的若干条指令。可以看到 Qemu 上的固件非常简单，因为它并不需要负责将 bootloader 从硬盘加载到物理内存中，这个任务此前已经由 Qemu 自身完成了。
第二阶段：bootloader 同样完成一些 CPU 的初始化工作，将操作系统镜像从硬盘加载到物理内存中，最后跳转到适当地址将控制权转移给操作系统。可以看到一般情况下 bootloader 需要完成一些数据加载工作，这也就是它名字中 loader 的来源。它对应于 Qemu 启动的第二阶段。在 Qemu 中，我们使用的 RustSBI 功能较弱，它并没有能力完成加载的工作，内核镜像实际上是和 bootloader 一起在 Qemu 启动之前加载到物理内存中的。
第三阶段：控制权被转移给操作系统。由于篇幅所限后面我们就不再赘述了。
值得一提的是，为了让计算机的启动更加灵活，bootloader 目前可能非常复杂：它可能也分为多个阶段，并且能管理一些硬件资源，从复杂性上它已接近一个传统意义上的操作系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;终端是如何控制颜色的&#34;&gt;终端是如何控制颜色的？&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LOG_COLOR&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;RED&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;GREEN&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;BLUE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;34&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;GRAY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;90&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;YELLOW&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;93&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#if defined(USE_LOG_ERROR)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define errorf(fmt, ...)                                               \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;	do {                                                               \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;		int tid = threadid();                                          \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;		printf(&amp;#34;\x1b[%dm[%s %d]&amp;#34; fmt &amp;#34;\x1b[0m\n&amp;#34;, RED, &amp;#34;ERROR&amp;#34;, tid,   \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;		       ##__VA_ARGS__);                                         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;	} while (0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ANSI 转义码是一种用于控制终端输出的特殊字符序列。它们由&lt;code&gt;\x1b&lt;/code&gt;（或&lt;code&gt;\033&lt;/code&gt;）开头，后面跟着一系列数字和分号组成。&lt;/p&gt;
&lt;p&gt;ANSI 转义码中的数字部分用于指定不同的控制操作，如设置文本颜色、背景颜色、光标位置等等。其中，用于设置颜色的转义码包括三个主要的部分：&lt;code&gt;\x1b[颜色代码m&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;具体来说，&lt;code&gt;\x1b[&lt;/code&gt;表示开始使用控制序列，接下来的数字代表不同的颜色代码，最后的&lt;code&gt;m&lt;/code&gt;表示结束控制序列。例如，&lt;code&gt;\x1b[31m&lt;/code&gt;表示将文本颜色设置为红色，而&lt;code&gt;\x1b[0m&lt;/code&gt;用于重置所有属性为默认值。&lt;/p&gt;
&lt;p&gt;当终端遇到这样的转义序列时，它会解析并执行相应的控制操作，从而实现对文本颜色、背景颜色和其他属性的控制。&lt;/p&gt;
&lt;p&gt;需要注意的是，不同的终端可能支持不同的 ANSI 转义码，并且不同操作系统也可能有不同的实现。因此，在编写使用 ANSI 转义码的代码时，建议先测试并确保其在目标终端上正常工作。&lt;/p&gt;
&lt;p&gt;更多详细解释可以参考文章：&lt;a href=&#34;https://www.jianshu.com/p/790fc612aaa5&#34;&gt;终端颜色控制 - 简书&lt;/a&gt;。&lt;/p&gt;
&lt;h1 id=&#34;应用程序输出字符会调用-sbi-服务sbi-中发生了什么&#34;&gt;应用程序输出字符会调用 SBI 服务，SBI 中发生了什么？&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;因为对 Rust 语言不熟悉，所以这里的分析是基于 C 语言的 OpenSBI 来分析的，他们的逻辑是一样的。如果有熟悉 Rust 的可以查看 &lt;a href=&#34;https://github.com/rustsbi/rustsbi/blob/main/src/instance.rs&#34;&gt;RustSBI 源码&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据指导书中的解释以及阅读代码，我们知道调用了 &lt;code&gt;printf&lt;/code&gt; 最终实际上是调用了 &lt;code&gt;sbi_call&lt;/code&gt;。那么 &lt;code&gt;sbi_call&lt;/code&gt; 是如何实现的呢？因为我是做驱动开发以及固件开发的，也经常需要使用 OpenSBI，所想多问一句，OpenSBI 是如何实现的呢？OpenSBI 是如何提供服务的呢？它是如何打印出字符的呢？&lt;/p&gt;
&lt;h2 id=&#34;内核中的-sbi-调用&#34;&gt;内核中的 SBI 调用&lt;/h2&gt;
&lt;p&gt;我们先看一下内核中的 &lt;code&gt;sbi_call&lt;/code&gt; 都做了写啥。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// uCore-Tutorial-Code-2023S/os/sbi.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_CONSOLE_PUTCHAR&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;console_putchar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nf&#34;&gt;sbi_call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SBI_CONSOLE_PUTCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// uCore-Tutorial-Code-2023S/os/sbi.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;which&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 使用寄存器变量来保存参数值和系统调用编号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;register&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a0&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;a0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 将 &amp;#39;arg0&amp;#39; 的值保存在寄存器 &amp;#39;a0&amp;#39; 中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;register&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a1&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;a1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 将 &amp;#39;arg1&amp;#39; 的值保存在寄存器 &amp;#39;a1&amp;#39; 中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;register&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a2&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;a2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 将 &amp;#39;arg2&amp;#39; 的值保存在寄存器 &amp;#39;a2&amp;#39; 中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;register&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a7&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;a7&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;which&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 将 &amp;#39;which&amp;#39; 的值保存在寄存器 &amp;#39;a7&amp;#39; 中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 内联汇编代码使用 ecall 指令进行系统调用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;volatile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s&#34;&gt;&amp;#34;ecall&amp;#34;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 使用 ecall 指令进行系统调用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 在这段代码中，指令 &amp;#34;ecall&amp;#34; 的输入参数是寄存器 a0 a1 a2 和 a7，输出参数是寄存器 a0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;=r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 输出操作数：将返回值存储在变量 &amp;#39;a0&amp;#39; 中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 输入操作数：传递参数和系统调用编号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;//  &amp;#34;memory&amp;#34; 标志告诉编译器，这条指令可能会修改内存中的数据，需要进行内存屏障操作来保证数据的正确性。 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 返回存储在变量 &amp;#39;a0&amp;#39; 中的值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那么 OpenSBI 如何提供服务？在&lt;code&gt;include/sbi/sbi_ecall.h&lt;/code&gt;这种定义了每个&lt;code&gt;ecall&lt;/code&gt;服务全局变量。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//include/sbi/sbi_ecall.h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_legacy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_rfence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_ipi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_vendor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_hsm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_srst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在&lt;code&gt;lib/sbi/sbi_ecall.c&lt;/code&gt;中注册了所有的&lt;code&gt;ecall&lt;/code&gt;服务，并将其加到链表&lt;code&gt;ecall_exts_list&lt;/code&gt;中。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_ecall_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_exts_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_exts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_ecall_register_extension&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_ecall_register_extension&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_EINVAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nf&#34;&gt;sbi_list_for_each_entry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ecall_exts_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;cm&#34;&gt;/* no overlap */&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_EINVAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nf&#34;&gt;SBI_INIT_LIST_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nf&#34;&gt;sbi_list_add_tail&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ecall_exts_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Iterate over list of given type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param pos the type * to use as a loop cursor.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param head the head for your list.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param member the name of the list_struct within the struct.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define sbi_list_for_each_entry(pos, head, member) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;	for (pos = sbi_list_entry((head)-&amp;gt;next, typeof(*pos), member);	\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;	     &amp;amp;pos-&amp;gt;member != (head); 	\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;	     pos = sbi_list_entry(pos-&amp;gt;member.next, typeof(*pos), member))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那么服务 id 如何和相对应的服务绑定的呢？以&lt;code&gt;ecall_time&lt;/code&gt;为例，查看其结构体原型&lt;code&gt;struct sbi_ecall_extension&lt;/code&gt; ：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// include/sbi/sbi_ecall.h: 23
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_dlist&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extid_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extid_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;probe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;out_val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;funcid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		       &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_trap_regs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		       &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;out_val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		       &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_trap_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;out_trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到有 &lt;code&gt;extid_start&lt;/code&gt;、&lt;code&gt;extid_end&lt;/code&gt; 和 &lt;code&gt;handle&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;目前 OpenSBI 逐步将每个服务的实现都放在了&lt;code&gt;lib/sbi&lt;/code&gt;单独文件中，以&lt;code&gt;ecall_time&lt;/code&gt;为例，其实现在&lt;code&gt;lib/sbi/sbi_ecall_time.c&lt;/code&gt;中。单独为其绑定回调处理函数&lt;code&gt;sbi_ecall_time_handler&lt;/code&gt;。但是还有很多服务的实现还是放在了&lt;code&gt;lib/sbi/sbi_ecall_legacy.c&lt;/code&gt;中，后续应该会逐步迁移。我们上文使用的&lt;code&gt;SBI_CONSOLE_PUTCHAR&lt;/code&gt;服务就是在这里实现的。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// lib/sbi/sbi_ecall_legacy.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_extension&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ecall_legacy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_EXT_0_1_SET_TIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid_end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_EXT_0_1_SHUTDOWN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;handle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_ecall_legacy_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_ecall_legacy_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;funcid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_trap_regs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;out_val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_trap_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;out_trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_tlb_info&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tlb_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source_hart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;current_hartid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;ulong&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hmask&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;SBI_EXT_0_1_SET_TIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nf&#34;&gt;sbi_timer_event_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;SBI_EXT_0_1_CONSOLE_PUTCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nf&#34;&gt;sbi_putc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;SBI_EXT_0_1_CONSOLE_GETCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_getc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这就把 &lt;code&gt;id&lt;/code&gt; 与相应的服务函数绑定。一个&lt;code&gt;extid&lt;/code&gt;对应一个&lt;code&gt;handler&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们可以在找到&lt;code&gt;SBI_EXT_0_1_CONSOLE_PUTCHAR&lt;/code&gt;的值，是与 Linux 内核里定义的值是一致的。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// include/sbi/sbi_ecall_interface.h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* SBI Extension IDs */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define SBI_EXT_0_1_CONSOLE_PUTCHAR		0x1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ecall-服务调用流程&#34;&gt;ecall 服务调用流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;code&gt;firmware/fw_base.S&lt;/code&gt; 中注册了 &lt;code&gt;Machine Mode&lt;/code&gt; 的 &lt;code&gt;trap handler&lt;/code&gt;，即 &lt;code&gt;sbi_trap_handler&lt;/code&gt;；&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nl&#34;&gt;_start_warm:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Setup trap handler */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;la&lt;/span&gt;	&lt;span class=&#34;no&#34;&gt;a4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;_trap_handler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;csrw&lt;/span&gt;	&lt;span class=&#34;no&#34;&gt;CSR_MTVEC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;a4&lt;/span&gt;  &lt;span class=&#34;cm&#34;&gt;/* CSR_MTVEC = _trap_handler */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nl&#34;&gt;_trap_handler:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;TRAP_SAVE_AND_SETUP_SP_T0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;TRAP_SAVE_MEPC_MSTATUS&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;TRAP_CALL_C_ROUTINE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;TRAP_RESTORE_MEPC_MSTATUS&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;TRAP_RESTORE_SP_T0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;mret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;.macro&lt;/span&gt;	&lt;span class=&#34;no&#34;&gt;TRAP_CALL_C_ROUTINE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Call C routine */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;add&lt;/span&gt;	&lt;span class=&#34;no&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;sp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;call&lt;/span&gt;	&lt;span class=&#34;no&#34;&gt;sbi_trap_handler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;.endm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;code&gt;lib/sbi/sbi_trap.c&lt;/code&gt; 中定义了 &lt;code&gt;sbi_trap_handler&lt;/code&gt;，处理各种 &lt;code&gt;mcause&lt;/code&gt;，比如 &lt;code&gt;Illegal Instructions&lt;/code&gt;，&lt;code&gt;Misaligned Load &amp;amp; Store&lt;/code&gt;, &lt;code&gt;Supervisor &amp;amp; Machine Ecall&lt;/code&gt; 等。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// lib/sbi/sbi_trap.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_trap_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_trap_regs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mcause&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;CAUSE_ILLEGAL_INSTRUCTION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_illegal_insn_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mtval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;CAUSE_MISALIGNED_LOAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_misaligned_load_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mtval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mtval2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mtinst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;CAUSE_MISALIGNED_STORE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_misaligned_store_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mtval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mtval2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mtinst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;CAUSE_SUPERVISOR_ECALL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;CAUSE_MACHINE_ECALL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_ecall_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;cm&#34;&gt;/* If the trap came from S or U mode, redirect it there */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;epc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mepc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cause&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mcause&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tval&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mtval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tval2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mtval2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tinst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mtinst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_trap_redirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;code&gt;lib/sbi/sbi_ecall.c&lt;/code&gt; 中定义了处理 &lt;code&gt;ecall mcause&lt;/code&gt; 的 &lt;code&gt;sbi_ecall_handler&lt;/code&gt;，它遍历上面 &lt;code&gt;ecall_exts_list&lt;/code&gt; 中注册的各种 &lt;code&gt;ecall&lt;/code&gt; 服务。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sbi_ecall_handler&lt;/code&gt; 根据 Linux 内核传递的 &lt;code&gt;ext (extension id)&lt;/code&gt; 找到链表中对应的 &lt;code&gt;ecall&lt;/code&gt; 服务，执行其中的 &lt;code&gt;handle&lt;/code&gt; 函数，该函数根据 &lt;code&gt;fid&lt;/code&gt; 执行具体的服务内容。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// lib/sbi/sbi_ecall.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_ecall_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_trap_regs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extension_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;func_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sbi_trap_info&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;out_val&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 遍历所有 ecall 服务
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sbi_ecall_find_extension&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extension_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 如果找到了就执行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extension_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;func_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;regs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;out_val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extension_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_EXT_0_1_SET_TIMER&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;extension_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_EXT_0_1_SHUTDOWN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;is_0_1_spec&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SBI_ENOTSUPP&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们可以发现 &lt;code&gt;extension_id&lt;/code&gt; 就是 a7 寄存器，他和我们在 uCore OS 中定义的 &lt;code&gt;SBI_EXT_0_1_CONSOLE_PUTCHAR&lt;/code&gt; 是一致的。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;程序的内存布局与编译流程&#34;&gt;程序的内存布局与编译流程&lt;/h1&gt;
&lt;h2 id=&#34;程序的内存布局&#34;&gt;程序的内存布局&lt;/h2&gt;
&lt;h1 id=&#34;ucore-的编译系统&#34;&gt;uCore 的编译系统&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-makefile&#34; data-lang=&#34;makefile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clean&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 设置伪目标clean、build和user，可以通过命令make来执行这些目标
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build_kernel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 默认目标为build_kernel，即执行build_kernel目标下的指令
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?=&lt;/span&gt; error
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量LOG，默认值是error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;K&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;TOOLPREFIX&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; riscv64-unknown-elf-
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CC&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;TOOLPREFIX&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;AS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;TOOLPREFIX&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LD&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;TOOLPREFIX&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;ld
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;OBJCOPY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;TOOLPREFIX&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;objcopy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;OBJDUMP&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;TOOLPREFIX&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;objdump
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; python3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;GDB&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;TOOLPREFIX&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;gdb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CP&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; cp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MKDIR_P&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; mkdir -p
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BUILDDIR&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; build
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;C_SRCS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;wildcard &lt;span class=&#34;nv&#34;&gt;$K&lt;/span&gt;/*.c&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量C_SRCS，使用wildcard函数匹配所有以.c为后缀的文件，并存储在$K目录下
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;AS_SRCS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;wildcard &lt;span class=&#34;nv&#34;&gt;$K&lt;/span&gt;/*.S&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量AS_SRCS，使用wildcard函数匹配所有以.S为后缀的文件，并存储在$K目录下
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;C_OBJS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;addprefix &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/, &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;addsuffix .o, &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;basename &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;C_SRCS&lt;span class=&#34;k&#34;&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量C_OBJS，通过addprefix和addsuffix函数将$(C_SRCS)中的路径替换为$(BUILDDIR)，并将后缀修改为.o
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;AS_OBJS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;addprefix &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/, &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;addsuffix .o, &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;basename &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;AS_SRCS&lt;span class=&#34;k&#34;&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量AS_OBJS，通过addprefix和addsuffix函数将$(AS_SRCS)中的路径替换为$(BUILDDIR)，并将后缀修改为.o
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;OBJS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;C_OBJS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;AS_OBJS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量OBJS，其值为$(C_OBJS)和$(AS_OBJS)的组合
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;HEADER_DEP&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;addsuffix .d, &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;basename &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;C_OBJS&lt;span class=&#34;k&#34;&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量HEADER_DEP，通过addsuffix函数将$(C_OBJS)中的后缀修改为.d
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;-include&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;HEADER_DEP&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 包含$(HEADER_DEP)中的.d文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; -Wall -Werror -O -fno-omit-frame-pointer -ggdb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量CFLAGS，并赋值为-Wall -Werror -O -fno-omit-frame-pointer -ggdb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -MD
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 将-MD选项追加到CFLAGS变量中，用于自动生成依赖关系文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -mcmodel&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;medany
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 将-mcmodel=medany选项追加到CFLAGS变量中，用于指定内存模型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -ffreestanding -fno-common -nostdlib -mno-relax
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 将-ffreestanding -fno-common -nostdlib -mno-relax选项追加到CFLAGS变量中，用于编译无操作系统环境下的程序
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -I&lt;span class=&#34;nv&#34;&gt;$K&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 将-I$K选项追加到CFLAGS变量中，用于指定头文件搜索路径为$K目录下
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;shell &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;CC&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -fno-stack-protector -E -x c /dev/null &amp;gt;/dev/null 2&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; -fno-stack-protector&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 将$(CC) -fno-stack-protector -E -x c /dev/null &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; echo -fno-stack-protector命令执行结果追加到CFLAGS变量中，用于禁用栈保护机制
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;ifeq&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;error)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -D LOG_LEVEL_ERROR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;ifeq&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;warn)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -D LOG_LEVEL_WARN
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;ifeq&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;info)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -D LOG_LEVEL_INFO
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;ifeq&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;debug)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -D LOG_LEVEL_DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;ifeq&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;trace)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -D LOG_LEVEL_TRACE
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;endif&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 根据$(LOG)变量的值，向CFLAGS变量追加相应的预处理器选项，相当于添加了一个宏定义，log.h中的LOG_LEVEL_ERROR等宏定义会根据这个宏定义来决定是否生效
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Disable PIE when possible (for Ubuntu 16.10 toolchain)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;ifneq&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;shell&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;CC&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -&lt;span class=&#34;nv&#34;&gt;dumpspecs&lt;/span&gt; 2&amp;gt;/&lt;span class=&#34;nv&#34;&gt;dev&lt;/span&gt;/&lt;span class=&#34;nv&#34;&gt;null&lt;/span&gt; | &lt;span class=&#34;nv&#34;&gt;grep&lt;/span&gt; -&lt;span class=&#34;nv&#34;&gt;e&lt;/span&gt; &amp;#39;[^&lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;]&lt;span class=&#34;nv&#34;&gt;no&lt;/span&gt;-&lt;span class=&#34;nv&#34;&gt;pie&lt;/span&gt;&amp;#39;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -fno-pie -no-pie
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;endif&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;ifneq&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;shell&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;CC&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -&lt;span class=&#34;nv&#34;&gt;dumpspecs&lt;/span&gt; 2&amp;gt;/&lt;span class=&#34;nv&#34;&gt;dev&lt;/span&gt;/&lt;span class=&#34;nv&#34;&gt;null&lt;/span&gt; | &lt;span class=&#34;nv&#34;&gt;grep&lt;/span&gt; -&lt;span class=&#34;nv&#34;&gt;e&lt;/span&gt; &amp;#39;[^&lt;span class=&#34;nv&#34;&gt;f&lt;/span&gt;]&lt;span class=&#34;nv&#34;&gt;nopie&lt;/span&gt;&amp;#39;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; -fno-pie -nopie
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;endif&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 根据系统环境判断是否支持PIE（位置无关执行）选项，并根据情况向CFLAGS变量追加相应的选项
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LDFLAGS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; -z max-page-size&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4096&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个变量LDFLAGS，并赋值为-z max-page-size=4096
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;$(AS_OBJS)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BUILDDIR&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/$&lt;span class=&#34;n&#34;&gt;K&lt;/span&gt;/%.&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt; : $&lt;span class=&#34;n&#34;&gt;K&lt;/span&gt;/%.&lt;span class=&#34;n&#34;&gt;S&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @mkdir -p &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;@D&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;CC&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;CFLAGS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -c $&amp;lt; -o &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 规则：生成$(AS_OBJS)目标所需的依赖文件$(BUILDDIR)/$K/%.o，依赖于$K/%.S，并通过$(CC)命令编译生成目标文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;$(C_OBJS)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BUILDDIR&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/$&lt;span class=&#34;n&#34;&gt;K&lt;/span&gt;/%.&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt; : $&lt;span class=&#34;n&#34;&gt;K&lt;/span&gt;/%.&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BUILDDIR&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/$&lt;span class=&#34;n&#34;&gt;K&lt;/span&gt;/%.&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @mkdir -p &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;@D&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;CC&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;CFLAGS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -c $&amp;lt; -o &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 规则：生成$(C_OBJS)目标所需的依赖文件$(BUILDDIR)/$K/%.o，依赖于$K/%.c和$(BUILDDIR)/$K/%.d，并通过$(CC)命令编译生成目标文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;$(HEADER_DEP)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BUILDDIR&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/$&lt;span class=&#34;n&#34;&gt;K&lt;/span&gt;/%.&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt; : $&lt;span class=&#34;n&#34;&gt;K&lt;/span&gt;/%.&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @mkdir -p &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;@D&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @set -e&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; rm -f &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;CC&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -MM $&amp;lt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;INCLUDEFLAGS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;.&lt;span class=&#34;nv&#34;&gt;$$$$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        sed &lt;span class=&#34;s1&#34;&gt;&amp;#39;s,\($*\)\.o[ :]*,\1.o $@ : ,g&amp;#39;&lt;/span&gt; &amp;lt; &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;.&lt;span class=&#34;nv&#34;&gt;$$$$&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        rm -f &lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;.&lt;span class=&#34;nv&#34;&gt;$$$$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 规则：生成$(HEADER_DEP)目标所需的依赖文件$(BUILDDIR)/$K/%.d，依赖于$K/%.c，并通过$(CC)命令生成依赖关系文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;/&lt;span class=&#34;n&#34;&gt;kernel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 定义一个目标build，其依赖于build/kernel
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;build/kernel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;OBJS&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LD&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;LDFLAGS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -T os/kernel.ld -o &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/kernel &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;OBJS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;OBJDUMP&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -S &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/kernel &amp;gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/kernel.asm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;OBJDUMP&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -t &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/kernel &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sed &lt;span class=&#34;s1&#34;&gt;&amp;#39;1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d&amp;#39;&lt;/span&gt; &amp;gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;/kernel.sym
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s1&#34;&gt;&amp;#39;Build kernel done&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 规则：生成build/kernel目标，依赖于$(OBJS)，通过$(LD)命令连接生成kernel，并通过$(OBJDUMP)命令生成汇编文件和符号表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;clean&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    rm -rf &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BUILDDIR&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# BOARD
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BOARD&lt;/span&gt;		&lt;span class=&#34;o&#34;&gt;?=&lt;/span&gt; qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SBI&lt;/span&gt;			&lt;span class=&#34;o&#34;&gt;?=&lt;/span&gt; rustsbi
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BOOTLOADER&lt;/span&gt;	&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; ./bootloader/rustsbi-qemu.bin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;QEMU&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; qemu-system-riscv64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;QEMUOPTS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-nographic &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-machine virt &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-bios &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;BOOTLOADER&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	-kernel build/kernel	&lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;/&lt;span class=&#34;n&#34;&gt;kernel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;QEMU&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;QEMUOPTS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# QEMU&amp;#39;s gdb stub command line changed in 0.11
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;QEMUGDB&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;shell &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;QEMU&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -help &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -q &lt;span class=&#34;s1&#34;&gt;&amp;#39;^-gdb&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-gdb tcp::15234&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-s -p 15234&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 启动QEMU并通过GDB调试，此时QEMu会进入后台运行，并暂停执行，等待GDB连接
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 连接的GDB端口为15234
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;/&lt;span class=&#34;n&#34;&gt;kernel&lt;/span&gt; .&lt;span class=&#34;n&#34;&gt;gdbinit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;QEMU&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;QEMUOPTS&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -S &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;QEMUGDB&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	sleep &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;GDB&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编译、运行 uCore 的一些常用命令有如下一些，涉及了后续章节中引入的测试用例中的命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make run
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make debug
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 编译测试用例的前四章&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make user &lt;span class=&#34;nv&#34;&gt;CHAPTER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;trace
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 编译测试用例的第四章&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make user &lt;span class=&#34;nv&#34;&gt;CHAPTER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4_only &lt;span class=&#34;nv&#34;&gt;LOG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;trace
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 只运行测试用例的第四章&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CHAPTER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4_only    
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;附录&#34;&gt;附录&lt;/h1&gt;
&lt;p&gt;makefile 和 qemu&lt;/p&gt;
&lt;p&gt;AS = $(TOOLPREFIX)gas  &amp;gt; AS = $(TOOLPREFIX)as&lt;/p&gt;
&lt;h1 id=&#34;参考资料&#34;&gt;参考资料&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.jianshu.com/p/790fc612aaa5&#34;&gt;终端颜色控制 - 简书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h1 id="了解系统调用">了解系统调用</h1>
<p>操作系统的系统调用（syscall）是操作系统提供给应用程序使用的一种接口。它允许应用程序通过向操作系统发送请求，来执行一些必须由操作系统来完成的任务，例如读取文件、创建进程、分配内存等。</p>
<p>通俗地说，可以把操作系统看作一个巨大的服务员，而应用程序就像是顾客。应用程序不能直接访问硬件或执行特权操作，因为这样可能会导致系统不稳定或不安全。所以，应用程序需要通过系统调用来与操作系统进行交互，请求操作系统代表它完成某些任务。</p>
<p>当应用程序需要操作系统执行特定的功能时，它会调用适当的系统调用函数，并传递参数给它。然后操作系统会接收到这个请求，并根据请求的类型和参数来执行相应的操作。完成后，操作系统会将执行结果返回给应用程序。</p>
<p><strong>在 RISC-V 架构中</strong>，系统调用是通过使用特定的指令来实现的。具体来说，RISC-V 架构提供了一个称为 <code>ecall</code>（environment call）的指令来触发系统调用。</p>
<p>要使用 syscall，在 RISC-V 汇编代码中可以通过以下步骤来完成：</p>
<ol>
<li>将系统调用编号（syscall number）放入寄存器 a7 中，该编号对应于所需的系统调用功能。</li>
<li>将系统调用所需的参数放入其他相应的寄存器中。例如，参数传递给文件读取系统调用可能需要将文件描述符放入 a0 寄存器，缓冲区地址放入 a1 寄存器，以及读取的字节数放入 a2 寄存器。</li>
<li>执行 ecall 指令。这会触发操作系统处理当前运行的程序的系统调用请求。</li>
<li>操作系统接收到系统调用请求后，根据寄存器 a7 中的系统调用编号和其他寄存器中的参数来执行相应的操作。</li>
<li>当操作系统完成系统调用请求时，它将结果放入适当的寄存器中，通常是 a0 寄存器。</li>
<li>程序继续执行，可以检查结果并进行后续的处理。</li>
</ol>
<p>需要注意的是，具体的系统调用编号以及参数的传递方式会根据操作系统的实现而有所不同。所以在编写 RISC-V 汇编代码时，需要参考操作系统的相关文档来了解具体的系统调用接口和参数传递方式。</p>
<h1 id="makr-run-之后发生了什么">makr run 之后发生了什么？</h1>
<p>当执行<code>make run</code>命令后，以下是运行流程的概述：</p>
<ol>
<li>
<p>内核代码编译：执行<code>make run</code>会触发 Makefile 中的相应规则，从而编译生成内核（kernel）二进制文件。</p>
</li>
<li>
<p>加载 kernel 并启动 QEMU：根据 QEMUOPTS 变量指定的参数，QEMU 加载生成的 kernel 二进制文件，并启动模拟器。</p>
</li>
<li>
<p>引导代码执行：在模拟器启动后，CPU 的通用寄存器被清零，程序计数器（PC）指向 0x1000 的位置，这里有硬件固化的一小段引导代码。该引导代码会迅速跳转到 0x80000000 处的 RustSBI（Rust Supervisor Binary Interface）。</p>
</li>
<li>
<p>RustSBI 完成硬件初始化：RustSBI 是一个用于与操作系统进行交互的接口层。在跳转到 RustSBI 之后，它会完成必要的硬件初始化工作。</p>
</li>
<li>
<p>执行操作系统第一条指令：RustSBI 在完成硬件初始化后，会跳转到 kernel 二进制文件所在内存位置 0x80200000 处，并开始执行我们操作系统的第一条指令。</p>
</li>
</ol>
<p>综上所述，执行<code>make run</code>命令会完成内核的编译和加载，启动 QEMU 虚拟机，并经过引导代码和 RustSBI 的处理，最终开始执行操作系统的第一条指令。</p>
<h1 id="了解链接脚本">了解链接脚本</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># kernel.ld
</span></span><span class="line"><span class="cl">BASE_ADDRESS = 0x80200000;
</span></span><span class="line"><span class="cl">SECTIONS
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">   . = BASE_ADDRESS;
</span></span><span class="line"><span class="cl">   skernel = .;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   stext = .;
</span></span><span class="line"><span class="cl">   .text : {
</span></span><span class="line"><span class="cl">      *(.text.entry)   # 第一行代码
</span></span><span class="line"><span class="cl">      *(.text .text.*)
</span></span><span class="line"><span class="cl">   }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   ...
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h2 id="kernelld-中的-base_address--0x80200000-指定了内核的加载地址这个地址哪来的">kernel.ld 中的 <code>BASE_ADDRESS = 0x80200000</code> 指定了内核的加载地址，这个地址哪来的？</h2>
<p>以下内容摘自参考<a href="https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/3first-instruction-in-kernel1.html">rCore-Tutorial-Book-v3 3.6.0-alpha.1 文档</a>：</p>
<p>在 Qemu 模拟的 virt 硬件平台上，物理内存的起始物理地址为 <code>0x80000000</code>，物理内存的默认大小为 128MiB，它可以通过 <code>-m</code> 选项进行配置。如果使用默认配置的 128MiB 物理内存则对应的物理地址区间为 <code>[0x80000000,0x88000000)</code> 。如果使用上面给出的命令启动 Qemu，那么在 Qemu 开始执行任何指令之前，首先把两个文件加载到 Qemu 的物理内存中：即作把作为 bootloader 的 rustsbi-qemu.bin 加载到物理内存以物理地址 <code>0x80000000</code> 开头的区域上，同时把内核镜像 <code>os.bin</code> 加载到以物理地址 <code>0x80200000</code> 开头的区域上。</p>
<p>为什么加载到这两个位置呢？这与 Qemu 模拟计算机加电启动后的运行流程有关。一般来说，计算机加电之后的启动流程可以分成若干个阶段，每个阶段均由一层软件或 固件 负责，每一层软件或固件的功能是进行它应当承担的初始化工作，并在此之后跳转到下一层软件或固件的入口地址，也就是将计算机的控制权移交给了下一层软件或固件。Qemu 模拟的启动流程则可以分为三个阶段：第一个阶段由固化在 Qemu 内的一小段汇编程序负责；第二个阶段由 bootloader 负责；第三个阶段则由内核镜像负责。</p>
<p>第一阶段：将必要的文件载入到 Qemu 物理内存之后，Qemu CPU 的程序计数器（PC, Program Counter）会被初始化为 0x1000，因此 Qemu 实际执行的第一条指令位于物理地址 0x1000，接下来它将执行寥寥数条指令并跳转到物理地址 <code>0x80000000</code> 对应的指令处并进入第二阶段。从后面的调试过程可以看出，该地址 <code>0x80000000</code> 被固化在 Qemu 中，作为 Qemu 的使用者，我们在不触及 Qemu 源代码的情况下无法进行更改。</p>
<p>第二阶段：由于 Qemu 的第一阶段固定跳转到 <code>0x80000000</code>，我们需要将负责第二阶段的 bootloader rustsbi-qemu.bin 放在以物理地址 <code>0x80000000</code> 开头的物理内存中，这样就能保证 <code>0x80000000</code> 处正好保存 bootloader 的第一条指令。在这一阶段，bootloader 负责对计算机进行一些初始化工作，并跳转到下一阶段软件的入口，在 Qemu 上即可实现将计算机控制权移交给我们的内核镜像 os.bin。这里需要注意的是，对于不同的 bootloader 而言，下一阶段软件的入口不一定相同，而且获取这一信息的方式和时间点也不同：入口地址可能是一个预先约定好的固定的值，也有可能是在 bootloader 运行期间才动态获取到的值。我们选用的 RustSBI 则是将下一阶段的入口地址预先约定为固定的 0x80200000，在 RustSBI 的初始化工作完成之后，它会跳转到该地址并将计算机控制权移交给下一阶段的软件——也即我们的内核镜像。</p>
<p>第三阶段：为了正确地和上一阶段的 RustSBI 对接，我们需要保证内核的第一条指令位于物理地址 <code>0x80200000</code> 处。为此，我们需要将内核镜像预先加载到 Qemu 物理内存以地址 0x80200000 开头的区域上。一旦 CPU 开始执行内核的第一条指令，证明计算机的控制权已经被移交给我们的内核，也就达到了本节的目标。</p>
<blockquote>
<p>以上过程是 QEMU 中的启动流程，真实计算机的加电启动流程大致如下：
第一阶段：加电后 CPU 的 PC 寄存器被设置为计算机内部只读存储器（ROM，Read-only Memory）的物理地址，随后 CPU 开始运行 ROM 内的软件。我们一般将该软件称为固件（Firmware），它的功能是对 CPU 进行一些初始化操作，将后续阶段的 bootloader 的代码、数据从硬盘载入到物理内存，最后跳转到适当的地址将计算机控制权转移给 bootloader。它大致对应于 Qemu 启动的第一阶段，即在物理地址 0x1000 处放置的若干条指令。可以看到 Qemu 上的固件非常简单，因为它并不需要负责将 bootloader 从硬盘加载到物理内存中，这个任务此前已经由 Qemu 自身完成了。
第二阶段：bootloader 同样完成一些 CPU 的初始化工作，将操作系统镜像从硬盘加载到物理内存中，最后跳转到适当地址将控制权转移给操作系统。可以看到一般情况下 bootloader 需要完成一些数据加载工作，这也就是它名字中 loader 的来源。它对应于 Qemu 启动的第二阶段。在 Qemu 中，我们使用的 RustSBI 功能较弱，它并没有能力完成加载的工作，内核镜像实际上是和 bootloader 一起在 Qemu 启动之前加载到物理内存中的。
第三阶段：控制权被转移给操作系统。由于篇幅所限后面我们就不再赘述了。
值得一提的是，为了让计算机的启动更加灵活，bootloader 目前可能非常复杂：它可能也分为多个阶段，并且能管理一些硬件资源，从复杂性上它已接近一个传统意义上的操作系统。</p>
</blockquote>
<h1 id="终端是如何控制颜色的">终端是如何控制颜色的？</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">enum</span> <span class="n">LOG_COLOR</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="n">RED</span> <span class="o">=</span> <span class="mi">31</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="n">GREEN</span> <span class="o">=</span> <span class="mi">32</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="n">BLUE</span> <span class="o">=</span> <span class="mi">34</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="n">GRAY</span> <span class="o">=</span> <span class="mi">90</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="n">YELLOW</span> <span class="o">=</span> <span class="mi">93</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#if defined(USE_LOG_ERROR)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define errorf(fmt, ...)                                               \
</span></span></span><span class="line"><span class="cl"><span class="cp">	do {                                                               \
</span></span></span><span class="line"><span class="cl"><span class="cp">		int tid = threadid();                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">		printf(&#34;\x1b[%dm[%s %d]&#34; fmt &#34;\x1b[0m\n&#34;, RED, &#34;ERROR&#34;, tid,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">		       ##__VA_ARGS__);                                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">	} while (0)
</span></span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span></code></pre></div><p>ANSI 转义码是一种用于控制终端输出的特殊字符序列。它们由<code>\x1b</code>（或<code>\033</code>）开头，后面跟着一系列数字和分号组成。</p>
<p>ANSI 转义码中的数字部分用于指定不同的控制操作，如设置文本颜色、背景颜色、光标位置等等。其中，用于设置颜色的转义码包括三个主要的部分：<code>\x1b[颜色代码m</code>。</p>
<p>具体来说，<code>\x1b[</code>表示开始使用控制序列，接下来的数字代表不同的颜色代码，最后的<code>m</code>表示结束控制序列。例如，<code>\x1b[31m</code>表示将文本颜色设置为红色，而<code>\x1b[0m</code>用于重置所有属性为默认值。</p>
<p>当终端遇到这样的转义序列时，它会解析并执行相应的控制操作，从而实现对文本颜色、背景颜色和其他属性的控制。</p>
<p>需要注意的是，不同的终端可能支持不同的 ANSI 转义码，并且不同操作系统也可能有不同的实现。因此，在编写使用 ANSI 转义码的代码时，建议先测试并确保其在目标终端上正常工作。</p>
<p>更多详细解释可以参考文章：<a href="https://www.jianshu.com/p/790fc612aaa5">终端颜色控制 - 简书</a>。</p>
<h1 id="应用程序输出字符会调用-sbi-服务sbi-中发生了什么">应用程序输出字符会调用 SBI 服务，SBI 中发生了什么？</h1>
<blockquote>
<p>因为对 Rust 语言不熟悉，所以这里的分析是基于 C 语言的 OpenSBI 来分析的，他们的逻辑是一样的。如果有熟悉 Rust 的可以查看 <a href="https://github.com/rustsbi/rustsbi/blob/main/src/instance.rs">RustSBI 源码</a></p>
</blockquote>
<p>根据指导书中的解释以及阅读代码，我们知道调用了 <code>printf</code> 最终实际上是调用了 <code>sbi_call</code>。那么 <code>sbi_call</code> 是如何实现的呢？因为我是做驱动开发以及固件开发的，也经常需要使用 OpenSBI，所想多问一句，OpenSBI 是如何实现的呢？OpenSBI 是如何提供服务的呢？它是如何打印出字符的呢？</p>
<h2 id="内核中的-sbi-调用">内核中的 SBI 调用</h2>
<p>我们先看一下内核中的 <code>sbi_call</code> 都做了写啥。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// uCore-Tutorial-Code-2023S/os/sbi.c
</span></span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="n">uint64</span> <span class="n">SBI_CONSOLE_PUTCHAR</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">console_putchar</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nf">sbi_call</span><span class="p">(</span><span class="n">SBI_CONSOLE_PUTCHAR</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// uCore-Tutorial-Code-2023S/os/sbi.c
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="kr">inline</span> <span class="nf">sbi_call</span><span class="p">(</span><span class="n">uint64</span> <span class="n">which</span><span class="p">,</span> <span class="n">uint64</span> <span class="n">arg0</span><span class="p">,</span> <span class="n">uint64</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">uint64</span> <span class="n">arg2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 使用寄存器变量来保存参数值和系统调用编号
</span></span></span><span class="line"><span class="cl">    <span class="k">register</span> <span class="n">uint64</span> <span class="n">a0</span> <span class="k">asm</span><span class="p">(</span><span class="s">&#34;a0&#34;</span><span class="p">)</span> <span class="o">=</span> <span class="n">arg0</span><span class="p">;</span>  <span class="c1">// 将 &#39;arg0&#39; 的值保存在寄存器 &#39;a0&#39; 中
</span></span></span><span class="line"><span class="cl">    <span class="k">register</span> <span class="n">uint64</span> <span class="n">a1</span> <span class="k">asm</span><span class="p">(</span><span class="s">&#34;a1&#34;</span><span class="p">)</span> <span class="o">=</span> <span class="n">arg1</span><span class="p">;</span>  <span class="c1">// 将 &#39;arg1&#39; 的值保存在寄存器 &#39;a1&#39; 中
</span></span></span><span class="line"><span class="cl">    <span class="k">register</span> <span class="n">uint64</span> <span class="n">a2</span> <span class="k">asm</span><span class="p">(</span><span class="s">&#34;a2&#34;</span><span class="p">)</span> <span class="o">=</span> <span class="n">arg2</span><span class="p">;</span>  <span class="c1">// 将 &#39;arg2&#39; 的值保存在寄存器 &#39;a2&#39; 中
</span></span></span><span class="line"><span class="cl">    <span class="k">register</span> <span class="n">uint64</span> <span class="n">a7</span> <span class="k">asm</span><span class="p">(</span><span class="s">&#34;a7&#34;</span><span class="p">)</span> <span class="o">=</span> <span class="n">which</span><span class="p">;</span> <span class="c1">// 将 &#39;which&#39; 的值保存在寄存器 &#39;a7&#39; 中
</span></span></span><span class="line"><span class="cl">    <span class="c1">// 内联汇编代码使用 ecall 指令进行系统调用
</span></span></span><span class="line"><span class="cl">    <span class="k">asm</span> <span class="k">volatile</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s">&#34;ecall&#34;</span>  <span class="c1">// 使用 ecall 指令进行系统调用
</span></span></span><span class="line"><span class="cl">        <span class="c1">// 在这段代码中，指令 &#34;ecall&#34; 的输入参数是寄存器 a0 a1 a2 和 a7，输出参数是寄存器 a0
</span></span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="s">&#34;=r&#34;</span><span class="p">(</span><span class="n">a0</span><span class="p">)</span>  <span class="c1">// 输出操作数：将返回值存储在变量 &#39;a0&#39; 中
</span></span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="s">&#34;r&#34;</span><span class="p">(</span><span class="n">a0</span><span class="p">),</span> <span class="s">&#34;r&#34;</span><span class="p">(</span><span class="n">a1</span><span class="p">),</span> <span class="s">&#34;r&#34;</span><span class="p">(</span><span class="n">a2</span><span class="p">),</span> <span class="s">&#34;r&#34;</span><span class="p">(</span><span class="n">a7</span><span class="p">)</span>  <span class="c1">// 输入操作数：传递参数和系统调用编号
</span></span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="s">&#34;memory&#34;</span>  <span class="c1">//  &#34;memory&#34; 标志告诉编译器，这条指令可能会修改内存中的数据，需要进行内存屏障操作来保证数据的正确性。 
</span></span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">a0</span><span class="p">;</span>  <span class="c1">// 返回存储在变量 &#39;a0&#39; 中的值
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>那么 OpenSBI 如何提供服务？在<code>include/sbi/sbi_ecall.h</code>这种定义了每个<code>ecall</code>服务全局变量。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">//include/sbi/sbi_ecall.h
</span></span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_legacy</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_time</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_rfence</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_ipi</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_vendor</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_hsm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_srst</span><span class="p">;</span>
</span></span></code></pre></div><p>在<code>lib/sbi/sbi_ecall.c</code>中注册了所有的<code>ecall</code>服务，并将其加到链表<code>ecall_exts_list</code>中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">sbi_ecall_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="o">*</span><span class="n">ext</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">sbi_ecall_exts_size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="n">ext</span> <span class="o">=</span> <span class="n">sbi_ecall_exts</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">		<span class="n">ret</span> <span class="o">=</span> <span class="nf">sbi_ecall_register_extension</span><span class="p">(</span><span class="n">ext</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">(</span><span class="n">ret</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">sbi_ecall_register_extension</span><span class="p">(</span><span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="o">*</span><span class="n">ext</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="o">*</span><span class="n">t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ext</span> <span class="o">||</span> <span class="p">(</span><span class="n">ext</span><span class="o">-&gt;</span><span class="n">extid_end</span> <span class="o">&lt;</span> <span class="n">ext</span><span class="o">-&gt;</span><span class="n">extid_start</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">ext</span><span class="o">-&gt;</span><span class="n">handle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="n">SBI_EINVAL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">sbi_list_for_each_entry</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ecall_exts_list</span><span class="p">,</span> <span class="n">head</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">start</span> <span class="o">=</span> <span class="n">t</span><span class="o">-&gt;</span><span class="n">extid_start</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">end</span> <span class="o">=</span> <span class="n">t</span><span class="o">-&gt;</span><span class="n">extid_end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">(</span><span class="n">end</span> <span class="o">&lt;</span> <span class="n">ext</span><span class="o">-&gt;</span><span class="n">extid_start</span> <span class="o">||</span> <span class="n">ext</span><span class="o">-&gt;</span><span class="n">extid_end</span> <span class="o">&lt;</span> <span class="n">start</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="cm">/* no overlap */</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="k">else</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="n">SBI_EINVAL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">SBI_INIT_LIST_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ext</span><span class="o">-&gt;</span><span class="n">head</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">sbi_list_add_tail</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ext</span><span class="o">-&gt;</span><span class="n">head</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ecall_exts_list</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Iterate over list of given type
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param pos the type * to use as a loop cursor.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param head the head for your list.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param member the name of the list_struct within the struct.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define sbi_list_for_each_entry(pos, head, member) \
</span></span></span><span class="line"><span class="cl"><span class="cp">	for (pos = sbi_list_entry((head)-&gt;next, typeof(*pos), member);	\
</span></span></span><span class="line"><span class="cl"><span class="cp">	     &amp;pos-&gt;member != (head); 	\
</span></span></span><span class="line"><span class="cl"><span class="cp">	     pos = sbi_list_entry(pos-&gt;member.next, typeof(*pos), member))
</span></span></span></code></pre></div><p>那么服务 id 如何和相对应的服务绑定的呢？以<code>ecall_time</code>为例，查看其结构体原型<code>struct sbi_ecall_extension</code> ：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// include/sbi/sbi_ecall.h: 23
</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">struct</span> <span class="n">sbi_dlist</span> <span class="n">head</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">extid_start</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">extid_end</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span> <span class="p">(</span><span class="o">*</span> <span class="n">probe</span><span class="p">)(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">extid</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">out_val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span> <span class="p">(</span><span class="o">*</span> <span class="n">handle</span><span class="p">)(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">extid</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">funcid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		       <span class="k">const</span> <span class="k">struct</span> <span class="n">sbi_trap_regs</span> <span class="o">*</span><span class="n">regs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		       <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">out_val</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		       <span class="k">struct</span> <span class="n">sbi_trap_info</span> <span class="o">*</span><span class="n">out_trap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>可以看到有 <code>extid_start</code>、<code>extid_end</code> 和 <code>handle</code>。</p>
<p>目前 OpenSBI 逐步将每个服务的实现都放在了<code>lib/sbi</code>单独文件中，以<code>ecall_time</code>为例，其实现在<code>lib/sbi/sbi_ecall_time.c</code>中。单独为其绑定回调处理函数<code>sbi_ecall_time_handler</code>。但是还有很多服务的实现还是放在了<code>lib/sbi/sbi_ecall_legacy.c</code>中，后续应该会逐步迁移。我们上文使用的<code>SBI_CONSOLE_PUTCHAR</code>服务就是在这里实现的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// lib/sbi/sbi_ecall_legacy.c
</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">sbi_ecall_extension</span> <span class="n">ecall_legacy</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="p">.</span><span class="n">extid_start</span> <span class="o">=</span> <span class="n">SBI_EXT_0_1_SET_TIMER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="p">.</span><span class="n">extid_end</span> <span class="o">=</span> <span class="n">SBI_EXT_0_1_SHUTDOWN</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="p">.</span><span class="n">handle</span> <span class="o">=</span> <span class="n">sbi_ecall_legacy_handler</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">sbi_ecall_legacy_handler</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">extid</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">funcid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">				    <span class="k">const</span> <span class="k">struct</span> <span class="n">sbi_trap_regs</span> <span class="o">*</span><span class="n">regs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">				    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">out_val</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">				    <span class="k">struct</span> <span class="n">sbi_trap_info</span> <span class="o">*</span><span class="n">out_trap</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">struct</span> <span class="n">sbi_tlb_info</span> <span class="n">tlb_info</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="n">u32</span> <span class="n">source_hart</span> <span class="o">=</span> <span class="nf">current_hartid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="n">ulong</span> <span class="n">hmask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">switch</span> <span class="p">(</span><span class="n">extid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="nl">SBI_EXT_0_1_SET_TIMER</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="nf">sbi_timer_event_start</span><span class="p">((</span><span class="n">u64</span><span class="p">)</span><span class="n">regs</span><span class="o">-&gt;</span><span class="n">a0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="nl">SBI_EXT_0_1_CONSOLE_PUTCHAR</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="nf">sbi_putc</span><span class="p">(</span><span class="n">regs</span><span class="o">-&gt;</span><span class="n">a0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="nl">SBI_EXT_0_1_CONSOLE_GETCHAR</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="n">ret</span> <span class="o">=</span> <span class="nf">sbi_getc</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">		<span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// ...
</span></span></span><span class="line"><span class="cl">	<span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这就把 <code>id</code> 与相应的服务函数绑定。一个<code>extid</code>对应一个<code>handler</code>。</p>
<p>我们可以在找到<code>SBI_EXT_0_1_CONSOLE_PUTCHAR</code>的值，是与 Linux 内核里定义的值是一致的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// include/sbi/sbi_ecall_interface.h
</span></span></span><span class="line"><span class="cl"><span class="cm">/* SBI Extension IDs */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define SBI_EXT_0_1_CONSOLE_PUTCHAR		0x1
</span></span></span></code></pre></div><h2 id="ecall-服务调用流程">ecall 服务调用流程</h2>
<ol>
<li>
<p>在 <code>firmware/fw_base.S</code> 中注册了 <code>Machine Mode</code> 的 <code>trap handler</code>，即 <code>sbi_trap_handler</code>；</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nl">_start_warm:</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Setup trap handler */</span>
</span></span><span class="line"><span class="cl">    <span class="nf">la</span>	<span class="no">a4</span><span class="p">,</span> <span class="no">_trap_handler</span>
</span></span><span class="line"><span class="cl">    <span class="nf">csrw</span>	<span class="no">CSR_MTVEC</span><span class="p">,</span> <span class="no">a4</span>  <span class="cm">/* CSR_MTVEC = _trap_handler */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">_trap_handler:</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TRAP_SAVE_AND_SETUP_SP_T0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">TRAP_SAVE_MEPC_MSTATUS</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">TRAP_CALL_C_ROUTINE</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">TRAP_RESTORE_MEPC_MSTATUS</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">TRAP_RESTORE_SP_T0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">mret</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="na">.macro</span>	<span class="no">TRAP_CALL_C_ROUTINE</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Call C routine */</span>
</span></span><span class="line"><span class="cl">    <span class="nf">add</span>	<span class="no">a0</span><span class="p">,</span> <span class="no">sp</span><span class="p">,</span> <span class="no">zero</span>
</span></span><span class="line"><span class="cl">    <span class="nf">call</span>	<span class="no">sbi_trap_handler</span>
</span></span><span class="line"><span class="cl"><span class="na">.endm</span>
</span></span></code></pre></div></li>
<li>
<p>在 <code>lib/sbi/sbi_trap.c</code> 中定义了 <code>sbi_trap_handler</code>，处理各种 <code>mcause</code>，比如 <code>Illegal Instructions</code>，<code>Misaligned Load &amp; Store</code>, <code>Supervisor &amp; Machine Ecall</code> 等。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// lib/sbi/sbi_trap.c
</span></span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">sbi_trap_handler</span><span class="p">(</span><span class="k">struct</span> <span class="n">sbi_trap_regs</span> <span class="o">*</span><span class="n">regs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">mcause</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">CAUSE_ILLEGAL_INSTRUCTION</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">rc</span>  <span class="o">=</span> <span class="nf">sbi_illegal_insn_handler</span><span class="p">(</span><span class="n">mtval</span><span class="p">,</span> <span class="n">regs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">CAUSE_MISALIGNED_LOAD</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">rc</span> <span class="o">=</span> <span class="nf">sbi_misaligned_load_handler</span><span class="p">(</span><span class="n">mtval</span><span class="p">,</span> <span class="n">mtval2</span><span class="p">,</span> <span class="n">mtinst</span><span class="p">,</span> <span class="n">regs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">CAUSE_MISALIGNED_STORE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">rc</span>  <span class="o">=</span> <span class="nf">sbi_misaligned_store_handler</span><span class="p">(</span><span class="n">mtval</span><span class="p">,</span> <span class="n">mtval2</span><span class="p">,</span> <span class="n">mtinst</span><span class="p">,</span> <span class="n">regs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">CAUSE_SUPERVISOR_ECALL</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">CAUSE_MACHINE_ECALL</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">rc</span>  <span class="o">=</span> <span class="nf">sbi_ecall_handler</span><span class="p">(</span><span class="n">regs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* If the trap came from S or U mode, redirect it there */</span>
</span></span><span class="line"><span class="cl">        <span class="n">trap</span><span class="p">.</span><span class="n">epc</span> <span class="o">=</span> <span class="n">regs</span><span class="o">-&gt;</span><span class="n">mepc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">trap</span><span class="p">.</span><span class="n">cause</span> <span class="o">=</span> <span class="n">mcause</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">trap</span><span class="p">.</span><span class="n">tval</span> <span class="o">=</span> <span class="n">mtval</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">trap</span><span class="p">.</span><span class="n">tval2</span> <span class="o">=</span> <span class="n">mtval2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">trap</span><span class="p">.</span><span class="n">tinst</span> <span class="o">=</span> <span class="n">mtinst</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">rc</span> <span class="o">=</span> <span class="nf">sbi_trap_redirect</span><span class="p">(</span><span class="n">regs</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">trap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span></code></pre></div></li>
<li>
<p>在 <code>lib/sbi/sbi_ecall.c</code> 中定义了处理 <code>ecall mcause</code> 的 <code>sbi_ecall_handler</code>，它遍历上面 <code>ecall_exts_list</code> 中注册的各种 <code>ecall</code> 服务。</p>
</li>
<li>
<p><code>sbi_ecall_handler</code> 根据 Linux 内核传递的 <code>ext (extension id)</code> 找到链表中对应的 <code>ecall</code> 服务，执行其中的 <code>handle</code> 函数，该函数根据 <code>fid</code> 执行具体的服务内容。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// lib/sbi/sbi_ecall.c
</span></span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">sbi_ecall_handler</span><span class="p">(</span><span class="k">struct</span> <span class="n">sbi_trap_regs</span> <span class="o">*</span><span class="n">regs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">extension_id</span> <span class="o">=</span> <span class="n">regs</span><span class="o">-&gt;</span><span class="n">a7</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">func_id</span> <span class="o">=</span> <span class="n">regs</span><span class="o">-&gt;</span><span class="n">a6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">sbi_trap_info</span> <span class="n">trap</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">out_val</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 遍历所有 ecall 服务
</span></span></span><span class="line"><span class="cl">    <span class="n">ext</span> <span class="o">=</span> <span class="nf">sbi_ecall_find_extension</span><span class="p">(</span><span class="n">extension_id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ext</span> <span class="o">&amp;&amp;</span> <span class="n">ext</span><span class="o">-&gt;</span><span class="n">handle</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果找到了就执行
</span></span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="n">ext</span><span class="o">-&gt;</span><span class="nf">handle</span><span class="p">(</span><span class="n">extension_id</span><span class="p">,</span> <span class="n">func_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="n">regs</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">out_val</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">trap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">extension_id</span> <span class="o">&gt;=</span> <span class="n">SBI_EXT_0_1_SET_TIMER</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">            <span class="n">extension_id</span> <span class="o">&lt;=</span> <span class="n">SBI_EXT_0_1_SHUTDOWN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">is_0_1_spec</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="n">SBI_ENOTSUPP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我们可以发现 <code>extension_id</code> 就是 a7 寄存器，他和我们在 uCore OS 中定义的 <code>SBI_EXT_0_1_CONSOLE_PUTCHAR</code> 是一致的。</p>
</li>
</ol>
<h1 id="程序的内存布局与编译流程">程序的内存布局与编译流程</h1>
<h2 id="程序的内存布局">程序的内存布局</h2>
<h1 id="ucore-的编译系统">uCore 的编译系统</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nf">.PHONY</span><span class="o">:</span> <span class="n">clean</span> <span class="n">build</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl"><span class="c"># 设置伪目标clean、build和user，可以通过命令make来执行这些目标
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">all</span><span class="o">:</span> <span class="n">build_kernel</span>
</span></span><span class="line"><span class="cl"><span class="c"># 默认目标为build_kernel，即执行build_kernel目标下的指令
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">LOG</span> <span class="o">?=</span> error
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量LOG，默认值是error
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">K</span> <span class="o">=</span> os
</span></span><span class="line"><span class="cl"><span class="nv">TOOLPREFIX</span> <span class="o">=</span> riscv64-unknown-elf-
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CC</span> <span class="o">=</span> <span class="k">$(</span>TOOLPREFIX<span class="k">)</span>gcc
</span></span><span class="line"><span class="cl"><span class="nv">AS</span> <span class="o">=</span> <span class="k">$(</span>TOOLPREFIX<span class="k">)</span>gcc
</span></span><span class="line"><span class="cl"><span class="nv">LD</span> <span class="o">=</span> <span class="k">$(</span>TOOLPREFIX<span class="k">)</span>ld
</span></span><span class="line"><span class="cl"><span class="nv">OBJCOPY</span> <span class="o">=</span> <span class="k">$(</span>TOOLPREFIX<span class="k">)</span>objcopy
</span></span><span class="line"><span class="cl"><span class="nv">OBJDUMP</span> <span class="o">=</span> <span class="k">$(</span>TOOLPREFIX<span class="k">)</span>objdump
</span></span><span class="line"><span class="cl"><span class="nv">PY</span> <span class="o">=</span> python3
</span></span><span class="line"><span class="cl"><span class="nv">GDB</span> <span class="o">=</span> <span class="k">$(</span>TOOLPREFIX<span class="k">)</span>gdb
</span></span><span class="line"><span class="cl"><span class="nv">CP</span> <span class="o">=</span> cp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">MKDIR_P</span> <span class="o">=</span> mkdir -p
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">BUILDDIR</span> <span class="o">=</span> build
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">C_SRCS</span> <span class="o">=</span> <span class="k">$(</span>wildcard <span class="nv">$K</span>/*.c<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量C_SRCS，使用wildcard函数匹配所有以.c为后缀的文件，并存储在$K目录下
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">AS_SRCS</span> <span class="o">=</span> <span class="k">$(</span>wildcard <span class="nv">$K</span>/*.S<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量AS_SRCS，使用wildcard函数匹配所有以.S为后缀的文件，并存储在$K目录下
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">C_OBJS</span> <span class="o">=</span> <span class="k">$(</span>addprefix <span class="k">$(</span>BUILDDIR<span class="k">)</span>/, <span class="k">$(</span>addsuffix .o, <span class="k">$(</span>basename <span class="k">$(</span>C_SRCS<span class="k">))))</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量C_OBJS，通过addprefix和addsuffix函数将$(C_SRCS)中的路径替换为$(BUILDDIR)，并将后缀修改为.o
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">AS_OBJS</span> <span class="o">=</span> <span class="k">$(</span>addprefix <span class="k">$(</span>BUILDDIR<span class="k">)</span>/, <span class="k">$(</span>addsuffix .o, <span class="k">$(</span>basename <span class="k">$(</span>AS_SRCS<span class="k">))))</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量AS_OBJS，通过addprefix和addsuffix函数将$(AS_SRCS)中的路径替换为$(BUILDDIR)，并将后缀修改为.o
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">OBJS</span> <span class="o">=</span> <span class="k">$(</span>C_OBJS<span class="k">)</span> <span class="k">$(</span>AS_OBJS<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量OBJS，其值为$(C_OBJS)和$(AS_OBJS)的组合
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">HEADER_DEP</span> <span class="o">=</span> <span class="k">$(</span>addsuffix .d, <span class="k">$(</span>basename <span class="k">$(</span>C_OBJS<span class="k">)))</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量HEADER_DEP，通过addsuffix函数将$(C_OBJS)中的后缀修改为.d
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">-include</span> <span class="k">$(</span><span class="nv">HEADER_DEP</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="c"># 包含$(HEADER_DEP)中的.d文件
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">=</span> -Wall -Werror -O -fno-omit-frame-pointer -ggdb
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量CFLAGS，并赋值为-Wall -Werror -O -fno-omit-frame-pointer -ggdb
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -MD
</span></span><span class="line"><span class="cl"><span class="c"># 将-MD选项追加到CFLAGS变量中，用于自动生成依赖关系文件
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -mcmodel<span class="o">=</span>medany
</span></span><span class="line"><span class="cl"><span class="c"># 将-mcmodel=medany选项追加到CFLAGS变量中，用于指定内存模型
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -ffreestanding -fno-common -nostdlib -mno-relax
</span></span><span class="line"><span class="cl"><span class="c"># 将-ffreestanding -fno-common -nostdlib -mno-relax选项追加到CFLAGS变量中，用于编译无操作系统环境下的程序
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -I<span class="nv">$K</span>
</span></span><span class="line"><span class="cl"><span class="c"># 将-I$K选项追加到CFLAGS变量中，用于指定头文件搜索路径为$K目录下
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> <span class="k">$(</span>shell <span class="k">$(</span>CC<span class="k">)</span> -fno-stack-protector -E -x c /dev/null &gt;/dev/null 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> -fno-stack-protector<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="c"># 将$(CC) -fno-stack-protector -E -x c /dev/null &gt;/dev/null 2&gt;&amp;1 &amp;&amp; echo -fno-stack-protector命令执行结果追加到CFLAGS变量中，用于禁用栈保护机制
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">ifeq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">LOG</span><span class="k">)</span><span class="err">,</span> <span class="err">error)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -D LOG_LEVEL_ERROR
</span></span><span class="line"><span class="cl"><span class="err">else</span> <span class="err">ifeq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">LOG</span><span class="k">)</span><span class="err">,</span> <span class="err">warn)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -D LOG_LEVEL_WARN
</span></span><span class="line"><span class="cl"><span class="err">else</span> <span class="err">ifeq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">LOG</span><span class="k">)</span><span class="err">,</span> <span class="err">info)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -D LOG_LEVEL_INFO
</span></span><span class="line"><span class="cl"><span class="err">else</span> <span class="err">ifeq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">LOG</span><span class="k">)</span><span class="err">,</span> <span class="err">debug)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -D LOG_LEVEL_DEBUG
</span></span><span class="line"><span class="cl"><span class="err">else</span> <span class="err">ifeq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">LOG</span><span class="k">)</span><span class="err">,</span> <span class="err">trace)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -D LOG_LEVEL_TRACE
</span></span><span class="line"><span class="cl"><span class="err">endif</span>
</span></span><span class="line"><span class="cl"><span class="c"># 根据$(LOG)变量的值，向CFLAGS变量追加相应的预处理器选项，相当于添加了一个宏定义，log.h中的LOG_LEVEL_ERROR等宏定义会根据这个宏定义来决定是否生效
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Disable PIE when possible (for Ubuntu 16.10 toolchain)
</span></span></span><span class="line"><span class="cl"><span class="err">ifneq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">shell</span> <span class="k">$(</span><span class="nv">CC</span><span class="k">)</span> -<span class="nv">dumpspecs</span> 2&gt;/<span class="nv">dev</span>/<span class="nv">null</span> | <span class="nv">grep</span> -<span class="nv">e</span> &#39;[^<span class="nv">f</span>]<span class="nv">no</span>-<span class="nv">pie</span>&#39;<span class="k">)</span><span class="err">,)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -fno-pie -no-pie
</span></span><span class="line"><span class="cl"><span class="err">endif</span>
</span></span><span class="line"><span class="cl"><span class="err">ifneq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">shell</span> <span class="k">$(</span><span class="nv">CC</span><span class="k">)</span> -<span class="nv">dumpspecs</span> 2&gt;/<span class="nv">dev</span>/<span class="nv">null</span> | <span class="nv">grep</span> -<span class="nv">e</span> &#39;[^<span class="nv">f</span>]<span class="nv">nopie</span>&#39;<span class="k">)</span><span class="err">,)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CFLAGS</span> <span class="o">+=</span> -fno-pie -nopie
</span></span><span class="line"><span class="cl"><span class="err">endif</span>
</span></span><span class="line"><span class="cl"><span class="c"># 根据系统环境判断是否支持PIE（位置无关执行）选项，并根据情况向CFLAGS变量追加相应的选项
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">LDFLAGS</span> <span class="o">=</span> -z max-page-size<span class="o">=</span><span class="m">4096</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个变量LDFLAGS，并赋值为-z max-page-size=4096
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">$(AS_OBJS)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">BUILDDIR</span><span class="k">)</span>/$<span class="n">K</span>/%.<span class="n">o</span> : $<span class="n">K</span>/%.<span class="n">S</span>
</span></span><span class="line"><span class="cl">    @mkdir -p <span class="k">$(</span>@D<span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">$(</span>CC<span class="k">)</span> <span class="k">$(</span>CFLAGS<span class="k">)</span> -c $&lt; -o <span class="nv">$@</span>
</span></span><span class="line"><span class="cl"><span class="c"># 规则：生成$(AS_OBJS)目标所需的依赖文件$(BUILDDIR)/$K/%.o，依赖于$K/%.S，并通过$(CC)命令编译生成目标文件
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">$(C_OBJS)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">BUILDDIR</span><span class="k">)</span>/$<span class="n">K</span>/%.<span class="n">o</span> : $<span class="n">K</span>/%.<span class="n">c</span>  <span class="k">$(</span><span class="nv">BUILDDIR</span><span class="k">)</span>/$<span class="n">K</span>/%.<span class="n">d</span>
</span></span><span class="line"><span class="cl">    @mkdir -p <span class="k">$(</span>@D<span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">$(</span>CC<span class="k">)</span> <span class="k">$(</span>CFLAGS<span class="k">)</span> -c $&lt; -o <span class="nv">$@</span>
</span></span><span class="line"><span class="cl"><span class="c"># 规则：生成$(C_OBJS)目标所需的依赖文件$(BUILDDIR)/$K/%.o，依赖于$K/%.c和$(BUILDDIR)/$K/%.d，并通过$(CC)命令编译生成目标文件
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">$(HEADER_DEP)</span><span class="o">:</span> <span class="k">$(</span><span class="nv">BUILDDIR</span><span class="k">)</span>/$<span class="n">K</span>/%.<span class="n">d</span> : $<span class="n">K</span>/%.<span class="n">c</span>
</span></span><span class="line"><span class="cl">    @mkdir -p <span class="k">$(</span>@D<span class="k">)</span>
</span></span><span class="line"><span class="cl">    @set -e<span class="p">;</span> rm -f <span class="nv">$@</span><span class="p">;</span> <span class="k">$(</span>CC<span class="k">)</span> -MM $&lt; <span class="k">$(</span>INCLUDEFLAGS<span class="k">)</span> &gt; <span class="nv">$@</span>.<span class="nv">$$$$</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        sed <span class="s1">&#39;s,\($*\)\.o[ :]*,\1.o $@ : ,g&#39;</span> &lt; <span class="nv">$@</span>.<span class="nv">$$$$</span> &gt; <span class="nv">$@</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">        rm -f <span class="nv">$@</span>.<span class="nv">$$$$</span>
</span></span><span class="line"><span class="cl"><span class="c"># 规则：生成$(HEADER_DEP)目标所需的依赖文件$(BUILDDIR)/$K/%.d，依赖于$K/%.c，并通过$(CC)命令生成依赖关系文件
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">build</span><span class="o">:</span> <span class="n">build</span>/<span class="n">kernel</span>
</span></span><span class="line"><span class="cl"><span class="c"># 定义一个目标build，其依赖于build/kernel
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">build/kernel</span><span class="o">:</span> <span class="k">$(</span><span class="nv">OBJS</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">$(</span>LD<span class="k">)</span> <span class="k">$(</span>LDFLAGS<span class="k">)</span> -T os/kernel.ld -o <span class="k">$(</span>BUILDDIR<span class="k">)</span>/kernel <span class="k">$(</span>OBJS<span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">$(</span>OBJDUMP<span class="k">)</span> -S <span class="k">$(</span>BUILDDIR<span class="k">)</span>/kernel &gt; <span class="k">$(</span>BUILDDIR<span class="k">)</span>/kernel.asm
</span></span><span class="line"><span class="cl">    <span class="k">$(</span>OBJDUMP<span class="k">)</span> -t <span class="k">$(</span>BUILDDIR<span class="k">)</span>/kernel <span class="p">|</span> sed <span class="s1">&#39;1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d&#39;</span> &gt; <span class="k">$(</span>BUILDDIR<span class="k">)</span>/kernel.sym
</span></span><span class="line"><span class="cl">    @echo <span class="s1">&#39;Build kernel done&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c"># 规则：生成build/kernel目标，依赖于$(OBJS)，通过$(LD)命令连接生成kernel，并通过$(OBJDUMP)命令生成汇编文件和符号表
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">clean</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    rm -rf <span class="k">$(</span>BUILDDIR<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># BOARD
</span></span></span><span class="line"><span class="cl"><span class="nv">BOARD</span>		<span class="o">?=</span> qemu
</span></span><span class="line"><span class="cl"><span class="nv">SBI</span>			<span class="o">?=</span> rustsbi
</span></span><span class="line"><span class="cl"><span class="nv">BOOTLOADER</span>	<span class="o">:=</span> ./bootloader/rustsbi-qemu.bin
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">QEMU</span> <span class="o">=</span> qemu-system-riscv64
</span></span><span class="line"><span class="cl"><span class="nv">QEMUOPTS</span> <span class="o">=</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">	-nographic <span class="se">\
</span></span></span><span class="line"><span class="cl">	-machine virt <span class="se">\
</span></span></span><span class="line"><span class="cl">	-bios <span class="k">$(</span>BOOTLOADER<span class="k">)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">	-kernel build/kernel	<span class="se">\
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">run</span><span class="o">:</span> <span class="n">build</span>/<span class="n">kernel</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>QEMU<span class="k">)</span> <span class="k">$(</span>QEMUOPTS<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># QEMU&#39;s gdb stub command line changed in 0.11
</span></span></span><span class="line"><span class="cl"><span class="nv">QEMUGDB</span> <span class="o">=</span> <span class="k">$(</span>shell <span class="k">if</span> <span class="k">$(</span>QEMU<span class="k">)</span> -help <span class="p">|</span> grep -q <span class="s1">&#39;^-gdb&#39;</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">	<span class="k">then</span> <span class="nb">echo</span> <span class="s2">&#34;-gdb tcp::15234&#34;</span><span class="p">;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">	<span class="k">else</span> <span class="nb">echo</span> <span class="s2">&#34;-s -p 15234&#34;</span><span class="p">;</span> <span class="k">fi</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 启动QEMU并通过GDB调试，此时QEMu会进入后台运行，并暂停执行，等待GDB连接
</span></span></span><span class="line"><span class="cl"><span class="c"># 连接的GDB端口为15234
</span></span></span><span class="line"><span class="cl"><span class="nf">debug</span><span class="o">:</span> <span class="n">build</span>/<span class="n">kernel</span> .<span class="n">gdbinit</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>QEMU<span class="k">)</span> <span class="k">$(</span>QEMUOPTS<span class="k">)</span> -S <span class="k">$(</span>QEMUGDB<span class="k">)</span> <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl">	sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>GDB<span class="k">)</span>
</span></span></code></pre></div><p>编译、运行 uCore 的一些常用命令有如下一些，涉及了后续章节中引入的测试用例中的命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make run
</span></span><span class="line"><span class="cl">make debug
</span></span><span class="line"><span class="cl">make clean
</span></span><span class="line"><span class="cl"><span class="c1"># 编译测试用例的前四章</span>
</span></span><span class="line"><span class="cl">make user <span class="nv">CHAPTER</span><span class="o">=</span><span class="m">4</span> <span class="nv">LOG</span><span class="o">=</span>trace
</span></span><span class="line"><span class="cl"><span class="c1"># 编译测试用例的第四章</span>
</span></span><span class="line"><span class="cl">make user <span class="nv">CHAPTER</span><span class="o">=</span>4_only <span class="nv">LOG</span><span class="o">=</span>trace
</span></span><span class="line"><span class="cl"><span class="c1"># 只运行测试用例的第四章</span>
</span></span><span class="line"><span class="cl">make <span class="nb">test</span> <span class="nv">CHAPTER</span><span class="o">=</span>4_only    
</span></span></code></pre></div><h1 id="附录">附录</h1>
<p>makefile 和 qemu</p>
<p>AS = $(TOOLPREFIX)gas  &gt; AS = $(TOOLPREFIX)as</p>
<h1 id="参考资料">参考资料</h1>
<ul>
<li><a href="https://www.jianshu.com/p/790fc612aaa5">终端颜色控制 - 简书</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>uCore 实验第 5 章 - 进程及进程管理</title>
      <link>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC5%E7%AB%A0-%E8%BF%9B%E7%A8%8B%E5%8F%8A%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86/</link>
      <pubDate>Fri, 08 Sep 2023 10:01:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC5%E7%AB%A0-%E8%BF%9B%E7%A8%8B%E5%8F%8A%E8%BF%9B%E7%A8%8B%E7%AE%A1%E7%90%86/</guid>
      <description>&lt;p&gt;首先，&lt;code&gt;.section .data&lt;/code&gt; 表示定义了一个数据段，在这个段中定义了一系列的全局变量。其中，&lt;code&gt;_app_num&lt;/code&gt; 是一个标签，表示一个 64 位的整数，初始值为 23。接下来是一系列的标签，分别代表了应用程序的起始地址，每个标签都是 64 位的整数。&lt;/p&gt;
&lt;p&gt;接着，&lt;code&gt;.section .data&lt;/code&gt; 后面又出现了一个标签 &lt;code&gt;_app_names&lt;/code&gt;，它是一个字符串数组，包含了一组字符串，分别命名为 &amp;ldquo;ch2b_exit&amp;rdquo;、&amp;ldquo;ch2b_hello_world&amp;rdquo;、&amp;ldquo;ch2b_power&amp;rdquo; 等等。这些字符串名字对应了前面定义的应用程序的起始地址。&lt;/p&gt;
&lt;p&gt;再往下，出现了一个标签 &lt;code&gt;INIT_PROC&lt;/code&gt;，它是一个字符串，表示初始化进程的名称，值为 &amp;ldquo;usershell&amp;rdquo;。&lt;/p&gt;
&lt;p&gt;之后，每个应用程序都有自己的标签和段名，比如 &lt;code&gt;app_0_start&lt;/code&gt;、&lt;code&gt;app_1_start&lt;/code&gt; 等等。每个标签都包含一个指令 &lt;code&gt;.incbin&lt;/code&gt;，它用于将一个二进制文件（以字符串形式指定文件路径）插入到当前段中。&lt;/p&gt;
&lt;h1 id=&#34;进程初始化分析&#34;&gt;进程初始化分析&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;fetch_task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 获取下一个要执行的进程
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;swtch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;curenv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nextenv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 切换到下一个进程上下文
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Per-process state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;procstate&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// 进程状态
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;               &lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// 进程 ID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;ustack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 进程用户栈虚拟地址 (用户页表)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 进程内核栈虚拟地址 (内核页表)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 进程中断帧
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 用于保存进程内核态的寄存器信息，进程切换时使用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;pagetable_t&lt;/span&gt;       &lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// User page table
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;max_page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;program_brk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;heap_bottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;     &lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Parent process
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;exit_code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;     &lt;span class=&#34;n&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FD_BUFFER_SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint32&lt;/span&gt;     &lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MAX_SYSCALL_NUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 系统调用次数统计
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;     &lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                     &lt;span class=&#34;c1&#34;&gt;// 进程开始运行时间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vma&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vmas&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NVMA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;                     &lt;span class=&#34;c1&#34;&gt;// 虚拟内存区域
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;wait-系统调用的功能&#34;&gt;wait 系统调用的功能&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;wait&lt;/code&gt; 系统调用是用于处理子进程终止状态的系统调用。其主要功能是等待子进程的终止，并获取子进程的退出状态信息。在操作系统中，当一个父进程创建了一个子进程后，通常会使用 &lt;code&gt;wait&lt;/code&gt; 来等待子进程的终止，以便进行后续的处理，如回收子进程的资源或获取其运行结果。&lt;/p&gt;
&lt;p&gt;以下是 &lt;code&gt;wait&lt;/code&gt; 系统调用的主要功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;等待子进程终止&lt;/strong&gt;：父进程调用 &lt;code&gt;wait&lt;/code&gt; 系统调用后，会进入阻塞状态，等待子进程终止。如果子进程已经终止，那么 &lt;code&gt;wait&lt;/code&gt; 立即返回，否则父进程会一直等待直到子进程终止。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;获取子进程的退出状态&lt;/strong&gt;：&lt;code&gt;wait&lt;/code&gt; 系统调用会获取子进程的退出状态信息，包括子进程的退出码（通常是一个整数）。这个退出码可以告诉父进程子进程的终止情况，例如是否成功执行等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;回收子进程资源&lt;/strong&gt;：一旦子进程终止，其占用的系统资源（如内存、文件描述符等）通常需要由父进程来回收，以避免资源泄漏。&lt;code&gt;wait&lt;/code&gt; 系统调用在等待子进程终止后，会自动回收这些资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;处理僵尸进程&lt;/strong&gt;：在某些情况下，子进程可能会在终止后成为僵尸进程，即已经终止但其进程描述符仍然存在。父进程可以使用 &lt;code&gt;wait&lt;/code&gt; 来回收这些僵尸进程，释放相关资源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;exec&lt;/code&gt;、&lt;code&gt;fork&lt;/code&gt; 和 &lt;code&gt;spawn&lt;/code&gt; 是操作系统中常见的进程管理系统调用，各自具有不同的功能和用途：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;exec&lt;/code&gt; 系统调用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：&lt;code&gt;exec&lt;/code&gt; 系统调用用于在当前进程的上下文中加载并执行一个新的程序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用途&lt;/strong&gt;：通常在一个进程需要替换自身的执行映像时使用。它会加载一个新的可执行文件，覆盖当前进程的地址空间和代码段，然后开始执行新的程序。这个新程序可以是完全不同的程序，从而允许进程动态切换到不同的应用程序，而不需要创建新的进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;fork&lt;/code&gt; 系统调用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：&lt;code&gt;fork&lt;/code&gt; 系统调用用于创建一个与当前进程几乎完全相同的新进程，包括代码、数据和上下文等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用途&lt;/strong&gt;：通常用于创建新的进程，新进程称为子进程，它从父进程继承了大部分状态，然后可以在独立的地址空间中执行不同的操作。&lt;code&gt;fork&lt;/code&gt; 创建的子进程是父进程的副本，可以并行执行不同的任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;spawn&lt;/code&gt; 系统调用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：&lt;code&gt;spawn&lt;/code&gt; 系统调用通常用于创建新的进程并执行指定的程序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用途&lt;/strong&gt;：类似于 &lt;code&gt;fork&lt;/code&gt;，它也创建了一个新的进程，但不像 &lt;code&gt;fork&lt;/code&gt; 那样完全复制父进程。相反，&lt;code&gt;spawn&lt;/code&gt; 允许你指定一个新程序的路径和参数，而不是完全复制当前进程的状态。这使得它更适合用于启动新程序，而不是简单地创建一个进程副本。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;总结：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;exec&lt;/code&gt; 用于替换当前进程的执行映像，允许加载和执行新程序。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fork&lt;/code&gt; 用于创建一个几乎与父进程相同的新进程，新进程成为父进程的副本。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spawn&lt;/code&gt; 通常用于创建一个新进程并执行指定的程序，允许指定不同的程序路径和参数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;附录&#34;&gt;附录&lt;/h1&gt;
&lt;p&gt;本章任务：
在次 -&amp;gt; 在此&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>首先，<code>.section .data</code> 表示定义了一个数据段，在这个段中定义了一系列的全局变量。其中，<code>_app_num</code> 是一个标签，表示一个 64 位的整数，初始值为 23。接下来是一系列的标签，分别代表了应用程序的起始地址，每个标签都是 64 位的整数。</p>
<p>接着，<code>.section .data</code> 后面又出现了一个标签 <code>_app_names</code>，它是一个字符串数组，包含了一组字符串，分别命名为 &ldquo;ch2b_exit&rdquo;、&ldquo;ch2b_hello_world&rdquo;、&ldquo;ch2b_power&rdquo; 等等。这些字符串名字对应了前面定义的应用程序的起始地址。</p>
<p>再往下，出现了一个标签 <code>INIT_PROC</code>，它是一个字符串，表示初始化进程的名称，值为 &ldquo;usershell&rdquo;。</p>
<p>之后，每个应用程序都有自己的标签和段名，比如 <code>app_0_start</code>、<code>app_1_start</code> 等等。每个标签都包含一个指令 <code>.incbin</code>，它用于将一个二进制文件（以字符串形式指定文件路径）插入到当前段中。</p>
<h1 id="进程初始化分析">进程初始化分析</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">scheduler</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nf">fetch_task</span><span class="p">()</span> <span class="c1">// 获取下一个要执行的进程
</span></span></span><span class="line"><span class="cl">    <span class="nf">swtch</span><span class="p">(</span><span class="o">&amp;</span><span class="n">curenv</span><span class="o">-&gt;</span><span class="n">context</span><span class="p">,</span> <span class="n">nextenv</span><span class="o">-&gt;</span><span class="n">context</span><span class="p">)</span> <span class="c1">// 切换到下一个进程上下文
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Per-process state
</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">proc</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">enum</span> <span class="n">procstate</span>    <span class="n">state</span><span class="p">;</span>     <span class="c1">// 进程状态
</span></span></span><span class="line"><span class="cl">    <span class="kt">int</span>               <span class="n">pid</span><span class="p">;</span>       <span class="c1">// 进程 ID
</span></span></span><span class="line"><span class="cl">    <span class="n">uint64</span>            <span class="n">ustack</span><span class="p">;</span>    <span class="c1">// 进程用户栈虚拟地址 (用户页表)
</span></span></span><span class="line"><span class="cl">    <span class="n">uint64</span>            <span class="n">kstack</span><span class="p">;</span>    <span class="c1">// 进程内核栈虚拟地址 (内核页表)
</span></span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">trapframe</span> <span class="o">*</span><span class="n">trapframe</span><span class="p">;</span> <span class="c1">// 进程中断帧
</span></span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">context</span>    <span class="n">context</span><span class="p">;</span> <span class="c1">// 用于保存进程内核态的寄存器信息，进程切换时使用
</span></span></span><span class="line"><span class="cl">    <span class="kt">pagetable_t</span>       <span class="n">pagetable</span><span class="p">;</span> <span class="c1">// User page table
</span></span></span><span class="line"><span class="cl">    <span class="n">uint64</span>            <span class="n">max_page</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint64</span>            <span class="n">program_brk</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint64</span>            <span class="n">heap_bottom</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span>     <span class="n">parent</span><span class="p">;</span> <span class="c1">// Parent process
</span></span></span><span class="line"><span class="cl">    <span class="n">uint64</span>            <span class="n">exit_code</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">file</span> <span class="o">*</span>     <span class="n">files</span><span class="p">[</span><span class="n">FD_BUFFER_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint32</span>     <span class="n">syscall_times</span><span class="p">[</span><span class="n">MAX_SYSCALL_NUM</span><span class="p">];</span> <span class="c1">// 系统调用次数统计
</span></span></span><span class="line"><span class="cl">    <span class="n">uint64</span>     <span class="n">start_time</span><span class="p">;</span>                     <span class="c1">// 进程开始运行时间
</span></span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">vma</span> <span class="n">vmas</span><span class="p">[</span><span class="n">NVMA</span><span class="p">];</span>                     <span class="c1">// 虚拟内存区域
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="wait-系统调用的功能">wait 系统调用的功能</h2>
<p><code>wait</code> 系统调用是用于处理子进程终止状态的系统调用。其主要功能是等待子进程的终止，并获取子进程的退出状态信息。在操作系统中，当一个父进程创建了一个子进程后，通常会使用 <code>wait</code> 来等待子进程的终止，以便进行后续的处理，如回收子进程的资源或获取其运行结果。</p>
<p>以下是 <code>wait</code> 系统调用的主要功能：</p>
<ol>
<li>
<p><strong>等待子进程终止</strong>：父进程调用 <code>wait</code> 系统调用后，会进入阻塞状态，等待子进程终止。如果子进程已经终止，那么 <code>wait</code> 立即返回，否则父进程会一直等待直到子进程终止。</p>
</li>
<li>
<p><strong>获取子进程的退出状态</strong>：<code>wait</code> 系统调用会获取子进程的退出状态信息，包括子进程的退出码（通常是一个整数）。这个退出码可以告诉父进程子进程的终止情况，例如是否成功执行等。</p>
</li>
<li>
<p><strong>回收子进程资源</strong>：一旦子进程终止，其占用的系统资源（如内存、文件描述符等）通常需要由父进程来回收，以避免资源泄漏。<code>wait</code> 系统调用在等待子进程终止后，会自动回收这些资源。</p>
</li>
<li>
<p><strong>处理僵尸进程</strong>：在某些情况下，子进程可能会在终止后成为僵尸进程，即已经终止但其进程描述符仍然存在。父进程可以使用 <code>wait</code> 来回收这些僵尸进程，释放相关资源。</p>
</li>
</ol>
<p><code>exec</code>、<code>fork</code> 和 <code>spawn</code> 是操作系统中常见的进程管理系统调用，各自具有不同的功能和用途：</p>
<ol>
<li>
<p><strong><code>exec</code> 系统调用</strong>：</p>
<ul>
<li><strong>功能</strong>：<code>exec</code> 系统调用用于在当前进程的上下文中加载并执行一个新的程序。</li>
<li><strong>用途</strong>：通常在一个进程需要替换自身的执行映像时使用。它会加载一个新的可执行文件，覆盖当前进程的地址空间和代码段，然后开始执行新的程序。这个新程序可以是完全不同的程序，从而允许进程动态切换到不同的应用程序，而不需要创建新的进程。</li>
</ul>
</li>
<li>
<p><strong><code>fork</code> 系统调用</strong>：</p>
<ul>
<li><strong>功能</strong>：<code>fork</code> 系统调用用于创建一个与当前进程几乎完全相同的新进程，包括代码、数据和上下文等。</li>
<li><strong>用途</strong>：通常用于创建新的进程，新进程称为子进程，它从父进程继承了大部分状态，然后可以在独立的地址空间中执行不同的操作。<code>fork</code> 创建的子进程是父进程的副本，可以并行执行不同的任务。</li>
</ul>
</li>
<li>
<p><strong><code>spawn</code> 系统调用</strong>：</p>
<ul>
<li><strong>功能</strong>：<code>spawn</code> 系统调用通常用于创建新的进程并执行指定的程序。</li>
<li><strong>用途</strong>：类似于 <code>fork</code>，它也创建了一个新的进程，但不像 <code>fork</code> 那样完全复制父进程。相反，<code>spawn</code> 允许你指定一个新程序的路径和参数，而不是完全复制当前进程的状态。这使得它更适合用于启动新程序，而不是简单地创建一个进程副本。</li>
</ul>
</li>
</ol>
<p>总结：</p>
<ul>
<li><code>exec</code> 用于替换当前进程的执行映像，允许加载和执行新程序。</li>
<li><code>fork</code> 用于创建一个几乎与父进程相同的新进程，新进程成为父进程的副本。</li>
<li><code>spawn</code> 通常用于创建一个新进程并执行指定的程序，允许指定不同的程序路径和参数。</li>
</ul>
<h1 id="附录">附录</h1>
<p>本章任务：
在次 -&gt; 在此</p>
]]></content:encoded>
    </item>
    <item>
      <title>uCore 实验第 4 章 - 地址空间</title>
      <link>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC4%E7%AB%A0-%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4/</link>
      <pubDate>Mon, 04 Sep 2023 11:11:48 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC4%E7%AB%A0-%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;为何指定 TRAMPOLINE 和 TRAPFRAME 在 va 的最高位？
TRAMPOLINE 和 TRAPFRAME 被定义在最高的虚拟内存地址上，是因为它们在操作系统的内存布局中起着重要作用。
TRAMPOLINE 被用作从用户模式切换到内核模式的跳转目标。当发生异常或中断时，处理器将从用户模式切换到内核模式，并将控制权转移到内核中预定义的位置，也就是陷阱处理程序。TRAMPOLINE 页面被映射到最高虚拟地址，以便处理器能够在这个转换过程中方便地引用它。通过将其放置在最高地址，确保了无论系统的具体内存布局如何，它始终是可访问的。
另一方面，TRAPFRAME 用于在发生异常或中断时存储机器状态。它包含寄存器、标志和其他操作系统处理异常所需的信息。TRAPFRAME 也被放置在最高的虚拟地址上，以确保它易于访问，并且陷阱处理程序可以高效地访问它。
通过将 TRAMPOLINE 和 TRAPFRAME 定义在最高的虚拟内存地址上，内核可以方便而可靠地处理异常和中断，而无需关心它们在内存中的特定位置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;如何确定分页方案---satp&#34;&gt;如何确定分页方案 - satp&lt;/h1&gt;
&lt;p&gt;在 MMU 没有使能的情况下，虚拟地址和物理地址是相同的。在 MMU 使能的情况下，虚拟地址会被转换成物理地址。这个转换过程是由操作系统来管理的，操作系统需要维护一个数据结构来记录虚拟地址和物理地址的映射关系。这个数据结构就是页表。&lt;/p&gt;
&lt;p&gt;转换的过程需要分页机制，分页机制有多种。RISC-V 的分页方案以 SvX 的模式命名，其中 X 是以位为单位的&lt;strong&gt;虚拟地址的长度&lt;/strong&gt;。在 RV64 架构下，RISC-V 支持多种分页方案，包括 Sv39，Sv48，Sv57 以及 Sv64。Sv39 最大支持 39 位的虚拟地址，这意味着它可以支持 512 GB 的虚拟地址空间。Sv48 最大支持 48 位的虚拟地址，这意味着它可以支持 256 TB 的虚拟地址空间。我们将在本章中实现 Sv39 分页方案。&lt;/p&gt;
&lt;p&gt;如何开启分页机制呢？RISC-V 的分页机制是通过 satp（Supervisor address translation and protection）寄存器来开启的。satp 寄存器字段分布如下：&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//2023/09/05/5d1ec6e9adaf743f7c9abc177cd12eb1.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/05/5d1ec6e9adaf743f7c9abc177cd12eb1.png&#34; alt=&#34;&#34;  title=&#34;RV64 架构下的 satp 寄存器&#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;ul&gt;
&lt;li&gt;Mode 字段可以决定是否开启分页以及分页级数。Mode=0 时，不开启分页；Mode=8 时，开启 Sv39 分页机制。&lt;/li&gt;
&lt;li&gt;ASID（Address Space Identifier，地址空间标识符）域是可选的，它可以用来降低上下文切换的开销。目前我们暂不考虑这个字段的作用。&lt;/li&gt;
&lt;li&gt;PPN（Physical Page Number，物理页号），保存了根页表的物理地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;sv39-多级页表机制&#34;&gt;SV39 多级页表机制&lt;/h1&gt;
&lt;h2 id=&#34;页表项描述&#34;&gt;页表项描述&lt;/h2&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//2023/09/05/86e06238c562bdd238e868fcd819df3c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/05/86e06238c562bdd238e868fcd819df3c.png&#34; alt=&#34;&#34;  title=&#34;Sv39 页表项&#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;Sv39 页表项（page-table entry，PTE）的布局，从左到右分别包含如下所述的域：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;V 位决定了该页表项的其余部分是否有效 (V=1 时有效)。若 V=0，则任何遍历到此页表项的虚址转换操作都会导致页错误。&lt;/li&gt;
&lt;li&gt;R、W 和 X 位分别表示此页是否可以读取、写入和执行。如果这三个位都是 0，那么这个页表项是指向下一级页表的指针，否则它是页表树的一个叶节点。&lt;/li&gt;
&lt;li&gt;U 位表示该页是否是用户页面。若 U=0，则 U 模式不能访问此页面，但 S 模式可以。若 U=1，则 U 模式下能访问这个页面，而 S 模式不能。&lt;/li&gt;
&lt;li&gt;G 位表示这个映射是否对所有虚址空间有效，硬件可以用这个信息来提高地址转换的性能。这一位通常只用于属于操作系统的页面。&lt;/li&gt;
&lt;li&gt;A 位表示自从上次 A 位被清除以来，该页面是否被访问过。&lt;/li&gt;
&lt;li&gt;D 位表示自从上次清除 D 位以来页面是否被弄脏（例如被写入）。&lt;/li&gt;
&lt;li&gt;RSW 域留给操作系统使用，它会被硬件忽略。&lt;/li&gt;
&lt;li&gt;PPN 域包含物理页号，这是物理地址的一部分。若这个页表项是一个叶节点，那么 PPN 是转换后物理地址的一部分。否则 PPN 给出下一节页表的地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;虚拟地址转换物理地址过程&#34;&gt;虚拟地址转换物理地址过程&lt;/h2&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//2023/09/05/48e6ce48ffb827a10371344ad07324c2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/05/48e6ce48ffb827a10371344ad07324c2.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;当 satp 寄存器中开启分页时，S 模式和 U 模式中访存的地址都会被视为虚拟地址，需要将其转换为物理地址。虚拟地址转换物理地址的过程如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从 satp 寄存器中读取 PPN，得到根页表的物理地址，为了表述方便，我们将其记做三级页表基地址 satp.PPN；&lt;/li&gt;
&lt;li&gt;从虚拟地址中取出三级虚拟页号 L2&lt;/li&gt;
&lt;li&gt;处理器会读取地址位于 satp.PPN * 4096 + L2 * 4 的页表项，得到下一级页表的基地址 L1.PPN；&lt;/li&gt;
&lt;li&gt;从虚拟地址中取出二级虚拟页号 L1&lt;/li&gt;
&lt;li&gt;处理器会读取地址位于 L1.PPN * 4096 + L1 * 4 的页表项，得到下一级页表的基地址 L0.PPN；&lt;/li&gt;
&lt;li&gt;从虚拟地址中取出一级虚拟页号 L0&lt;/li&gt;
&lt;li&gt;处理器会读取地址位于 L0.PPN * 4096 + L0 * 4 的页表项，得到物理页号 PPN；&lt;/li&gt;
&lt;li&gt;将 PPN 和虚拟地址的低 12 位也就是 Offset 拼接起来，得到物理地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们看代码中是如何实现的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define PTE2PA(pte) (((pte) &amp;gt;&amp;gt; 10) &amp;lt;&amp;lt; 12)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 从虚拟地址中提取三个 9 位的页表索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define PXMASK 0x1FF &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 9
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// PGSHIFT = 12，这段宏定义用于定位 VPNx 的位置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define PXSHIFT(level) (PGSHIFT + (9 * (level)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 从虚拟地址 VA 中提取出第 level 级页表的索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define PX(level, va) ((((uint64)(va)) &amp;gt;&amp;gt; PXSHIFT(level)) &amp;amp; PXMASK)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面这三个工具宏可以用来提取虚拟页号 VPN。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 返回页表 pagetable 中与虚拟地址 va 对应的 PTE 的地址。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 如果 alloc != 0，则创建所需的页表页。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// RISC-V Sv39 方案有三级页表页。一个页表页包含 512 个 64 位的 PTEs。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 一个 64 位的虚拟地址被分为五个字段：
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   39..63 -- 必须为零。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   30..38 -- 2 级索引的 9 位。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   21..29 -- 1 级索引的 9 位。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//   12..20 -- 0 级索引的 9 位。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//    0..11 -- 页面内的 12 位字节偏移量。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// pagetable 页表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// va 虚拟地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// alloc 页表项不存在时是否分配
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;pte_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;walk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;pagetable_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;va&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;alloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;va&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MAXVA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;panic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;walk&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;level&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;level&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;pte_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;PX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;va&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 通过 PTE 的标志位判断每一级的 pte 是否是有效的（V 位）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PTE_V&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;pagetable_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;PTE2PA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 如果该项无效且 alloc 标志被设置，则分配一个新的页表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 如果 alloc 参数=0 或者已经没有空闲的内存了，那么遇到中途 V=0 的 pte 整个 walk 过程就会直接退出
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alloc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;pde_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;kalloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 清空分配的页表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;memset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PGSIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// 更新页表项，将其指向新分配的页表，并设置有效位 PTE_V
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;PA2PTE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PTE_V&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 返回最低级和虚拟地址的页表项，不是返回物理地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;PX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;va&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每次从虚拟地址 va 中提取出一个虚拟页号，然后根据这个虚拟页号从页表中取出下一级页表的基地址。如果这个页表项无效，那么根据 alloc 参数决定是否分配一个新的页表。如果 alloc 参数为 0 或者已经没有空闲的内存了，那么遇到中途 V=0 的 pte 整个 walk 过程就会直接退出。如果 alloc 参数为 1，那么就会分配一个新的页表，然后将这个页表项指向新分配的页表，并设置有效位 PTE_V。&lt;/p&gt;
&lt;p&gt;我们可以发现 walk 返回的结果不是物理地址，而是页表项的地址。这是因为 walk 函数的作用是将虚拟地址转换为物理地址，而页表项中的 PPN 只是物理地址的一部分，&lt;strong&gt;还需要加上虚拟地址的低 12 位偏移量才能得到物理地址&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;如何建立页表&#34;&gt;如何建立页表&lt;/h2&gt;
&lt;p&gt;前面的过程实际上是以用户的角度来考虑的，也就是给你一个虚拟地址按照分页的规则将其转化成物理地址就能访问了。但是作为一个操作系统，我们还需要多考虑一下，页表是哪来的？我们知道从虚拟地址中去获取页表地址，但是&lt;strong&gt;页表的内容是哪来的呢&lt;/strong&gt;？页表是如何建立起来的呢？这些是需要操作系统来完成的。&lt;/p&gt;
&lt;p&gt;建立页表也就是建立虚拟地址到物理地址的映射关系。也就是给你一个虚拟地址，你需要告诉我如何查到物理地址，实际上这个过程就是建立页表的过程。这个过程也是通过 walk 函数来完成的，从上文我们知道如果页表都建好的情况下 walk 就是不断查页表的过程，那么在没有页表的情况下，walk 还可以建立一个个页表。稍有不同的是，walk 返回的是最后一级页表项的地址，我们需要将物理地址写入这个页表项中。&lt;/p&gt;
&lt;p&gt;在 uCore 中使用 mappages 函数封装了 walk 函数，具体如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define PA2PTE(pa) ((((uint64)pa) &amp;gt;&amp;gt; 12) &amp;lt;&amp;lt; 10)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 为从虚拟地址 va 开始的页面创建指向物理地址 pa 开始的页表项（PTE）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 注意：va 和 size 可能不是页面对齐的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 如果无法分配所需的页表，则返回 0，否则返回 -1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param pagetable 根页表地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param va        虚拟地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param size      映射的字节数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param pa        物理地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @param perm      权限位
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @return          成功返回 0，否则返回 -1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mappages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;pagetable_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;va&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;perm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;virtualAddress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lastVirtualAddress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;pte_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 地址必须是页面对齐的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;virtualAddress&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;PGROUNDDOWN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;va&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;lastVirtualAddress&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;PGROUNDDOWN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;va&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(;;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 返回最低级的虚拟地址的页表项，如果不存在会创建一个新的页表项
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 页表项可能会因为内存不足创建失败，如果创建失败，则返回 -1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;walk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pagetable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;virtualAddress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 如果 PTE 已经有效，则输出错误信息并返回 -1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PTE_V&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;remap&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 将物理地址 pa 转换为页表项，并设置权限位 perm 和 有效位 PTE_V
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pte&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;PA2PTE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;perm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PTE_V&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// 如果当前是最后一个地址，则结束循环
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;virtualAddress&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lastVirtualAddress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;virtualAddress&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PGSIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;pa&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PGSIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;问答作业&#34;&gt;问答作业&lt;/h1&gt;
&lt;h2 id=&#34;请列举-sv39-页表页表项的组成结合课堂内容描述其中的标志位有何作用潜在作用&#34;&gt;请列举 SV39 页表页表项的组成，结合课堂内容，描述其中的标志位有何作用／潜在作用？&lt;/h2&gt;
&lt;p&gt;Sv39 页表页表项的组成如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;有效位 (V)&lt;/strong&gt;：这是页表项的最高位，用于指示页表项是否有效。如果有效位设置为 1，表示页表项有效，可以使用；如果设置为 0，表示页表项无效，禁止使用。这是虚拟内存中页表项的基本有效性标志。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;写入位 (W)&lt;/strong&gt;：这个标志位用于指示是否可以对此页进行写入操作。如果设置为 1，表示允许写入；如果设置为 0，表示禁止写入。它是页表项的访问权限控制标志之一。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户位 (U)&lt;/strong&gt;：用户位用于指示是否允许用户态程序访问此页。如果设置为 1，表示允许用户态访问；如果设置为 0，表示只允许内核态访问。它是页表项的访问权限控制标志之一。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行位 (X)&lt;/strong&gt;：执行位用于指示是否允许执行此页上的指令。如果设置为 1，表示允许执行；如果设置为 0，表示禁止执行。它也是页表项的访问权限控制标志之一。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局位 (G)&lt;/strong&gt;：全局位用于指示此页是否是全局的，即无需 TLB 缓存，通常用于内核页。如果设置为 1，表示是全局的；如果设置为 0，表示不是全局的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;已访问位 (A)&lt;/strong&gt;：已访问位表示是否已经访问过此页，通常由硬件设置。操作系统可以用它来实现页面置换算法，如 LRU。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;已修改位 (D)&lt;/strong&gt;：已修改位表示是否已经对此页进行了写入操作。与已访问位类似，操作系统可以用它来实现页面置换算法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;物理页框地址 (PPN)&lt;/strong&gt;：这是页表项中存储的物理页框的地址。它指示了虚拟页到物理页的映射关系。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Sv39 页表的页表项标志位允许操作系统和硬件实现对虚拟内存的细粒度控制和保护。不同的标志位组合可以实现不同级别的内存保护和权限控制，从而提高系统的安全性和可用性。例如，有效位、写入位、用户位和执行位的不同组合可以实现不同级别的内存保护，使操作系统可以将不同的内存区域分配给用户态和内核态，并设置不同的权限。已访问位和已修改位则用于实现页面置换算法，帮助操作系统决定哪些页面应该被置换出去，以优化内存利用率。全局位可以用于标识全局共享的页，从而节省 TLB 缓存空间。物理页框地址是页表项的核心，它建立了虚拟地址到物理地址的映射关系，使虚拟内存管理成为可能。&lt;/p&gt;
&lt;h2 id=&#34;缺页相关问题&#34;&gt;缺页相关问题&lt;/h2&gt;
&lt;h3 id=&#34;请问哪些异常可能是缺页导致的&#34;&gt;请问哪些异常可能是缺页导致的？&lt;/h3&gt;
&lt;p&gt;缺页异常是由于进程访问的页面不在页表中或者在页表中无效而引发的异常。以下这些异常可能是因为缺页导致的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Load Page Fault（Load 异常）：当进程试图读取一个不在页表中或者无效的页面时，会引发 Load Page Fault 异常。在 RISC-V 中，这个异常对应的异常代码是 5。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Store Page Fault（Store 异常）：当进程试图写入一个不在页表中或者无效的页面时，会引发 Store Page Fault 异常。在 RISC-V 中，这个异常对应的异常代码是 7。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Instruction Page Fault（指令页异常）：当进程试图执行一个不在页表中或者无效的页面上的指令时，会引发 Instruction Page Fault 异常。在 RISC-V 中，这个异常对应的异常代码是 12。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;发生缺页时描述相关的重要寄存器的值lab2-中描述过的可以简单点&#34;&gt;发生缺页时，描述相关的重要寄存器的值（lab2 中描述过的可以简单点）。&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;sepc（Exception Program Counter）：trap 发生时会将当前指令的下一条指令地址写入其中，用于 trap 处理完成后返回。&lt;/li&gt;
&lt;li&gt;stval（Machine Trap Value）：mtval 寄存器包含导致异常的原因，即导致异常的指令的具体信息。例如，如果是缺页异常，那么 mtval 寄存器包含导致缺页异常的虚拟地址。&lt;/li&gt;
&lt;li&gt;scause: 中断/异常发生时， CSR 寄存器 scause 中会记录其信息， Interrupt 位记录是中断还是异常， Exception Code 记录中断/异常的种类。&lt;/li&gt;
&lt;li&gt;sstatus: 记录处理器当前状态，其中 SPP 段记录当前特权等级。&lt;/li&gt;
&lt;li&gt;stvec: 记录处理 trap 的入口地址，现有两种模式  Direct 和 Vectored 。&lt;/li&gt;
&lt;li&gt;sscratch: 其中的值是指向hart相关的S态上下文的指针，比如内核栈的指针。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;以下行为的好处&#34;&gt;以下行为的好处？&lt;/h3&gt;
&lt;p&gt;缺页有两个常见的原因，其一是 Lazy 策略，也就是直到内存页面被访问才实际进行页表操作。比如，一个程序被执行时，进程的代码段理论上需要从磁盘加载到内存。但是 os 并不会马上这样做，而是会保存 .text 段在磁盘的位置信息，在这些代码第一次被执行时才完成从磁盘的加载操作。&lt;/p&gt;
&lt;p&gt;Lazy Loading 策略有以下好处：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;减少初始化开销&lt;/strong&gt;：Lazy Loading 允许操作系统在程序启动时只加载必需的页面，而不是一次性加载整个程序。这可以减少启动时间和初始化开销，因为不需要将整个程序加载到内存中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;节省内存&lt;/strong&gt;：Lazy Loading 策略避免了不必要的内存占用。如果程序的某些部分从不被访问，那么它们就不会被加载到内存中，从而节省了内存资源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提高响应速度&lt;/strong&gt;：通过仅在需要时加载页面，Lazy Loading 可以提高系统的响应速度。只有当程序访问某个页面时，操作系统才会执行磁盘加载操作，而不会在程序启动时浪费时间加载可能永远不会被访问的内容。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更好的磁盘利用率&lt;/strong&gt;：Lazy Loading 允许操作系统将程序的不同部分分散在磁盘上，根据需要加载。这可以提高磁盘利用率，因为不需要在磁盘上为整个程序分配连续的空间。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;请问处理-10g-连续的内存页面需要操作的页表实际大致占用多少内存-给出数量级即可&#34;&gt;请问处理 10G 连续的内存页面，需要操作的页表实际大致占用多少内存 (给出数量级即可)？&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;此外 COW(Copy On Write) 也是常见的容易导致缺页的 Lazy 策略，这个之后再说。其实，我们的 mmap 也可以采取 Lazy 策略，比如：一个用户进程先后申请了 10G 的内存空间，然后用了其中 1M 就直接退出了。按照现在的做法，我们显然亏大了，进行了很多没有意义的页表操作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;处理 10GB 连续的内存页面所需的页表实际上占用的内存量取决于操作系统的页表结构和管理策略。在 RISC-V 的页表结构中，一个页表项（Page Table Entry，PTE）通常占据 8 字节（64 位系统），其中包括物理页框号和一些标志位。让我们假设一个 PTE 占用 8 字节。&lt;/p&gt;
&lt;p&gt;为了估算 10GB 连续内存页面所需的页表实际占用内存量，我们可以按照以下步骤进行计算：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先，将 10GB 转换为字节数。1GB 等于 1,073,741,824 字节，所以 10GB 等于 10 * 1,073,741,824 = 10,737,418,240 字节。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后，计算每个页面表项覆盖的内存范围。假设每个页面表项管理 4KB（4 * 1024 字节）的内存页面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;计算需要多少个页面表项来管理 10GB 的内存。这可以通过将 10GB 除以每个页面表项管理的内存范围来实现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最后，将所需的页面表项数量乘以每个 PTE 的大小来估算所需的总内存量。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;让我们进行具体计算：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内存大小：10,737,418,240 字节&lt;/li&gt;
&lt;li&gt;每个页面表项管理的内存范围：4KB = 4 * 1024 字节&lt;/li&gt;
&lt;li&gt;需要的页面表项数量：10,737,418,240 字节 / 4KB = 2,621,440 个页表项&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;假设每个页表项占用 8 字节，则需要的总内存量为：&lt;/p&gt;
&lt;p&gt;2,621,440 个页表项 * 8 字节/页表项 = 20,971,520 字节&lt;/p&gt;
&lt;p&gt;所以，处理 10GB 连续的内存页面所需的页表实际占用内存量约为 20,971,520 字节，或者大约 20MB。这只是一个估算，实际内存占用可能会因操作系统的管理策略和对齐等因素而有所不同。&lt;/p&gt;
&lt;h3 id=&#34;请简单思考如何才能在现有框架基础上实现-lazy-策略缺页时又如何处理描述合理即可不需要考虑实现&#34;&gt;请简单思考如何才能在现有框架基础上实现 Lazy 策略，缺页时又如何处理？描述合理即可，不需要考虑实现。&lt;/h3&gt;
&lt;p&gt;要在现有框架基础上实现 Lazy 策略，可以采取以下简单思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;延迟加载（Lazy Loading）&lt;/strong&gt;：在用户进程请求内存映射时，不立即将整个内存区域加载到物理内存中。而是仅创建虚拟内存映射和页表项，记录对应的磁盘位置等信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;缺页处理（Page Fault Handling）&lt;/strong&gt;：当用户进程访问虚拟内存中的某个尚未加载的内存页面时，会触发缺页异常。在缺页异常处理程序中，操作系统会根据页表中的磁盘位置信息，将相应的磁盘数据加载到物理内存中，并更新页表项，使其指向新加载的物理页面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;惰性加载（Demand Paging）&lt;/strong&gt;：为了提高性能，可以采用惰性加载策略，即只加载实际被访问的内存页面，而不是一次性加载整个区域。这可以通过在缺页处理程序中进行懒加载操作来实现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;内存回收（Memory Reclamation）&lt;/strong&gt;：当系统内存不足时，操作系统可以选择回收一些不常访问的内存页面，将其写回磁盘，并更新页表项为无效。这需要根据页面访问模式和策略来确定哪些页面可以被回收。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能优化&lt;/strong&gt;：为了提高性能，可以采用预读取（Prefetching）策略，即在缺页处理时，不仅加载当前访问的页面，还预先加载相邻的页面，以减少未来可能的缺页次数。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;此时页面失效如何表现在页表项-pte-上&#34;&gt;此时页面失效如何表现在页表项 (PTE) 上？&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;缺页的另一个常见原因是 swap 策略，也就是内存页面可能被换到磁盘上了，导致对应页面失效。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Dirty bit (D 位)：当页面被修改并且尚未写回到主存时，该位会被设置为 1。如果页面已经被换出到磁盘上，D 位将保持为 1，以指示页面数据已过期。&lt;/p&gt;
&lt;p&gt;Valid bit (V 位)：当页面在主存中有效时，V 位被设置为 1。如果页面被换出到磁盘上，V 位将被清除为 0，表示该页无效。&lt;/p&gt;
&lt;p&gt;通过检查页表项的 D 位和 V 位，操作系统可以确定页面是否需要从磁盘重新加载到内存中。如果 D 位为 1，说明页面需要写回到主存，在将其置为有效之前，必须将页数据从磁盘读取到内存中。如果 V 位为 0，说明页面当前无效，需要将其从磁盘加载到内存中，并将 V 位设置为 1，表示页面有效。&lt;/p&gt;
&lt;h2 id=&#34;双页表与单页表&#34;&gt;双页表与单页表&lt;/h2&gt;
&lt;p&gt;为了防范侧信道攻击，我们的 os 使用了双页表。但是传统的设计一直是单页表的，也就是说，用户线程和对应的内核线程共用同一张页表，只不过内核对应的地址只允许在内核态访问。请结合课堂知识回答如下问题：(备注：这里的单/双的说法仅为自创的通俗说法，并无这个名词概念，详情见 KPTI )&lt;/p&gt;
&lt;h2 id=&#34;单页表情况下如何更换页表&#34;&gt;单页表情况下，如何更换页表？&lt;/h2&gt;
&lt;p&gt;在单页表情况下，页表的更换通常是由操作系统的上下文切换来触发的。当从用户态切换到内核态或从一个进程切换到另一个进程时，操作系统会根据相应的上下文信息加载不同的页表，实现页表的更换。&lt;/p&gt;
&lt;h2 id=&#34;单页表情况下如何控制用户态无法访问内核页面tips看看第一题最后一问&#34;&gt;单页表情况下，如何控制用户态无法访问内核页面？（tips:看看第一题最后一问）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;设置页面权限：内核页面通常会被设置为只能在内核态下访问（例如，设置 PTE_U 位为 0），这样用户态无法访问内核页面。&lt;/li&gt;
&lt;li&gt;操作系统权限：操作系统内核态拥有较高的权限，可以通过特权级别或访问控制机制来确保用户态无法直接访问内核页面。用户程序只能通过系统调用进入内核态，并在内核态下由操作系统执行，从而实现对内核页面的访问控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;单页表有何优势回答合理即可&#34;&gt;单页表有何优势？（回答合理即可）&lt;/h2&gt;
&lt;p&gt;单页表的主要优势在于简化了地址转换过程，减少了内存访问的开销。由于用户线程和内核线程共享同一张页表，不需要在上下文切换时频繁切换页表，这可以提高地址转换的效率。此外，单页表还可以节省内存，因为不需要为每个用户线程分配独立的页表。&lt;/p&gt;
&lt;h2 id=&#34;双页表实现下何时需要更换页表假设你写一个单页表操作系统你会选择何时更换页表回答合理即可&#34;&gt;双页表实现下，何时需要更换页表？假设你写一个单页表操作系统，你会选择何时更换页表（回答合理即可）？&lt;/h2&gt;
&lt;p&gt;在双页表实现下，页表的更换通常在发生上下文切换时需要。当从用户态切换到内核态或从一个进程切换到另一个进程时，需要加载相应的页表，以确保正确的地址转换。如果操作系统采用了每个进程独立的页表，那么在进程切换时需要更换页表。&lt;/p&gt;
&lt;p&gt;如果我写一个单页表操作系统，我会选择在发生进程切换时更换页表，因为这是最频繁的上下文切换情况之一。在其他情况下，如从用户态切换到内核态，可能不需要更换整张页表，而只需修改页表项的权限位来实现访问控制。这样可以减少页表更换的开销，提高性能。&lt;/p&gt;
&lt;h1 id=&#34;附录&#34;&gt;附录&lt;/h1&gt;
&lt;p&gt;修改user项目中的makefile，删除ch4_&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<blockquote>
<p>为何指定 TRAMPOLINE 和 TRAPFRAME 在 va 的最高位？
TRAMPOLINE 和 TRAPFRAME 被定义在最高的虚拟内存地址上，是因为它们在操作系统的内存布局中起着重要作用。
TRAMPOLINE 被用作从用户模式切换到内核模式的跳转目标。当发生异常或中断时，处理器将从用户模式切换到内核模式，并将控制权转移到内核中预定义的位置，也就是陷阱处理程序。TRAMPOLINE 页面被映射到最高虚拟地址，以便处理器能够在这个转换过程中方便地引用它。通过将其放置在最高地址，确保了无论系统的具体内存布局如何，它始终是可访问的。
另一方面，TRAPFRAME 用于在发生异常或中断时存储机器状态。它包含寄存器、标志和其他操作系统处理异常所需的信息。TRAPFRAME 也被放置在最高的虚拟地址上，以确保它易于访问，并且陷阱处理程序可以高效地访问它。
通过将 TRAMPOLINE 和 TRAPFRAME 定义在最高的虚拟内存地址上，内核可以方便而可靠地处理异常和中断，而无需关心它们在内存中的特定位置。</p>
</blockquote>
<h1 id="如何确定分页方案---satp">如何确定分页方案 - satp</h1>
<p>在 MMU 没有使能的情况下，虚拟地址和物理地址是相同的。在 MMU 使能的情况下，虚拟地址会被转换成物理地址。这个转换过程是由操作系统来管理的，操作系统需要维护一个数据结构来记录虚拟地址和物理地址的映射关系。这个数据结构就是页表。</p>
<p>转换的过程需要分页机制，分页机制有多种。RISC-V 的分页方案以 SvX 的模式命名，其中 X 是以位为单位的<strong>虚拟地址的长度</strong>。在 RV64 架构下，RISC-V 支持多种分页方案，包括 Sv39，Sv48，Sv57 以及 Sv64。Sv39 最大支持 39 位的虚拟地址，这意味着它可以支持 512 GB 的虚拟地址空间。Sv48 最大支持 48 位的虚拟地址，这意味着它可以支持 256 TB 的虚拟地址空间。我们将在本章中实现 Sv39 分页方案。</p>
<p>如何开启分页机制呢？RISC-V 的分页机制是通过 satp（Supervisor address translation and protection）寄存器来开启的。satp 寄存器字段分布如下：</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//2023/09/05/5d1ec6e9adaf743f7c9abc177cd12eb1.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/05/5d1ec6e9adaf743f7c9abc177cd12eb1.png" alt=""  title="RV64 架构下的 satp 寄存器" 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>
<ul>
<li>Mode 字段可以决定是否开启分页以及分页级数。Mode=0 时，不开启分页；Mode=8 时，开启 Sv39 分页机制。</li>
<li>ASID（Address Space Identifier，地址空间标识符）域是可选的，它可以用来降低上下文切换的开销。目前我们暂不考虑这个字段的作用。</li>
<li>PPN（Physical Page Number，物理页号），保存了根页表的物理地址。</li>
</ul>
<h1 id="sv39-多级页表机制">SV39 多级页表机制</h1>
<h2 id="页表项描述">页表项描述</h2>
<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//2023/09/05/86e06238c562bdd238e868fcd819df3c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/05/86e06238c562bdd238e868fcd819df3c.png" alt=""  title="Sv39 页表项" 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>Sv39 页表项（page-table entry，PTE）的布局，从左到右分别包含如下所述的域：</p>
<ul>
<li>V 位决定了该页表项的其余部分是否有效 (V=1 时有效)。若 V=0，则任何遍历到此页表项的虚址转换操作都会导致页错误。</li>
<li>R、W 和 X 位分别表示此页是否可以读取、写入和执行。如果这三个位都是 0，那么这个页表项是指向下一级页表的指针，否则它是页表树的一个叶节点。</li>
<li>U 位表示该页是否是用户页面。若 U=0，则 U 模式不能访问此页面，但 S 模式可以。若 U=1，则 U 模式下能访问这个页面，而 S 模式不能。</li>
<li>G 位表示这个映射是否对所有虚址空间有效，硬件可以用这个信息来提高地址转换的性能。这一位通常只用于属于操作系统的页面。</li>
<li>A 位表示自从上次 A 位被清除以来，该页面是否被访问过。</li>
<li>D 位表示自从上次清除 D 位以来页面是否被弄脏（例如被写入）。</li>
<li>RSW 域留给操作系统使用，它会被硬件忽略。</li>
<li>PPN 域包含物理页号，这是物理地址的一部分。若这个页表项是一个叶节点，那么 PPN 是转换后物理地址的一部分。否则 PPN 给出下一节页表的地址。</li>
</ul>
<h2 id="虚拟地址转换物理地址过程">虚拟地址转换物理地址过程</h2>
<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//2023/09/05/48e6ce48ffb827a10371344ad07324c2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/05/48e6ce48ffb827a10371344ad07324c2.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>当 satp 寄存器中开启分页时，S 模式和 U 模式中访存的地址都会被视为虚拟地址，需要将其转换为物理地址。虚拟地址转换物理地址的过程如下：</p>
<ul>
<li>从 satp 寄存器中读取 PPN，得到根页表的物理地址，为了表述方便，我们将其记做三级页表基地址 satp.PPN；</li>
<li>从虚拟地址中取出三级虚拟页号 L2</li>
<li>处理器会读取地址位于 satp.PPN * 4096 + L2 * 4 的页表项，得到下一级页表的基地址 L1.PPN；</li>
<li>从虚拟地址中取出二级虚拟页号 L1</li>
<li>处理器会读取地址位于 L1.PPN * 4096 + L1 * 4 的页表项，得到下一级页表的基地址 L0.PPN；</li>
<li>从虚拟地址中取出一级虚拟页号 L0</li>
<li>处理器会读取地址位于 L0.PPN * 4096 + L0 * 4 的页表项，得到物理页号 PPN；</li>
<li>将 PPN 和虚拟地址的低 12 位也就是 Offset 拼接起来，得到物理地址。</li>
</ul>
<p>我们看代码中是如何实现的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define PTE2PA(pte) (((pte) &gt;&gt; 10) &lt;&lt; 12)
</span></span></span><span class="line"><span class="cl"><span class="c1">// 从虚拟地址中提取三个 9 位的页表索引
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PXMASK 0x1FF </span><span class="c1">// 9
</span></span></span><span class="line"><span class="cl"><span class="c1">// PGSHIFT = 12，这段宏定义用于定位 VPNx 的位置
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PXSHIFT(level) (PGSHIFT + (9 * (level)))
</span></span></span><span class="line"><span class="cl"><span class="c1">// 从虚拟地址 VA 中提取出第 level 级页表的索引
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PX(level, va) ((((uint64)(va)) &gt;&gt; PXSHIFT(level)) &amp; PXMASK)
</span></span></span></code></pre></div><p>上面这三个工具宏可以用来提取虚拟页号 VPN。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 返回页表 pagetable 中与虚拟地址 va 对应的 PTE 的地址。
</span></span></span><span class="line"><span class="cl"><span class="c1">// 如果 alloc != 0，则创建所需的页表页。
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// RISC-V Sv39 方案有三级页表页。一个页表页包含 512 个 64 位的 PTEs。
</span></span></span><span class="line"><span class="cl"><span class="c1">// 一个 64 位的虚拟地址被分为五个字段：
</span></span></span><span class="line"><span class="cl"><span class="c1">//   39..63 -- 必须为零。
</span></span></span><span class="line"><span class="cl"><span class="c1">//   30..38 -- 2 级索引的 9 位。
</span></span></span><span class="line"><span class="cl"><span class="c1">//   21..29 -- 1 级索引的 9 位。
</span></span></span><span class="line"><span class="cl"><span class="c1">//   12..20 -- 0 级索引的 9 位。
</span></span></span><span class="line"><span class="cl"><span class="c1">//    0..11 -- 页面内的 12 位字节偏移量。
</span></span></span><span class="line"><span class="cl"><span class="c1">// pagetable 页表
</span></span></span><span class="line"><span class="cl"><span class="c1">// va 虚拟地址
</span></span></span><span class="line"><span class="cl"><span class="c1">// alloc 页表项不存在时是否分配
</span></span></span><span class="line"><span class="cl"><span class="kt">pte_t</span> <span class="o">*</span><span class="nf">walk</span><span class="p">(</span><span class="kt">pagetable_t</span> <span class="n">pagetable</span><span class="p">,</span> <span class="n">uint64</span> <span class="n">va</span><span class="p">,</span> <span class="kt">int</span> <span class="n">alloc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">va</span> <span class="o">&gt;=</span> <span class="n">MAXVA</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nf">panic</span><span class="p">(</span><span class="s">&#34;walk&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">level</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="n">level</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">;</span> <span class="n">level</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">pte_t</span> <span class="o">*</span><span class="n">pte</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">pagetable</span><span class="p">[</span><span class="nf">PX</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">va</span><span class="p">)];</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 通过 PTE 的标志位判断每一级的 pte 是否是有效的（V 位）
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">pte</span> <span class="o">&amp;</span> <span class="n">PTE_V</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">pagetable</span> <span class="o">=</span> <span class="p">(</span><span class="kt">pagetable_t</span><span class="p">)</span><span class="nf">PTE2PA</span><span class="p">(</span><span class="o">*</span><span class="n">pte</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 如果该项无效且 alloc 标志被设置，则分配一个新的页表
</span></span></span><span class="line"><span class="cl">            <span class="c1">// 如果 alloc 参数=0 或者已经没有空闲的内存了，那么遇到中途 V=0 的 pte 整个 walk 过程就会直接退出
</span></span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">alloc</span> <span class="o">||</span> <span class="p">(</span><span class="n">pagetable</span> <span class="o">=</span> <span class="p">(</span><span class="kt">pde_t</span> <span class="o">*</span><span class="p">)</span><span class="nf">kalloc</span><span class="p">())</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 清空分配的页表
</span></span></span><span class="line"><span class="cl">            <span class="nf">memset</span><span class="p">(</span><span class="n">pagetable</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">PGSIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 更新页表项，将其指向新分配的页表，并设置有效位 PTE_V
</span></span></span><span class="line"><span class="cl">            <span class="o">*</span><span class="n">pte</span> <span class="o">=</span> <span class="nf">PA2PTE</span><span class="p">(</span><span class="n">pagetable</span><span class="p">)</span> <span class="o">|</span> <span class="n">PTE_V</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 返回最低级和虚拟地址的页表项，不是返回物理地址
</span></span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">&amp;</span><span class="n">pagetable</span><span class="p">[</span><span class="nf">PX</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">va</span><span class="p">)];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>每次从虚拟地址 va 中提取出一个虚拟页号，然后根据这个虚拟页号从页表中取出下一级页表的基地址。如果这个页表项无效，那么根据 alloc 参数决定是否分配一个新的页表。如果 alloc 参数为 0 或者已经没有空闲的内存了，那么遇到中途 V=0 的 pte 整个 walk 过程就会直接退出。如果 alloc 参数为 1，那么就会分配一个新的页表，然后将这个页表项指向新分配的页表，并设置有效位 PTE_V。</p>
<p>我们可以发现 walk 返回的结果不是物理地址，而是页表项的地址。这是因为 walk 函数的作用是将虚拟地址转换为物理地址，而页表项中的 PPN 只是物理地址的一部分，<strong>还需要加上虚拟地址的低 12 位偏移量才能得到物理地址</strong>。</p>
<h2 id="如何建立页表">如何建立页表</h2>
<p>前面的过程实际上是以用户的角度来考虑的，也就是给你一个虚拟地址按照分页的规则将其转化成物理地址就能访问了。但是作为一个操作系统，我们还需要多考虑一下，页表是哪来的？我们知道从虚拟地址中去获取页表地址，但是<strong>页表的内容是哪来的呢</strong>？页表是如何建立起来的呢？这些是需要操作系统来完成的。</p>
<p>建立页表也就是建立虚拟地址到物理地址的映射关系。也就是给你一个虚拟地址，你需要告诉我如何查到物理地址，实际上这个过程就是建立页表的过程。这个过程也是通过 walk 函数来完成的，从上文我们知道如果页表都建好的情况下 walk 就是不断查页表的过程，那么在没有页表的情况下，walk 还可以建立一个个页表。稍有不同的是，walk 返回的是最后一级页表项的地址，我们需要将物理地址写入这个页表项中。</p>
<p>在 uCore 中使用 mappages 函数封装了 walk 函数，具体如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define PA2PTE(pa) ((((uint64)pa) &gt;&gt; 12) &lt;&lt; 10)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 为从虚拟地址 va 开始的页面创建指向物理地址 pa 开始的页表项（PTE）
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 注意：va 和 size 可能不是页面对齐的
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 如果无法分配所需的页表，则返回 0，否则返回 -1
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param pagetable 根页表地址
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param va        虚拟地址
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param size      映射的字节数
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param pa        物理地址
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @param perm      权限位
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @return          成功返回 0，否则返回 -1
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">mappages</span><span class="p">(</span><span class="kt">pagetable_t</span> <span class="n">pagetable</span><span class="p">,</span> <span class="n">uint64</span> <span class="n">va</span><span class="p">,</span> <span class="n">uint64</span> <span class="n">size</span><span class="p">,</span> <span class="n">uint64</span> <span class="n">pa</span><span class="p">,</span> <span class="kt">int</span> <span class="n">perm</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint64</span> <span class="n">virtualAddress</span><span class="p">,</span> <span class="n">lastVirtualAddress</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">pte_t</span> <span class="o">*</span><span class="n">pte</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 地址必须是页面对齐的
</span></span></span><span class="line"><span class="cl">    <span class="n">virtualAddress</span>     <span class="o">=</span> <span class="nf">PGROUNDDOWN</span><span class="p">(</span><span class="n">va</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">lastVirtualAddress</span> <span class="o">=</span> <span class="nf">PGROUNDDOWN</span><span class="p">(</span><span class="n">va</span> <span class="o">+</span> <span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 返回最低级的虚拟地址的页表项，如果不存在会创建一个新的页表项
</span></span></span><span class="line"><span class="cl">        <span class="c1">// 页表项可能会因为内存不足创建失败，如果创建失败，则返回 -1
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">pte</span> <span class="o">=</span> <span class="nf">walk</span><span class="p">(</span><span class="n">pagetable</span><span class="p">,</span> <span class="n">virtualAddress</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果 PTE 已经有效，则输出错误信息并返回 -1
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">pte</span> <span class="o">&amp;</span> <span class="n">PTE_V</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">errorf</span><span class="p">(</span><span class="s">&#34;remap&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 将物理地址 pa 转换为页表项，并设置权限位 perm 和 有效位 PTE_V
</span></span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">pte</span> <span class="o">=</span> <span class="nf">PA2PTE</span><span class="p">(</span><span class="n">pa</span><span class="p">)</span> <span class="o">|</span> <span class="n">perm</span> <span class="o">|</span> <span class="n">PTE_V</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果当前是最后一个地址，则结束循环
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">virtualAddress</span> <span class="o">==</span> <span class="n">lastVirtualAddress</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">virtualAddress</span> <span class="o">+=</span> <span class="n">PGSIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">pa</span> <span class="o">+=</span> <span class="n">PGSIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="问答作业">问答作业</h1>
<h2 id="请列举-sv39-页表页表项的组成结合课堂内容描述其中的标志位有何作用潜在作用">请列举 SV39 页表页表项的组成，结合课堂内容，描述其中的标志位有何作用／潜在作用？</h2>
<p>Sv39 页表页表项的组成如下：</p>
<ol>
<li><strong>有效位 (V)</strong>：这是页表项的最高位，用于指示页表项是否有效。如果有效位设置为 1，表示页表项有效，可以使用；如果设置为 0，表示页表项无效，禁止使用。这是虚拟内存中页表项的基本有效性标志。</li>
<li><strong>写入位 (W)</strong>：这个标志位用于指示是否可以对此页进行写入操作。如果设置为 1，表示允许写入；如果设置为 0，表示禁止写入。它是页表项的访问权限控制标志之一。</li>
<li><strong>用户位 (U)</strong>：用户位用于指示是否允许用户态程序访问此页。如果设置为 1，表示允许用户态访问；如果设置为 0，表示只允许内核态访问。它是页表项的访问权限控制标志之一。</li>
<li><strong>执行位 (X)</strong>：执行位用于指示是否允许执行此页上的指令。如果设置为 1，表示允许执行；如果设置为 0，表示禁止执行。它也是页表项的访问权限控制标志之一。</li>
<li><strong>全局位 (G)</strong>：全局位用于指示此页是否是全局的，即无需 TLB 缓存，通常用于内核页。如果设置为 1，表示是全局的；如果设置为 0，表示不是全局的。</li>
<li><strong>已访问位 (A)</strong>：已访问位表示是否已经访问过此页，通常由硬件设置。操作系统可以用它来实现页面置换算法，如 LRU。</li>
<li><strong>已修改位 (D)</strong>：已修改位表示是否已经对此页进行了写入操作。与已访问位类似，操作系统可以用它来实现页面置换算法。</li>
<li><strong>物理页框地址 (PPN)</strong>：这是页表项中存储的物理页框的地址。它指示了虚拟页到物理页的映射关系。</li>
</ol>
<p>Sv39 页表的页表项标志位允许操作系统和硬件实现对虚拟内存的细粒度控制和保护。不同的标志位组合可以实现不同级别的内存保护和权限控制，从而提高系统的安全性和可用性。例如，有效位、写入位、用户位和执行位的不同组合可以实现不同级别的内存保护，使操作系统可以将不同的内存区域分配给用户态和内核态，并设置不同的权限。已访问位和已修改位则用于实现页面置换算法，帮助操作系统决定哪些页面应该被置换出去，以优化内存利用率。全局位可以用于标识全局共享的页，从而节省 TLB 缓存空间。物理页框地址是页表项的核心，它建立了虚拟地址到物理地址的映射关系，使虚拟内存管理成为可能。</p>
<h2 id="缺页相关问题">缺页相关问题</h2>
<h3 id="请问哪些异常可能是缺页导致的">请问哪些异常可能是缺页导致的？</h3>
<p>缺页异常是由于进程访问的页面不在页表中或者在页表中无效而引发的异常。以下这些异常可能是因为缺页导致的：</p>
<ul>
<li>
<p>Load Page Fault（Load 异常）：当进程试图读取一个不在页表中或者无效的页面时，会引发 Load Page Fault 异常。在 RISC-V 中，这个异常对应的异常代码是 5。</p>
</li>
<li>
<p>Store Page Fault（Store 异常）：当进程试图写入一个不在页表中或者无效的页面时，会引发 Store Page Fault 异常。在 RISC-V 中，这个异常对应的异常代码是 7。</p>
</li>
<li>
<p>Instruction Page Fault（指令页异常）：当进程试图执行一个不在页表中或者无效的页面上的指令时，会引发 Instruction Page Fault 异常。在 RISC-V 中，这个异常对应的异常代码是 12。</p>
</li>
</ul>
<h3 id="发生缺页时描述相关的重要寄存器的值lab2-中描述过的可以简单点">发生缺页时，描述相关的重要寄存器的值（lab2 中描述过的可以简单点）。</h3>
<ul>
<li>sepc（Exception Program Counter）：trap 发生时会将当前指令的下一条指令地址写入其中，用于 trap 处理完成后返回。</li>
<li>stval（Machine Trap Value）：mtval 寄存器包含导致异常的原因，即导致异常的指令的具体信息。例如，如果是缺页异常，那么 mtval 寄存器包含导致缺页异常的虚拟地址。</li>
<li>scause: 中断/异常发生时， CSR 寄存器 scause 中会记录其信息， Interrupt 位记录是中断还是异常， Exception Code 记录中断/异常的种类。</li>
<li>sstatus: 记录处理器当前状态，其中 SPP 段记录当前特权等级。</li>
<li>stvec: 记录处理 trap 的入口地址，现有两种模式  Direct 和 Vectored 。</li>
<li>sscratch: 其中的值是指向hart相关的S态上下文的指针，比如内核栈的指针。</li>
</ul>
<h3 id="以下行为的好处">以下行为的好处？</h3>
<p>缺页有两个常见的原因，其一是 Lazy 策略，也就是直到内存页面被访问才实际进行页表操作。比如，一个程序被执行时，进程的代码段理论上需要从磁盘加载到内存。但是 os 并不会马上这样做，而是会保存 .text 段在磁盘的位置信息，在这些代码第一次被执行时才完成从磁盘的加载操作。</p>
<p>Lazy Loading 策略有以下好处：</p>
<ol>
<li><strong>减少初始化开销</strong>：Lazy Loading 允许操作系统在程序启动时只加载必需的页面，而不是一次性加载整个程序。这可以减少启动时间和初始化开销，因为不需要将整个程序加载到内存中。</li>
<li><strong>节省内存</strong>：Lazy Loading 策略避免了不必要的内存占用。如果程序的某些部分从不被访问，那么它们就不会被加载到内存中，从而节省了内存资源。</li>
<li><strong>提高响应速度</strong>：通过仅在需要时加载页面，Lazy Loading 可以提高系统的响应速度。只有当程序访问某个页面时，操作系统才会执行磁盘加载操作，而不会在程序启动时浪费时间加载可能永远不会被访问的内容。</li>
<li><strong>更好的磁盘利用率</strong>：Lazy Loading 允许操作系统将程序的不同部分分散在磁盘上，根据需要加载。这可以提高磁盘利用率，因为不需要在磁盘上为整个程序分配连续的空间。</li>
</ol>
<h3 id="请问处理-10g-连续的内存页面需要操作的页表实际大致占用多少内存-给出数量级即可">请问处理 10G 连续的内存页面，需要操作的页表实际大致占用多少内存 (给出数量级即可)？</h3>
<blockquote>
<p>此外 COW(Copy On Write) 也是常见的容易导致缺页的 Lazy 策略，这个之后再说。其实，我们的 mmap 也可以采取 Lazy 策略，比如：一个用户进程先后申请了 10G 的内存空间，然后用了其中 1M 就直接退出了。按照现在的做法，我们显然亏大了，进行了很多没有意义的页表操作。</p>
</blockquote>
<p>处理 10GB 连续的内存页面所需的页表实际上占用的内存量取决于操作系统的页表结构和管理策略。在 RISC-V 的页表结构中，一个页表项（Page Table Entry，PTE）通常占据 8 字节（64 位系统），其中包括物理页框号和一些标志位。让我们假设一个 PTE 占用 8 字节。</p>
<p>为了估算 10GB 连续内存页面所需的页表实际占用内存量，我们可以按照以下步骤进行计算：</p>
<ol>
<li>
<p>首先，将 10GB 转换为字节数。1GB 等于 1,073,741,824 字节，所以 10GB 等于 10 * 1,073,741,824 = 10,737,418,240 字节。</p>
</li>
<li>
<p>然后，计算每个页面表项覆盖的内存范围。假设每个页面表项管理 4KB（4 * 1024 字节）的内存页面。</p>
</li>
<li>
<p>计算需要多少个页面表项来管理 10GB 的内存。这可以通过将 10GB 除以每个页面表项管理的内存范围来实现。</p>
</li>
<li>
<p>最后，将所需的页面表项数量乘以每个 PTE 的大小来估算所需的总内存量。</p>
</li>
</ol>
<p>让我们进行具体计算：</p>
<ul>
<li>内存大小：10,737,418,240 字节</li>
<li>每个页面表项管理的内存范围：4KB = 4 * 1024 字节</li>
<li>需要的页面表项数量：10,737,418,240 字节 / 4KB = 2,621,440 个页表项</li>
</ul>
<p>假设每个页表项占用 8 字节，则需要的总内存量为：</p>
<p>2,621,440 个页表项 * 8 字节/页表项 = 20,971,520 字节</p>
<p>所以，处理 10GB 连续的内存页面所需的页表实际占用内存量约为 20,971,520 字节，或者大约 20MB。这只是一个估算，实际内存占用可能会因操作系统的管理策略和对齐等因素而有所不同。</p>
<h3 id="请简单思考如何才能在现有框架基础上实现-lazy-策略缺页时又如何处理描述合理即可不需要考虑实现">请简单思考如何才能在现有框架基础上实现 Lazy 策略，缺页时又如何处理？描述合理即可，不需要考虑实现。</h3>
<p>要在现有框架基础上实现 Lazy 策略，可以采取以下简单思路：</p>
<ol>
<li>
<p><strong>延迟加载（Lazy Loading）</strong>：在用户进程请求内存映射时，不立即将整个内存区域加载到物理内存中。而是仅创建虚拟内存映射和页表项，记录对应的磁盘位置等信息。</p>
</li>
<li>
<p><strong>缺页处理（Page Fault Handling）</strong>：当用户进程访问虚拟内存中的某个尚未加载的内存页面时，会触发缺页异常。在缺页异常处理程序中，操作系统会根据页表中的磁盘位置信息，将相应的磁盘数据加载到物理内存中，并更新页表项，使其指向新加载的物理页面。</p>
</li>
<li>
<p><strong>惰性加载（Demand Paging）</strong>：为了提高性能，可以采用惰性加载策略，即只加载实际被访问的内存页面，而不是一次性加载整个区域。这可以通过在缺页处理程序中进行懒加载操作来实现。</p>
</li>
<li>
<p><strong>内存回收（Memory Reclamation）</strong>：当系统内存不足时，操作系统可以选择回收一些不常访问的内存页面，将其写回磁盘，并更新页表项为无效。这需要根据页面访问模式和策略来确定哪些页面可以被回收。</p>
</li>
<li>
<p><strong>性能优化</strong>：为了提高性能，可以采用预读取（Prefetching）策略，即在缺页处理时，不仅加载当前访问的页面，还预先加载相邻的页面，以减少未来可能的缺页次数。</p>
</li>
</ol>
<h3 id="此时页面失效如何表现在页表项-pte-上">此时页面失效如何表现在页表项 (PTE) 上？</h3>
<blockquote>
<p>缺页的另一个常见原因是 swap 策略，也就是内存页面可能被换到磁盘上了，导致对应页面失效。</p>
</blockquote>
<p>Dirty bit (D 位)：当页面被修改并且尚未写回到主存时，该位会被设置为 1。如果页面已经被换出到磁盘上，D 位将保持为 1，以指示页面数据已过期。</p>
<p>Valid bit (V 位)：当页面在主存中有效时，V 位被设置为 1。如果页面被换出到磁盘上，V 位将被清除为 0，表示该页无效。</p>
<p>通过检查页表项的 D 位和 V 位，操作系统可以确定页面是否需要从磁盘重新加载到内存中。如果 D 位为 1，说明页面需要写回到主存，在将其置为有效之前，必须将页数据从磁盘读取到内存中。如果 V 位为 0，说明页面当前无效，需要将其从磁盘加载到内存中，并将 V 位设置为 1，表示页面有效。</p>
<h2 id="双页表与单页表">双页表与单页表</h2>
<p>为了防范侧信道攻击，我们的 os 使用了双页表。但是传统的设计一直是单页表的，也就是说，用户线程和对应的内核线程共用同一张页表，只不过内核对应的地址只允许在内核态访问。请结合课堂知识回答如下问题：(备注：这里的单/双的说法仅为自创的通俗说法，并无这个名词概念，详情见 KPTI )</p>
<h2 id="单页表情况下如何更换页表">单页表情况下，如何更换页表？</h2>
<p>在单页表情况下，页表的更换通常是由操作系统的上下文切换来触发的。当从用户态切换到内核态或从一个进程切换到另一个进程时，操作系统会根据相应的上下文信息加载不同的页表，实现页表的更换。</p>
<h2 id="单页表情况下如何控制用户态无法访问内核页面tips看看第一题最后一问">单页表情况下，如何控制用户态无法访问内核页面？（tips:看看第一题最后一问）</h2>
<ul>
<li>设置页面权限：内核页面通常会被设置为只能在内核态下访问（例如，设置 PTE_U 位为 0），这样用户态无法访问内核页面。</li>
<li>操作系统权限：操作系统内核态拥有较高的权限，可以通过特权级别或访问控制机制来确保用户态无法直接访问内核页面。用户程序只能通过系统调用进入内核态，并在内核态下由操作系统执行，从而实现对内核页面的访问控制。</li>
</ul>
<h2 id="单页表有何优势回答合理即可">单页表有何优势？（回答合理即可）</h2>
<p>单页表的主要优势在于简化了地址转换过程，减少了内存访问的开销。由于用户线程和内核线程共享同一张页表，不需要在上下文切换时频繁切换页表，这可以提高地址转换的效率。此外，单页表还可以节省内存，因为不需要为每个用户线程分配独立的页表。</p>
<h2 id="双页表实现下何时需要更换页表假设你写一个单页表操作系统你会选择何时更换页表回答合理即可">双页表实现下，何时需要更换页表？假设你写一个单页表操作系统，你会选择何时更换页表（回答合理即可）？</h2>
<p>在双页表实现下，页表的更换通常在发生上下文切换时需要。当从用户态切换到内核态或从一个进程切换到另一个进程时，需要加载相应的页表，以确保正确的地址转换。如果操作系统采用了每个进程独立的页表，那么在进程切换时需要更换页表。</p>
<p>如果我写一个单页表操作系统，我会选择在发生进程切换时更换页表，因为这是最频繁的上下文切换情况之一。在其他情况下，如从用户态切换到内核态，可能不需要更换整张页表，而只需修改页表项的权限位来实现访问控制。这样可以减少页表更换的开销，提高性能。</p>
<h1 id="附录">附录</h1>
<p>修改user项目中的makefile，删除ch4_</p>
]]></content:encoded>
    </item>
    <item>
      <title>uCore 实验第 3 章 - 多道程序与分时多任务</title>
      <link>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC3%E7%AB%A0-%E5%A4%9A%E9%81%93%E7%A8%8B%E5%BA%8F%E4%B8%8E%E5%88%86%E6%97%B6%E5%A4%9A%E4%BB%BB%E5%8A%A1/</link>
      <pubDate>Sat, 02 Sep 2023 16:03:02 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC3%E7%AB%A0-%E5%A4%9A%E9%81%93%E7%A8%8B%E5%BA%8F%E4%B8%8E%E5%88%86%E6%97%B6%E5%A4%9A%E4%BB%BB%E5%8A%A1/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 启动时初始化进程表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;proc_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NPROC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UNUSED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// p - pool 是 p 指向的 proc 在 pool 中的下标，因此 p - pool 变化情况是 0, 1, 2, ..., NPROC - 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ustack&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ustack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;		* LAB1: you may need to initialize your new fields of proc here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;		*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;boot_stack_top&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;current_proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;p - pool 表示什么？
假设我们有一个名为 pool 的数组，其中包含了多个类型为 struct proc 的元素，并且有一个指针 p 指向其中的某个元素。
当 p 指向 pool 数组的第一个元素时，p - pool 的结果将是 0，因为指针相对于数组首地址的偏移量为 0。
当 p 指向 pool 数组的第二个元素时，p - pool 的结果将是 1，因为指针相对于数组首地址的偏移量为 1。
以此类推，当 p 指向 pool 数组的第 N 个元素时，p - pool 的结果将是 N-1，因为指针相对于数组首地址的偏移量为 N-1。
总结来说，如果 p 是指向 pool 数组中第 N 个元素的指针，那么 p - pool 的结果将是 N-1。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;原调度函数每次都会从 pool 数组的第一个元素开始遍历，这样会导致每次都是从第一个进程开始运行，而不是从上次运行的进程开始运行。需要修改为如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 调度程序永不返回。它循环执行以下操作：
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//  - 选择要运行的进程。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//  - 切换以启动运行该进程。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//  - 最终，该进程通过切换将控制权
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//    传递回调度程序。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last_checked_proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 初始化指针为 pool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(;;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last_checked_proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NPROC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 将 p 初始化为 last_checked_proc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RUNNABLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;                * LAB1：你可能需要在这里初始化进程的起始时间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;                */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RUNNING&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;current_proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;swtch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;last_checked_proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 更新 last_checked_proc 的值为下一个位置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;lab1&#34;&gt;LAB1&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;---&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;           &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;   &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;             &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;             &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;55&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;++++-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;   &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;            &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;   &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;files&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;changed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;374&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;insertions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;291&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;deletions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;diff&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b45e85d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b21b0a4&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100644&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;---&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;loader.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;defs.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;trap.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;app_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app_info_ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;49&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;run_all_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sp&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ustack&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;USER_STACK_SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RUNNABLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-		* LAB1: you may need to initialize your new fields of proc here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+		* LAB1: 初始化系统调用数以及进程开始时间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; 		*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;memset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MAX_SYSCALL_NUM&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;No&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;newline&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;at&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;diff&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fee3886&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;.0&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c69ae5&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100644&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;---&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;defs.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;loader.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;trap.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;timer.h&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NPROC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NPROC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PAGE_SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;33&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;34&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;proc_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ustack&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ustack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;        &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-		* LAB1: you may need to initialize your new fields of proc here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-		*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;memset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MAX_SYSCALL_NUM&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;n&#34;&gt;idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;boot_stack_top&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;n&#34;&gt;idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;47&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;47&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;allocpid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PID&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;c1&#34;&gt;// 在进程表中寻找一个未使用的进程。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;c1&#34;&gt;// 如果找到，则初始化在内核中运行所需的状态。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;c1&#34;&gt;// 如果没有空闲的进程，或者内存分配失败，则返回 0。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;81&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;18&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last_checked_proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 初始化指针为 pool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(;;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last_checked_proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NPROC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 将 p 初始化为 last_checked_proc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RUNNABLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-                * LAB1：你可能需要在这里初始化进程的起始时间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+                * LAB1：在这里初始化进程的开始时间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;                 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_cycle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;                        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MILLISECONDS_PER_SECOND&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RUNNING&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;n&#34;&gt;current_proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;nf&#34;&gt;swtch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;diff&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d208c5d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;.53576&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bf&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100644&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;---&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;types.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;define&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NPROC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;define&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NPROC&lt;/span&gt;           &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 最大进程数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;define&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;MAX_SYSCALL_NUM&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 最大系统调用数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;c1&#34;&gt;// Saved registers for kernel context switches.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;42&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;43&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;28&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;kstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 进程内核栈虚拟地址 (内核页表)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;trapframe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 进程中断帧
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 用于保存进程内核态的寄存器信息，进程切换时使用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;                               &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-	* LAB1: you may need to add some new fields here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+    /*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+	* LAB1: 添加一些新的成员用于新的 sys_task_info 系统调用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; 	*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;uint32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MAX_SYSCALL_NUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 系统调用次数统计 TODO: 后续改为指针
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                     &lt;span class=&#34;c1&#34;&gt;// 进程开始运行时间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-* LAB1: you may need to define struct for TaskInfo here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+* LAB1: 定义 TaskInfo 结构体
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;UnInit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;Ready&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;Exited&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TaskStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;TaskStatus&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;uint32&lt;/span&gt;     &lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MAX_SYSCALL_NUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 进程运行时间统计
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TaskInfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;curr_proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;         &lt;span class=&#34;nf&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;diff&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cc5aeb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f54ed86&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100644&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;---&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;syscall_ids.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;timer.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;#34;trap.h&amp;#34;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;proc.h&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sys_write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sys_sched_yield&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sys_gettimeofday&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TimeVal&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_tz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_cycle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sec&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;usec&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MICROSECONDS_PER_SECOND&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;tracef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;sys_gettimeofday cycle = %d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sec&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;msec&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MILLISECONDS_PER_SECOND&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;usec&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MICROSECONDS_PER_SECOND&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-* LAB1: you may need to define sys_task_info here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/** 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+ * LAB1：此处定义 sys_task_info 函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+ * 查询当前正在执行的任务信息，任务信息包括任务控制块相关信息（任务状态）、任务使用的系统调用次数、任务总运行时长。 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+ */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sys_task_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TaskInfo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;curr_proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// TODO: proc 检查为空
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MAX_SYSCALL_NUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_cycle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cycle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MILLISECONDS_PER_SECOND&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CPU_FREQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;infof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;sys_task_info current_time = %d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;infof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;proc-&amp;gt;start_time = %d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;infof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ti-&amp;gt;time = %d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RUNNING&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Running&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RUNNABLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ready&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SLEEPING&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ready&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ZOMBIE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exited&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UNUSED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UnInit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;proc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trap_page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;51&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;84&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;nf&#34;&gt;tracef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;syscall %d args = [%x, %x, %x, %x, %x, %x]&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-	* LAB1: you may need to update syscall counter for task info here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+	* LAB1: 更新系统调用次数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; 	*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;curr_proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;SYS_write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sys_write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;67&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;101&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sys_gettimeofday&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TimeVal&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-	* LAB1: you may need to add SYS_taskinfo case here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;+	* LAB1: 在此处添加 SYS_task_info 的系统调用处理情况
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; 	*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;SYS_task_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sys_task_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TaskInfo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;SYS_getpid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;infof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;SYS_getpid %d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SYS_getpid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;curr_proc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;nf&#34;&gt;errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;unknown syscall %d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;diff&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;mo&#34;&gt;05&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a6cb9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;.3&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c1a5a9&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100644&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;---&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;syscall_ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;277&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;277&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define SYS_io_pgetevents          292
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define SYS_rseq                   293
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define SYS_kexec_file_load        294
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-* LAB1: you may need to define SYS_task_info here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;-*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// LAB1：添加 SYS_task_info 的系统调用号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;define&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SYS_task_info&lt;/span&gt;          &lt;span class=&#34;mi&#34;&gt;410&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define SYS_pidfd_send_signal  424
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define SYS_io_uring_setup     425
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define SYS_io_uring_enter     426
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;diff&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c6ebd14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;.63&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ab45c&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100644&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;---&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+++&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define TICKS_PER_SEC (100)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;c1&#34;&gt;// QEMU
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define CPU_FREQ                (12500000)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;define&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;MILLISECONDS_PER_SECOND&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;cp&#34;&gt;#define MICROSECONDS_PER_SECOND (1000000)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_cycle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;@@&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;set_next_timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 自 Unix 纪元起的秒数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;msec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 毫秒数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;usec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 微秒数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TimeVal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;mf&#34;&gt;2.34.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;问答作业&#34;&gt;问答作业&lt;/h1&gt;
&lt;h2 id=&#34;问题一&#34;&gt;问题一&lt;/h2&gt;
&lt;p&gt;正确进入 U 态后，程序的特征还应有：使用 S 态特权指令，访问 S 态寄存器后会报错。请同学们可以自行测试这些内容（参考 前三个测例，描述程序出错行为，同时注意注明你使用的 sbi 及其版本。&lt;/p&gt;
&lt;p&gt;测试前三个测试用例指的是&lt;code&gt;uCore-Tutorial-Code-2023S/user/src/&lt;/code&gt; 目录下的三个&lt;code&gt;bad&lt;/code&gt;测试用例，查看&lt;code&gt;user&lt;/code&gt;项目的 Makefile 可以发现在编译时修改&lt;code&gt;CHAPTER&lt;/code&gt;参数值为&lt;code&gt;2_bad&lt;/code&gt;即可编译运行这些测试用例。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; RustSBI version 0.3.0-alpha.2, adapting to RISC-V SBI v1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.______       __    __      _______.___________.  _______..______   __
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;   _  &lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;    /       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;           &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; /       &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;   _  &lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;_&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;----&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;---&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;----&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;----&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;_&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      /     &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;  &lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      &lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;  &lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;   _  &amp;lt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\-&lt;/span&gt;---.&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;--&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;.----&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  .----&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;_&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; _&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;._____&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\_&lt;/span&gt;_____/ &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;_______/       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;__&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;_______/    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;______/ &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;__&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Implementation     : RustSBI-QEMU Version 0.2.0-alpha.2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Platform Name      : riscv-virtio,qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Platform SMP       : &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Platform Memory    : 0x80000000..0x88000000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Boot HART          : &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Device Tree Region : 0x87000000..0x87000ef2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Firmware Address   : 0x80000000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Supervisor Address : 0x80200000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; pmp01: 0x00000000..0x80000000 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;-wr&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; pmp02: 0x80000000..0x80200000 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;---&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; pmp03: 0x80200000..0x88000000 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;xwr&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;rustsbi&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; pmp04: 0x88000000..0x00000000 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;-wr&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;TRACE 0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;load app &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; at 0x0000000080400000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;TRACE 0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;load app &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; at 0x0000000080420000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;TRACE 0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;load app &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; at 0x0000000080440000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;INFO 0&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;start scheduler!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;ERROR 1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;unknown trap: 0x0000000000000007, &lt;span class=&#34;nv&#34;&gt;stval&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0x0000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;INFO 1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;进程 &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; 以代码 -1 退出
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;IllegalInstruction in application, core dumped.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;INFO 2&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;进程 &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; 以代码 -3 退出
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;IllegalInstruction in application, core dumped.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;INFO 3&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;进程 &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; 以代码 -3 退出
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;PANIC 3&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; os/loader.c:15: all apps over
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个进程测试用例如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在您提供的代码中，将空指针分配给指针变量*p 后，试图对其进行解引用并将值 0 赋给该指针。由于用户模式下禁止直接访问物理内存，操作系统会检测到这个非法操作并触发异常。因此，该程序 IllegalInstruction in application, core dumped.&lt;/p&gt;
&lt;p&gt;在 RISC-V 架构中，U 模式是最低的用户模式，用户程序无法直接访问物理内存或其他特权级别资源。这种限制是为了确保操作系统的安全性和稳定性。&lt;/p&gt;
&lt;p&gt;第二个进程测试用例如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;volatile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;sret&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;试图使用汇编语言执行 sret 指令，该指令用于从中断或异常处理程序返回。由于用户模式下禁止直接访问特权级别寄存器，操作系统会检测到这个非法操作并触发异常。因此，该程序 IllegalInstruction in application, core dumped。&lt;/p&gt;
&lt;p&gt;第三个进程测试用例如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;uint64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;volatile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;csrr %0, sstatus&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;=r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;原因同上，试图使用汇编语言执行 csrr 指令，该指令用于从特权级别寄存器中读取值。由于用户模式下禁止直接访问特权级别寄存器，操作系统会检测到这个非法操作并触发异常。因此，该程序 IllegalInstruction in application, core dumped。&lt;/p&gt;
&lt;p&gt;在操作系统代码中，触发异常后会进入&lt;code&gt;void usertrap()&lt;/code&gt; 函数，该函数会根据 &lt;code&gt;scause&lt;/code&gt; 寄存器的值判断异常类型，用例中的结果进入了&lt;code&gt;case IllegalInstruction&lt;/code&gt;，其中 &lt;code&gt;IllegalInstruction = 2&lt;/code&gt;。我们查阅手册 &lt;code&gt;riscv-privileged.pdf&lt;/code&gt; ，可以查到 &lt;code&gt;IllegalInstruction&lt;/code&gt; 的值为 2，与预期相符。&lt;/p&gt;
&lt;h2 id=&#34;问题二&#34;&gt;问题二&lt;/h2&gt;
&lt;p&gt;请结合用例理解 trampoline.S 中两个函数 &lt;code&gt;userret&lt;/code&gt; 和 &lt;code&gt;uservec&lt;/code&gt; 的作用，并回答如下几个问题：&lt;/p&gt;
&lt;h3 id=&#34;l79-刚进入-userret-时a0a1-分别代表了什么值&#34;&gt;L79: 刚进入 &lt;code&gt;userret&lt;/code&gt; 时，&lt;code&gt;a0&lt;/code&gt;、&lt;code&gt;a1&lt;/code&gt; 分别代表了什么值。&lt;/h3&gt;
&lt;p&gt;在进入&lt;code&gt;userret&lt;/code&gt;函数时，&lt;code&gt;a0&lt;/code&gt;和&lt;code&gt;a1&lt;/code&gt;分别代表以下值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a0&lt;/code&gt;: TRAPFRAME 的地址，指向当前进程的陷阱帧（trapframe）结构体。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a1&lt;/code&gt;: 用户页表的地址，即进程的页表（pagetable）。这个地址会被写入到&lt;code&gt;satp&lt;/code&gt;寄存器中，用于切换到用户模式的页表。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;l87-l88-sfence-指令有何作用为什么要执行该指令当前章节中删掉该指令会导致错误吗&#34;&gt;L87-L88: &lt;code&gt;sfence&lt;/code&gt; 指令有何作用？为什么要执行该指令，当前章节中，删掉该指令会导致错误吗？&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;csrw&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;satp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;a1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;sfence.vma&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;sfence&lt;/code&gt;指令（Store Fence）的作用是确保之前的存储操作完成，并且对其他处理器上的核心可见。&lt;/p&gt;
&lt;p&gt;执行&lt;code&gt;sfence&lt;/code&gt;指令的主要目的是为了保证内存访问的顺序性和一致性。在多核处理器系统中，不同的核心可能会有自己的缓存，当一个核心修改了共享内存中的数据时，为了保证其他核心能够看到这个修改，需要使用&lt;code&gt;sfence&lt;/code&gt;指令来刷新缓存并将修改写回共享内存。&lt;/p&gt;
&lt;p&gt;在代码中，&lt;code&gt;sfence&lt;/code&gt;指令被用于确保对用户页表的修改对其他处理器上的核心可见。因为目前我只使用了单核处理器，所以不会出现多核处理器的情况，因此&lt;code&gt;sfence&lt;/code&gt;指令的作用是确保对用户页表的修改对当前核心可见。&lt;/p&gt;
&lt;p&gt;因此，当前章节中，&lt;strong&gt;删掉该指令不会导致错误&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;l96-l125-为何注释中说要除去-a0哪一个地址代表-a0现在-a0-的值存在何处&#34;&gt;L96-L125: 为何注释中说要除去 &lt;code&gt;a0&lt;/code&gt;？哪一个地址代表 &lt;code&gt;a0&lt;/code&gt;？现在 &lt;code&gt;a0&lt;/code&gt; 的值存在何处？&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# restore all but a0 from TRAPFRAME
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ld ra, 40(a0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ld sp, 48(a0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ld t5, 272(a0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ld t6, 280(a0)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;a0&lt;/code&gt; 是&lt;strong&gt;保存在 &lt;code&gt;sscratch&lt;/code&gt; 寄存器中的&lt;/strong&gt;，首先，该代码通过 &lt;code&gt;ld&lt;/code&gt; 指令从 &lt;code&gt;TRAPFRAME&lt;/code&gt; 中加载各个寄存器的值。然后，这些值被存储在相应的寄存器中，以便在恢复用户上下文时使用。&lt;/p&gt;
&lt;p&gt;接下来，代码使用 &lt;code&gt;csrrw&lt;/code&gt; 指令将 sscratch 寄存器的值与 &lt;code&gt;a0&lt;/code&gt;（即 &lt;code&gt;TRAPFRAME&lt;/code&gt;）进行交换。这样做是为了将用户的 &lt;code&gt;a0&lt;/code&gt;（&lt;code&gt;TRAPFRAME&lt;/code&gt;）保存在 &lt;code&gt;sscratch&lt;/code&gt; 寄存器中，以便后续步骤可以正确地恢复用户上下文。&lt;/p&gt;
&lt;p&gt;最后，通过 &lt;code&gt;sret&lt;/code&gt; 指令返回到用户模式，并将控制权交给用户代码。在执行 &lt;code&gt;sret&lt;/code&gt; 指令后，处理器将根据用户上下文中的 &lt;code&gt;sepc&lt;/code&gt; 寄存器的值跳转到用户代码的指令地址。返回的同时，处理器还会自动恢复 &lt;code&gt;sstatus&lt;/code&gt; 寄存器的值，以确保正确的特权级别和中断状态。&lt;/p&gt;
&lt;h3 id=&#34;userret中发生状态切换在哪一条指令为何执行之后会进入用户态&#34;&gt;&lt;code&gt;userret&lt;/code&gt;：中发生状态切换在哪一条指令？为何执行之后会进入用户态？&lt;/h3&gt;
&lt;p&gt;在&lt;code&gt;userret&lt;/code&gt;函数中，发生状态切换的指令是&lt;code&gt;sret&lt;/code&gt;指令。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sret&lt;/code&gt;指令用于从内核模式切换到用户模式，并将控制权交给用户代码。执行&lt;code&gt;sret&lt;/code&gt;指令后，处理器会根据用户上下文中的&lt;code&gt;sepc&lt;/code&gt;寄存器的值跳转到用户代码的指令地址。&lt;/p&gt;
&lt;p&gt;执行&lt;code&gt;sret&lt;/code&gt;指令之后进入用户态的原因是，该指令会自动恢复&lt;code&gt;sstatus&lt;/code&gt;寄存器的值，以确保正确的特权级别和中断状态。当&lt;code&gt;sret&lt;/code&gt;指令执行后，处理器将从内核态切换回用户态，程序将继续执行用户代码。这意味着&lt;code&gt;userret&lt;/code&gt;函数成功完成了从内核切换到用户模式的过程。&lt;/p&gt;
&lt;h3 id=&#34;l29执行之后a0-和-sscratch-中各是什么值为什么&#34;&gt;L29：执行之后，a0 和 sscratch 中各是什么值，为什么？&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;csrrw a0, sscratch, a0     
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在执行指令后，&lt;code&gt;a0&lt;/code&gt;和&lt;code&gt;sscratch&lt;/code&gt;中的值发生了互换。&lt;/p&gt;
&lt;p&gt;假设原始&lt;code&gt;a0&lt;/code&gt;寄存器中的值为 X，而&lt;code&gt;sscratch&lt;/code&gt;寄存器中的值为 Y。执行&lt;code&gt;csrrw a0, sscratch, a0&lt;/code&gt;指令后，&lt;code&gt;a0&lt;/code&gt;寄存器中的值变为 Y，而&lt;code&gt;sscratch&lt;/code&gt;寄存器中的值变为 X。&lt;/p&gt;
&lt;p&gt;这是因为&lt;code&gt;csrrw&lt;/code&gt;指令是一个特权指令，用于将某个 CSR（Control and Status Register）的值读取到目标寄存器，然后将目标寄存器的值写回到该 CSR 中。在这里，&lt;code&gt;csrrw a0, sscratch, a0&lt;/code&gt;指令将&lt;code&gt;sscratch&lt;/code&gt;寄存器的值读取到&lt;code&gt;a0&lt;/code&gt;寄存器中，同时将&lt;code&gt;a0&lt;/code&gt;寄存器中的值写回到&lt;code&gt;sscratch&lt;/code&gt;寄存器中，从而实现了两者之间的数据交换。&lt;/p&gt;
&lt;h3 id=&#34;l32-l61-从-trapframe-第几项开始保存为什么是否从该项开始保存了所有的值如果不是为什么&#34;&gt;L32-L61: 从 trapframe 第几项开始保存？为什么？是否从该项开始保存了所有的值，如果不是，为什么？&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sd ra, 40(a0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sd sp, 48(a0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sd t5, 272(a0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sd t6, 280(a0)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;进入-s-态是哪一条指令发生的&#34;&gt;进入 S 态是哪一条指令发生的？&lt;/h3&gt;
&lt;h3 id=&#34;l75-l76-ld-t0-16a0-执行之后t0中的值是什么解释该值的由来&#34;&gt;L75-L76: &lt;code&gt;ld t0, 16(a0)&lt;/code&gt; 执行之后，&lt;code&gt;t0&lt;/code&gt;中的值是什么，解释该值的由来？&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ld t0, 16(a0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;jr t0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;ld t0, 16(a0)&lt;/code&gt;就是从 &lt;code&gt;trapframe&lt;/code&gt; 中恢复 &lt;code&gt;t0&lt;/code&gt;寄存器值，&lt;code&gt;t0&lt;/code&gt;保存了&lt;code&gt;kernel_trap&lt;/code&gt;的入口地址。使用 &lt;code&gt;jr t0&lt;/code&gt;，就跳转到了我们早先设定在 &lt;code&gt;trapframe-&amp;gt;kernel_trap&lt;/code&gt; 中的地址，也就是 &lt;code&gt;trap.c&lt;/code&gt; 之中的 &lt;code&gt;usertrap&lt;/code&gt; 函数。这个函数在 &lt;code&gt;main&lt;/code&gt; 的初始化之中已经调用了。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 启动时初始化进程表
</span></span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">proc_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">p</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="o">&amp;</span><span class="n">pool</span><span class="p">[</span><span class="n">NPROC</span><span class="p">];</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">=</span> <span class="n">UNUSED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// p - pool 是 p 指向的 proc 在 pool 中的下标，因此 p - pool 变化情况是 0, 1, 2, ..., NPROC - 1
</span></span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">-&gt;</span><span class="n">kstack</span>    <span class="o">=</span> <span class="p">(</span><span class="n">uint64</span><span class="p">)</span><span class="n">kstack</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="n">pool</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">-&gt;</span><span class="n">ustack</span>    <span class="o">=</span> <span class="p">(</span><span class="n">uint64</span><span class="p">)</span><span class="n">ustack</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="n">pool</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span><span class="o">-&gt;</span><span class="n">trapframe</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">trapframe</span> <span class="o">*</span><span class="p">)</span><span class="n">trapframe</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="n">pool</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">		* LAB1: you may need to initialize your new fields of proc here
</span></span></span><span class="line"><span class="cl"><span class="cm">		*/</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">idle</span><span class="p">.</span><span class="n">kstack</span>  <span class="o">=</span> <span class="p">(</span><span class="n">uint64</span><span class="p">)</span><span class="n">boot_stack_top</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">idle</span><span class="p">.</span><span class="n">pid</span>     <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">current_proc</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">idle</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p>p - pool 表示什么？
假设我们有一个名为 pool 的数组，其中包含了多个类型为 struct proc 的元素，并且有一个指针 p 指向其中的某个元素。
当 p 指向 pool 数组的第一个元素时，p - pool 的结果将是 0，因为指针相对于数组首地址的偏移量为 0。
当 p 指向 pool 数组的第二个元素时，p - pool 的结果将是 1，因为指针相对于数组首地址的偏移量为 1。
以此类推，当 p 指向 pool 数组的第 N 个元素时，p - pool 的结果将是 N-1，因为指针相对于数组首地址的偏移量为 N-1。
总结来说，如果 p 是指向 pool 数组中第 N 个元素的指针，那么 p - pool 的结果将是 N-1。</p>
</blockquote>
<p>原调度函数每次都会从 pool 数组的第一个元素开始遍历，这样会导致每次都是从第一个进程开始运行，而不是从上次运行的进程开始运行。需要修改为如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 调度程序永不返回。它循环执行以下操作：
</span></span></span><span class="line"><span class="cl"><span class="c1">//  - 选择要运行的进程。
</span></span></span><span class="line"><span class="cl"><span class="c1">//  - 切换以启动运行该进程。
</span></span></span><span class="line"><span class="cl"><span class="c1">//  - 最终，该进程通过切换将控制权
</span></span></span><span class="line"><span class="cl"><span class="c1">//    传递回调度程序。
</span></span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">scheduler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="n">last_checked_proc</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span> <span class="c1">// 初始化指针为 pool
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="n">p</span> <span class="o">=</span> <span class="n">last_checked_proc</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="o">&amp;</span><span class="n">pool</span><span class="p">[</span><span class="n">NPROC</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">             <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 将 p 初始化为 last_checked_proc
</span></span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">RUNNABLE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">                * LAB1：你可能需要在这里初始化进程的起始时间
</span></span></span><span class="line"><span class="cl"><span class="cm">                */</span>
</span></span><span class="line"><span class="cl">                <span class="n">p</span><span class="o">-&gt;</span><span class="n">state</span>     <span class="o">=</span> <span class="n">RUNNING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_proc</span> <span class="o">=</span> <span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">swtch</span><span class="p">(</span><span class="o">&amp;</span><span class="n">idle</span><span class="p">.</span><span class="n">context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">context</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">last_checked_proc</span> <span class="o">=</span> <span class="n">pool</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// 更新 last_checked_proc 的值为下一个位置
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="lab1">LAB1</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="o">---</span>
</span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">/</span><span class="n">loader</span><span class="p">.</span><span class="n">c</span>           <span class="o">|</span>   <span class="mi">5</span> <span class="o">+-</span>
</span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">c</span>             <span class="o">|</span>  <span class="mi">15</span> <span class="o">+-</span>
</span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">h</span>             <span class="o">|</span>  <span class="mi">23</span> <span class="o">+-</span>
</span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">/</span><span class="n">syscall</span><span class="p">.</span><span class="n">c</span>          <span class="o">|</span>  <span class="mi">55</span> <span class="o">++++-</span>
</span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">/</span><span class="n">syscall_ids</span><span class="p">.</span><span class="n">h</span>      <span class="o">|</span>   <span class="mi">5</span> <span class="o">+-</span>
</span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">/</span><span class="n">timer</span><span class="p">.</span><span class="n">h</span>            <span class="o">|</span>   <span class="mi">2</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl"> <span class="mi">9</span> <span class="n">files</span> <span class="n">changed</span><span class="p">,</span> <span class="mi">374</span> <span class="nf">insertions</span><span class="p">(</span><span class="o">+</span><span class="p">),</span> <span class="mi">291</span> <span class="nf">deletions</span><span class="p">(</span><span class="o">-</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">loader</span><span class="p">.</span><span class="n">c</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">loader</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="n">index</span> <span class="n">b45e85d</span><span class="p">..</span><span class="n">b21b0a4</span> <span class="mi">100644</span>
</span></span><span class="line"><span class="cl"><span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">loader</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">loader</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">6</span> <span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="mi">7</span> <span class="err">@@</span>
</span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;loader.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;defs.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;trap.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="err">#</span><span class="n">include</span> <span class="o">&lt;</span><span class="n">string</span><span class="p">.</span><span class="n">h</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="k">static</span> <span class="n">uint64</span>  <span class="n">app_num</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">static</span> <span class="n">uint64</span> <span class="o">*</span><span class="n">app_info_ptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">49</span><span class="p">,</span><span class="mi">8</span> <span class="o">+</span><span class="mi">50</span><span class="p">,</span><span class="mi">10</span> <span class="err">@@</span> <span class="kt">int</span> <span class="nf">run_all_app</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">         <span class="n">trapframe</span><span class="o">-&gt;</span><span class="n">sp</span>  <span class="o">=</span> <span class="p">(</span><span class="n">uint64</span><span class="p">)</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">ustack</span> <span class="o">+</span> <span class="n">USER_STACK_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">         <span class="n">p</span><span class="o">-&gt;</span><span class="n">state</span>       <span class="o">=</span> <span class="n">RUNNABLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">         <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-		* LAB1: you may need to initialize your new fields of proc here
</span></span></span><span class="line"><span class="cl"><span class="cm">+		* LAB1: 初始化系统调用数以及进程开始时间
</span></span></span><span class="line"><span class="cl"><span class="cm"> 		*/</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="nf">memset</span><span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">syscall_times</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MAX_SYSCALL_NUM</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uint32</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">p</span><span class="o">-&gt;</span><span class="n">start_time</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="p">}</span>
</span></span><span class="line"><span class="cl">     <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="err">\</span> <span class="n">No</span> <span class="n">newline</span> <span class="n">at</span> <span class="n">end</span> <span class="n">of</span> <span class="n">file</span>
</span></span><span class="line"><span class="cl"><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">c</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="n">index</span> <span class="n">fee3886</span><span class="p">.</span><span class="mf">.0</span><span class="n">c69ae5</span> <span class="mi">100644</span>
</span></span><span class="line"><span class="cl"><span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span><span class="mi">6</span> <span class="o">+</span><span class="mi">2</span><span class="p">,</span><span class="mi">7</span> <span class="err">@@</span>
</span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;defs.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;loader.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;trap.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="err">#</span><span class="n">include</span> <span class="s">&#34;timer.h&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">proc</span> <span class="n">pool</span><span class="p">[</span><span class="n">NPROC</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kt">char</span>        <span class="n">kstack</span><span class="p">[</span><span class="n">NPROC</span><span class="p">][</span><span class="n">PAGE_SIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">33</span><span class="p">,</span><span class="mi">9</span> <span class="o">+</span><span class="mi">34</span><span class="p">,</span><span class="mi">8</span> <span class="err">@@</span> <span class="kt">void</span> <span class="nf">proc_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">         <span class="n">p</span><span class="o">-&gt;</span><span class="n">kstack</span>    <span class="o">=</span> <span class="p">(</span><span class="n">uint64</span><span class="p">)</span><span class="n">kstack</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="n">pool</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">         <span class="n">p</span><span class="o">-&gt;</span><span class="n">ustack</span>    <span class="o">=</span> <span class="p">(</span><span class="n">uint64</span><span class="p">)</span><span class="n">ustack</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="n">pool</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">         <span class="n">p</span><span class="o">-&gt;</span><span class="n">trapframe</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">trapframe</span> <span class="o">*</span><span class="p">)</span><span class="n">trapframe</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="n">pool</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>        <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-		* LAB1: you may need to initialize your new fields of proc here
</span></span></span><span class="line"><span class="cl"><span class="cm">-		*/</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="nf">memset</span><span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">syscall_times</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MAX_SYSCALL_NUM</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uint32</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">p</span><span class="o">-&gt;</span><span class="n">start_time</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="p">}</span>
</span></span><span class="line"><span class="cl">     <span class="n">idle</span><span class="p">.</span><span class="n">kstack</span>  <span class="o">=</span> <span class="p">(</span><span class="n">uint64</span><span class="p">)</span><span class="n">boot_stack_top</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="n">idle</span><span class="p">.</span><span class="n">pid</span>     <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">47</span><span class="p">,</span><span class="mi">6</span> <span class="o">+</span><span class="mi">47</span><span class="p">,</span><span class="mi">7</span> <span class="err">@@</span> <span class="kt">int</span> <span class="nf">allocpid</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">     <span class="k">static</span> <span class="kt">int</span> <span class="n">PID</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">return</span> <span class="n">PID</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// 在进程表中寻找一个未使用的进程。
</span></span></span><span class="line"><span class="cl"> <span class="c1">// 如果找到，则初始化在内核中运行所需的状态。
</span></span></span><span class="line"><span class="cl"> <span class="c1">// 如果没有空闲的进程，或者内存分配失败，则返回 0。
</span></span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">80</span><span class="p">,</span><span class="mi">14</span> <span class="o">+</span><span class="mi">81</span><span class="p">,</span><span class="mi">18</span> <span class="err">@@</span> <span class="kt">void</span> <span class="nf">scheduler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="n">last_checked_proc</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span> <span class="c1">// 初始化指针为 pool
</span></span></span><span class="line"><span class="cl"><span class="o">-</span>
</span></span><span class="line"><span class="cl">     <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">         <span class="k">for</span> <span class="p">(</span><span class="n">p</span> <span class="o">=</span> <span class="n">last_checked_proc</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="o">&amp;</span><span class="n">pool</span><span class="p">[</span><span class="n">NPROC</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">              <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 将 p 初始化为 last_checked_proc
</span></span></span><span class="line"><span class="cl">             <span class="k">if</span> <span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">RUNNABLE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                 <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-                * LAB1：你可能需要在这里初始化进程的起始时间
</span></span></span><span class="line"><span class="cl"><span class="cm">+                * LAB1：在这里初始化进程的开始时间
</span></span></span><span class="line"><span class="cl"><span class="cm">                 */</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>                <span class="k">if</span> <span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">start_time</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>                    <span class="n">uint64</span> <span class="n">cycle</span> <span class="o">=</span> <span class="nf">get_cycle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>                    <span class="n">p</span><span class="o">-&gt;</span><span class="n">start_time</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>                        <span class="p">(</span><span class="n">cycle</span> <span class="o">%</span> <span class="n">CPU_FREQ</span><span class="p">)</span> <span class="o">*</span> <span class="n">MILLISECONDS_PER_SECOND</span> <span class="o">/</span> <span class="n">CPU_FREQ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                 <span class="n">p</span><span class="o">-&gt;</span><span class="n">state</span>     <span class="o">=</span> <span class="n">RUNNING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                 <span class="n">current_proc</span> <span class="o">=</span> <span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                 <span class="nf">swtch</span><span class="p">(</span><span class="o">&amp;</span><span class="n">idle</span><span class="p">.</span><span class="n">context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">context</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">h</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="n">index</span> <span class="n">d208c5d</span><span class="p">.</span><span class="mf">.53576</span><span class="n">bf</span> <span class="mi">100644</span>
</span></span><span class="line"><span class="cl"><span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">proc</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span><span class="mi">7</span> <span class="o">+</span><span class="mi">3</span><span class="p">,</span><span class="mi">8</span> <span class="err">@@</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;types.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="err">#</span><span class="n">define</span> <span class="nf">NPROC</span> <span class="p">(</span><span class="mi">16</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="err">#</span><span class="n">define</span> <span class="nf">NPROC</span>           <span class="p">(</span><span class="mi">16</span><span class="p">)</span>  <span class="c1">// 最大进程数
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="err">#</span><span class="n">define</span> <span class="nf">MAX_SYSCALL_NUM</span> <span class="p">(</span><span class="mi">500</span><span class="p">)</span> <span class="c1">// 最大系统调用数
</span></span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="c1">// Saved registers for kernel context switches.
</span></span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">context</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">42</span><span class="p">,</span><span class="mi">14</span> <span class="o">+</span><span class="mi">43</span><span class="p">,</span><span class="mi">28</span> <span class="err">@@</span> <span class="k">struct</span> <span class="n">proc</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="n">uint64</span>            <span class="n">kstack</span><span class="p">;</span>    <span class="c1">// 进程内核栈虚拟地址 (内核页表)
</span></span></span><span class="line"><span class="cl">     <span class="k">struct</span> <span class="n">trapframe</span> <span class="o">*</span><span class="n">trapframe</span><span class="p">;</span> <span class="c1">// 进程中断帧
</span></span></span><span class="line"><span class="cl">     <span class="k">struct</span> <span class="n">context</span>    <span class="n">context</span><span class="p">;</span> <span class="c1">// 用于保存进程内核态的寄存器信息，进程切换时使用
</span></span></span><span class="line"><span class="cl"><span class="o">-</span>                               <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-	* LAB1: you may need to add some new fields here
</span></span></span><span class="line"><span class="cl"><span class="cm">+    /*
</span></span></span><span class="line"><span class="cl"><span class="cm">+	* LAB1: 添加一些新的成员用于新的 sys_task_info 系统调用
</span></span></span><span class="line"><span class="cl"><span class="cm"> 	*/</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">uint32</span> <span class="n">syscall_times</span><span class="p">[</span><span class="n">MAX_SYSCALL_NUM</span><span class="p">];</span> <span class="c1">// 系统调用次数统计 TODO: 后续改为指针
</span></span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">uint64</span> <span class="n">start_time</span><span class="p">;</span>                     <span class="c1">// 进程开始运行时间
</span></span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-* LAB1: you may need to define struct for TaskInfo here
</span></span></span><span class="line"><span class="cl"><span class="cm">+* LAB1: 定义 TaskInfo 结构体
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">UnInit</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">Ready</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">Running</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">Exited</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="p">}</span> <span class="n">TaskStatus</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">TaskStatus</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">uint32</span>     <span class="n">syscall_times</span><span class="p">[</span><span class="n">MAX_SYSCALL_NUM</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kt">int</span>        <span class="n">time</span><span class="p">;</span> <span class="c1">// 进程运行时间统计
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="p">}</span> <span class="n">TaskInfo</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="nf">curr_proc</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="kt">void</span>         <span class="nf">exit</span><span class="p">(</span><span class="kt">int</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall</span><span class="p">.</span><span class="n">c</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="n">index</span> <span class="mi">1</span><span class="n">cc5aeb</span><span class="p">..</span><span class="n">f54ed86</span> <span class="mi">100644</span>
</span></span><span class="line"><span class="cl"><span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">4</span><span class="p">,</span><span class="mi">6</span> <span class="o">+</span><span class="mi">4</span><span class="p">,</span><span class="mi">7</span> <span class="err">@@</span>
</span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;syscall_ids.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;timer.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;trap.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="err">#</span><span class="n">include</span> <span class="s">&#34;proc.h&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="n">uint64</span> <span class="nf">sys_write</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">,</span> <span class="n">uint</span> <span class="n">len</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">31</span><span class="p">,</span><span class="mi">14</span> <span class="o">+</span><span class="mi">32</span><span class="p">,</span><span class="mi">46</span> <span class="err">@@</span> <span class="n">uint64</span> <span class="nf">sys_sched_yield</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">uint64</span> <span class="nf">sys_gettimeofday</span><span class="p">(</span><span class="n">TimeVal</span> <span class="o">*</span><span class="n">val</span><span class="p">,</span> <span class="kt">int</span> <span class="n">_tz</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="n">uint64</span> <span class="n">cycle</span> <span class="o">=</span> <span class="nf">get_cycle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">val</span><span class="o">-&gt;</span><span class="n">sec</span>     <span class="o">=</span> <span class="n">cycle</span> <span class="o">/</span> <span class="n">CPU_FREQ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">val</span><span class="o">-&gt;</span><span class="n">usec</span>    <span class="o">=</span> <span class="p">(</span><span class="n">cycle</span> <span class="o">%</span> <span class="n">CPU_FREQ</span><span class="p">)</span> <span class="o">*</span> <span class="n">MICROSECONDS_PER_SECOND</span> <span class="o">/</span> <span class="n">CPU_FREQ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nf">tracef</span><span class="p">(</span><span class="s">&#34;sys_gettimeofday cycle = %d&#34;</span><span class="p">,</span> <span class="n">cycle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">val</span><span class="o">-&gt;</span><span class="n">sec</span>  <span class="o">=</span> <span class="n">cycle</span> <span class="o">/</span> <span class="n">CPU_FREQ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">val</span><span class="o">-&gt;</span><span class="n">msec</span> <span class="o">=</span> <span class="p">(</span><span class="n">cycle</span> <span class="o">%</span> <span class="n">CPU_FREQ</span><span class="p">)</span> <span class="o">*</span> <span class="n">MILLISECONDS_PER_SECOND</span> <span class="o">/</span> <span class="n">CPU_FREQ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">val</span><span class="o">-&gt;</span><span class="n">usec</span> <span class="o">=</span> <span class="p">(</span><span class="n">cycle</span> <span class="o">%</span> <span class="n">CPU_FREQ</span><span class="p">)</span> <span class="o">*</span> <span class="n">MICROSECONDS_PER_SECOND</span> <span class="o">/</span> <span class="n">CPU_FREQ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-* LAB1: you may need to define sys_task_info here
</span></span></span><span class="line"><span class="cl"><span class="cm">-*/</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="cm">/** 
</span></span></span><span class="line"><span class="cl"><span class="cm">+ * LAB1：此处定义 sys_task_info 函数
</span></span></span><span class="line"><span class="cl"><span class="cm">+ * 查询当前正在执行的任务信息，任务信息包括任务控制块相关信息（任务状态）、任务使用的系统调用次数、任务总运行时长。 
</span></span></span><span class="line"><span class="cl"><span class="cm">+ */</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="kt">int</span> <span class="nf">sys_task_info</span><span class="p">(</span><span class="n">TaskInfo</span> <span class="o">*</span><span class="n">ti</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="n">proc</span> <span class="o">=</span> <span class="nf">curr_proc</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="c1">// TODO: proc 检查为空
</span></span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">MAX_SYSCALL_NUM</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ti</span><span class="o">-&gt;</span><span class="n">syscall_times</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">proc</span><span class="o">-&gt;</span><span class="n">syscall_times</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">uint64</span> <span class="n">cycle</span> <span class="o">=</span> <span class="nf">get_cycle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">uint64</span> <span class="n">current_time</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="p">(</span><span class="n">cycle</span> <span class="o">%</span> <span class="n">CPU_FREQ</span><span class="p">)</span> <span class="o">*</span> <span class="n">MILLISECONDS_PER_SECOND</span> <span class="o">/</span> <span class="n">CPU_FREQ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nf">infof</span><span class="p">(</span><span class="s">&#34;sys_task_info current_time = %d&#34;</span><span class="p">,</span> <span class="n">current_time</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nf">infof</span><span class="p">(</span><span class="s">&#34;proc-&gt;start_time = %d&#34;</span><span class="p">,</span> <span class="n">proc</span><span class="o">-&gt;</span><span class="n">start_time</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nf">infof</span><span class="p">(</span><span class="s">&#34;ti-&gt;time = %d&#34;</span><span class="p">,</span> <span class="n">current_time</span> <span class="o">-</span> <span class="n">proc</span><span class="o">-&gt;</span><span class="n">start_time</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="k">if</span> <span class="p">(</span><span class="n">proc</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">RUNNING</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ti</span><span class="o">-&gt;</span><span class="n">status</span> <span class="o">=</span> <span class="n">Running</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">proc</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">RUNNABLE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ti</span><span class="o">-&gt;</span><span class="n">status</span> <span class="o">=</span> <span class="n">Ready</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">proc</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">SLEEPING</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ti</span><span class="o">-&gt;</span><span class="n">status</span> <span class="o">=</span> <span class="n">Ready</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">proc</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">ZOMBIE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ti</span><span class="o">-&gt;</span><span class="n">status</span> <span class="o">=</span> <span class="n">Exited</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">proc</span><span class="o">-&gt;</span><span class="n">state</span> <span class="o">==</span> <span class="n">UNUSED</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ti</span><span class="o">-&gt;</span><span class="n">status</span> <span class="o">=</span> <span class="n">UnInit</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">time</span> <span class="o">=</span> <span class="n">current_time</span> <span class="o">-</span> <span class="n">proc</span><span class="o">-&gt;</span><span class="n">start_time</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="k">extern</span> <span class="kt">char</span> <span class="n">trap_page</span><span class="p">[];</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">51</span><span class="p">,</span><span class="mi">8</span> <span class="o">+</span><span class="mi">84</span><span class="p">,</span><span class="mi">9</span> <span class="err">@@</span> <span class="kt">void</span> <span class="nf">syscall</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">     <span class="nf">tracef</span><span class="p">(</span><span class="s">&#34;syscall %d args = [%x, %x, %x, %x, %x, %x]&#34;</span><span class="p">,</span> <span class="n">id</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">args</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span> <span class="n">args</span><span class="p">[</span><span class="mi">4</span><span class="p">],</span> <span class="n">args</span><span class="p">[</span><span class="mi">5</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">     <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-	* LAB1: you may need to update syscall counter for task info here
</span></span></span><span class="line"><span class="cl"><span class="cm">+	* LAB1: 更新系统调用次数
</span></span></span><span class="line"><span class="cl"><span class="cm"> 	*/</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nf">curr_proc</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">syscall_times</span><span class="p">[</span><span class="n">id</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">switch</span> <span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="k">case</span> <span class="nl">SYS_write</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">         <span class="n">ret</span> <span class="o">=</span> <span class="nf">sys_write</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">67</span><span class="p">,</span><span class="mi">8</span> <span class="o">+</span><span class="mi">101</span><span class="p">,</span><span class="mi">15</span> <span class="err">@@</span> <span class="kt">void</span> <span class="nf">syscall</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">         <span class="n">ret</span> <span class="o">=</span> <span class="nf">sys_gettimeofday</span><span class="p">((</span><span class="n">TimeVal</span> <span class="o">*</span><span class="p">)</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">         <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-	* LAB1: you may need to add SYS_taskinfo case here
</span></span></span><span class="line"><span class="cl"><span class="cm">+	* LAB1: 在此处添加 SYS_task_info 的系统调用处理情况
</span></span></span><span class="line"><span class="cl"><span class="cm"> 	*/</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="k">case</span> <span class="nl">SYS_task_info</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ret</span> <span class="o">=</span> <span class="nf">sys_task_info</span><span class="p">((</span><span class="n">TaskInfo</span> <span class="o">*</span><span class="p">)</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="k">case</span> <span class="nl">SYS_getpid</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="nf">infof</span><span class="p">(</span><span class="s">&#34;SYS_getpid %d&#34;</span><span class="p">,</span> <span class="n">SYS_getpid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">ret</span> <span class="o">=</span> <span class="nf">curr_proc</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">pid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">         <span class="n">ret</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">         <span class="nf">errorf</span><span class="p">(</span><span class="s">&#34;unknown syscall %d&#34;</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall_ids</span><span class="p">.</span><span class="n">h</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall_ids</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="n">index</span> <span class="mo">05</span><span class="n">a6cb9</span><span class="p">.</span><span class="mf">.3</span><span class="n">c1a5a9</span> <span class="mi">100644</span>
</span></span><span class="line"><span class="cl"><span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall_ids</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">syscall_ids</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">277</span><span class="p">,</span><span class="mi">9</span> <span class="o">+</span><span class="mi">277</span><span class="p">,</span><span class="mi">8</span> <span class="err">@@</span>
</span></span><span class="line"><span class="cl"> <span class="cp">#define SYS_io_pgetevents          292
</span></span></span><span class="line"><span class="cl"> <span class="cp">#define SYS_rseq                   293
</span></span></span><span class="line"><span class="cl"> <span class="cp">#define SYS_kexec_file_load        294
</span></span></span><span class="line"><span class="cl"><span class="o">-</span><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">-* LAB1: you may need to define SYS_task_info here
</span></span></span><span class="line"><span class="cl"><span class="cm">-*/</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span><span class="c1">// LAB1：添加 SYS_task_info 的系统调用号
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="err">#</span><span class="n">define</span> <span class="n">SYS_task_info</span>          <span class="mi">410</span>
</span></span><span class="line"><span class="cl"> <span class="cp">#define SYS_pidfd_send_signal  424
</span></span></span><span class="line"><span class="cl"> <span class="cp">#define SYS_io_uring_setup     425
</span></span></span><span class="line"><span class="cl"> <span class="cp">#define SYS_io_uring_enter     426
</span></span></span><span class="line"><span class="cl"><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">timer</span><span class="p">.</span><span class="n">h</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">timer</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="n">index</span> <span class="n">c6ebd14</span><span class="p">.</span><span class="mf">.63</span><span class="n">ab45c</span> <span class="mi">100644</span>
</span></span><span class="line"><span class="cl"><span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">timer</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">os</span><span class="o">/</span><span class="n">timer</span><span class="p">.</span><span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">6</span><span class="p">,</span><span class="mi">6</span> <span class="o">+</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span> <span class="err">@@</span>
</span></span><span class="line"><span class="cl"> <span class="cp">#define TICKS_PER_SEC (100)
</span></span></span><span class="line"><span class="cl"> <span class="c1">// QEMU
</span></span></span><span class="line"><span class="cl"> <span class="cp">#define CPU_FREQ                (12500000)
</span></span></span><span class="line"><span class="cl"><span class="o">+</span><span class="err">#</span><span class="n">define</span> <span class="nf">MILLISECONDS_PER_SECOND</span> <span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="cp">#define MICROSECONDS_PER_SECOND (1000000)
</span></span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="n">uint64</span> <span class="nf">get_cycle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="err">@@</span> <span class="o">-</span><span class="mi">14</span><span class="p">,</span><span class="mi">6</span> <span class="o">+</span><span class="mi">15</span><span class="p">,</span><span class="mi">7</span> <span class="err">@@</span> <span class="kt">void</span>   <span class="nf">set_next_timer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="n">uint64</span> <span class="n">sec</span><span class="p">;</span>  <span class="c1">// 自 Unix 纪元起的秒数
</span></span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">uint64</span> <span class="n">msec</span><span class="p">;</span> <span class="c1">// 毫秒数
</span></span></span><span class="line"><span class="cl">     <span class="n">uint64</span> <span class="n">usec</span><span class="p">;</span> <span class="c1">// 微秒数
</span></span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="n">TimeVal</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">--</span> 
</span></span><span class="line"><span class="cl"><span class="mf">2.34.1</span>
</span></span></code></pre></div><h1 id="问答作业">问答作业</h1>
<h2 id="问题一">问题一</h2>
<p>正确进入 U 态后，程序的特征还应有：使用 S 态特权指令，访问 S 态寄存器后会报错。请同学们可以自行测试这些内容（参考 前三个测例，描述程序出错行为，同时注意注明你使用的 sbi 及其版本。</p>
<p>测试前三个测试用例指的是<code>uCore-Tutorial-Code-2023S/user/src/</code> 目录下的三个<code>bad</code>测试用例，查看<code>user</code>项目的 Makefile 可以发现在编译时修改<code>CHAPTER</code>参数值为<code>2_bad</code>即可编译运行这些测试用例。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> RustSBI version 0.3.0-alpha.2, adapting to RISC-V SBI v1.0.0
</span></span><span class="line"><span class="cl">.______       __    __      _______.___________.  _______..______   __
</span></span><span class="line"><span class="cl"><span class="p">|</span>   _  <span class="se">\ </span>    <span class="p">|</span>  <span class="p">|</span>  <span class="p">|</span>  <span class="p">|</span>    /       <span class="p">|</span>           <span class="p">|</span> /       <span class="o">||</span>   _  <span class="se">\ </span><span class="p">|</span>  <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>  <span class="p">|</span>_<span class="o">)</span>  <span class="p">|</span>    <span class="p">|</span>  <span class="p">|</span>  <span class="p">|</span>  <span class="p">|</span>   <span class="p">|</span>   <span class="o">(</span>----<span class="sb">`</span>---<span class="p">|</span>  <span class="p">|</span>----<span class="sb">`</span><span class="p">|</span>   <span class="o">(</span>----<span class="sb">`</span><span class="p">|</span>  <span class="p">|</span>_<span class="o">)</span>  <span class="o">||</span>  <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>      /     <span class="p">|</span>  <span class="p">|</span>  <span class="p">|</span>  <span class="p">|</span>    <span class="se">\ </span>  <span class="se">\ </span>      <span class="p">|</span>  <span class="p">|</span>      <span class="se">\ </span>  <span class="se">\ </span>   <span class="p">|</span>   _  &lt; <span class="p">|</span>  <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>  <span class="p">|</span><span class="se">\ </span> <span class="se">\-</span>---.<span class="p">|</span>  <span class="sb">`</span>--<span class="err">&#39;</span>  <span class="p">|</span>.----<span class="o">)</span>   <span class="p">|</span>      <span class="p">|</span>  <span class="p">|</span>  .----<span class="o">)</span>   <span class="p">|</span>   <span class="p">|</span>  <span class="p">|</span>_<span class="o">)</span>  <span class="o">||</span>  <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> _<span class="p">|</span> <span class="sb">`</span>._____<span class="p">|</span> <span class="se">\_</span>_____/ <span class="p">|</span>_______/       <span class="p">|</span>__<span class="p">|</span>  <span class="p">|</span>_______/    <span class="p">|</span>______/ <span class="p">|</span>__<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Implementation     : RustSBI-QEMU Version 0.2.0-alpha.2
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Platform Name      : riscv-virtio,qemu
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Platform SMP       : <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Platform Memory    : 0x80000000..0x88000000
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Boot HART          : <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Device Tree Region : 0x87000000..0x87000ef2
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Firmware Address   : 0x80000000
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> Supervisor Address : 0x80200000
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> pmp01: 0x00000000..0x80000000 <span class="o">(</span>-wr<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> pmp02: 0x80000000..0x80200000 <span class="o">(</span>---<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> pmp03: 0x80200000..0x88000000 <span class="o">(</span>xwr<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>rustsbi<span class="o">]</span> pmp04: 0x88000000..0x00000000 <span class="o">(</span>-wr<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>TRACE 0<span class="o">]</span>load app <span class="m">0</span> at 0x0000000080400000
</span></span><span class="line"><span class="cl"><span class="o">[</span>TRACE 0<span class="o">]</span>load app <span class="m">1</span> at 0x0000000080420000
</span></span><span class="line"><span class="cl"><span class="o">[</span>TRACE 0<span class="o">]</span>load app <span class="m">2</span> at 0x0000000080440000
</span></span><span class="line"><span class="cl"><span class="o">[</span>INFO 0<span class="o">]</span>start scheduler!
</span></span><span class="line"><span class="cl"><span class="o">[</span>ERROR 1<span class="o">]</span>unknown trap: 0x0000000000000007, <span class="nv">stval</span> <span class="o">=</span> 0x0000000000000000
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>INFO 1<span class="o">]</span>进程 <span class="m">1</span> 以代码 -1 退出
</span></span><span class="line"><span class="cl">IllegalInstruction in application, core dumped.
</span></span><span class="line"><span class="cl"><span class="o">[</span>INFO 2<span class="o">]</span>进程 <span class="m">2</span> 以代码 -3 退出
</span></span><span class="line"><span class="cl">IllegalInstruction in application, core dumped.
</span></span><span class="line"><span class="cl"><span class="o">[</span>INFO 3<span class="o">]</span>进程 <span class="m">3</span> 以代码 -3 退出
</span></span><span class="line"><span class="cl"><span class="o">[</span>PANIC 3<span class="o">]</span> os/loader.c:15: all apps over
</span></span></code></pre></div><p>第一个进程测试用例如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span><span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在您提供的代码中，将空指针分配给指针变量*p 后，试图对其进行解引用并将值 0 赋给该指针。由于用户模式下禁止直接访问物理内存，操作系统会检测到这个非法操作并触发异常。因此，该程序 IllegalInstruction in application, core dumped.</p>
<p>在 RISC-V 架构中，U 模式是最低的用户模式，用户程序无法直接访问物理内存或其他特权级别资源。这种限制是为了确保操作系统的安全性和稳定性。</p>
<p>第二个进程测试用例如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">asm</span> <span class="k">volatile</span><span class="p">(</span><span class="s">&#34;sret&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>试图使用汇编语言执行 sret 指令，该指令用于从中断或异常处理程序返回。由于用户模式下禁止直接访问特权级别寄存器，操作系统会检测到这个非法操作并触发异常。因此，该程序 IllegalInstruction in application, core dumped。</p>
<p>第三个进程测试用例如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="n">uint64</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">asm</span> <span class="k">volatile</span><span class="p">(</span><span class="s">&#34;csrr %0, sstatus&#34;</span> <span class="o">:</span> <span class="s">&#34;=r&#34;</span><span class="p">(</span><span class="n">x</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>原因同上，试图使用汇编语言执行 csrr 指令，该指令用于从特权级别寄存器中读取值。由于用户模式下禁止直接访问特权级别寄存器，操作系统会检测到这个非法操作并触发异常。因此，该程序 IllegalInstruction in application, core dumped。</p>
<p>在操作系统代码中，触发异常后会进入<code>void usertrap()</code> 函数，该函数会根据 <code>scause</code> 寄存器的值判断异常类型，用例中的结果进入了<code>case IllegalInstruction</code>，其中 <code>IllegalInstruction = 2</code>。我们查阅手册 <code>riscv-privileged.pdf</code> ，可以查到 <code>IllegalInstruction</code> 的值为 2，与预期相符。</p>
<h2 id="问题二">问题二</h2>
<p>请结合用例理解 trampoline.S 中两个函数 <code>userret</code> 和 <code>uservec</code> 的作用，并回答如下几个问题：</p>
<h3 id="l79-刚进入-userret-时a0a1-分别代表了什么值">L79: 刚进入 <code>userret</code> 时，<code>a0</code>、<code>a1</code> 分别代表了什么值。</h3>
<p>在进入<code>userret</code>函数时，<code>a0</code>和<code>a1</code>分别代表以下值：</p>
<ul>
<li><code>a0</code>: TRAPFRAME 的地址，指向当前进程的陷阱帧（trapframe）结构体。</li>
<li><code>a1</code>: 用户页表的地址，即进程的页表（pagetable）。这个地址会被写入到<code>satp</code>寄存器中，用于切换到用户模式的页表。</li>
</ul>
<h3 id="l87-l88-sfence-指令有何作用为什么要执行该指令当前章节中删掉该指令会导致错误吗">L87-L88: <code>sfence</code> 指令有何作用？为什么要执行该指令，当前章节中，删掉该指令会导致错误吗？</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nf">csrw</span> <span class="no">satp</span><span class="p">,</span> <span class="no">a1</span>
</span></span><span class="line"><span class="cl"><span class="nf">sfence.vma</span> <span class="no">zero</span><span class="p">,</span> <span class="no">zero</span>
</span></span></code></pre></div><p><code>sfence</code>指令（Store Fence）的作用是确保之前的存储操作完成，并且对其他处理器上的核心可见。</p>
<p>执行<code>sfence</code>指令的主要目的是为了保证内存访问的顺序性和一致性。在多核处理器系统中，不同的核心可能会有自己的缓存，当一个核心修改了共享内存中的数据时，为了保证其他核心能够看到这个修改，需要使用<code>sfence</code>指令来刷新缓存并将修改写回共享内存。</p>
<p>在代码中，<code>sfence</code>指令被用于确保对用户页表的修改对其他处理器上的核心可见。因为目前我只使用了单核处理器，所以不会出现多核处理器的情况，因此<code>sfence</code>指令的作用是确保对用户页表的修改对当前核心可见。</p>
<p>因此，当前章节中，<strong>删掉该指令不会导致错误</strong>。</p>
<h3 id="l96-l125-为何注释中说要除去-a0哪一个地址代表-a0现在-a0-的值存在何处">L96-L125: 为何注释中说要除去 <code>a0</code>？哪一个地址代表 <code>a0</code>？现在 <code>a0</code> 的值存在何处？</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># restore all but a0 from TRAPFRAME
</span></span><span class="line"><span class="cl">ld ra, 40(a0)
</span></span><span class="line"><span class="cl">ld sp, 48(a0)
</span></span><span class="line"><span class="cl">ld t5, 272(a0)
</span></span><span class="line"><span class="cl">ld t6, 280(a0)
</span></span></code></pre></div><p><code>a0</code> 是<strong>保存在 <code>sscratch</code> 寄存器中的</strong>，首先，该代码通过 <code>ld</code> 指令从 <code>TRAPFRAME</code> 中加载各个寄存器的值。然后，这些值被存储在相应的寄存器中，以便在恢复用户上下文时使用。</p>
<p>接下来，代码使用 <code>csrrw</code> 指令将 sscratch 寄存器的值与 <code>a0</code>（即 <code>TRAPFRAME</code>）进行交换。这样做是为了将用户的 <code>a0</code>（<code>TRAPFRAME</code>）保存在 <code>sscratch</code> 寄存器中，以便后续步骤可以正确地恢复用户上下文。</p>
<p>最后，通过 <code>sret</code> 指令返回到用户模式，并将控制权交给用户代码。在执行 <code>sret</code> 指令后，处理器将根据用户上下文中的 <code>sepc</code> 寄存器的值跳转到用户代码的指令地址。返回的同时，处理器还会自动恢复 <code>sstatus</code> 寄存器的值，以确保正确的特权级别和中断状态。</p>
<h3 id="userret中发生状态切换在哪一条指令为何执行之后会进入用户态"><code>userret</code>：中发生状态切换在哪一条指令？为何执行之后会进入用户态？</h3>
<p>在<code>userret</code>函数中，发生状态切换的指令是<code>sret</code>指令。</p>
<p><code>sret</code>指令用于从内核模式切换到用户模式，并将控制权交给用户代码。执行<code>sret</code>指令后，处理器会根据用户上下文中的<code>sepc</code>寄存器的值跳转到用户代码的指令地址。</p>
<p>执行<code>sret</code>指令之后进入用户态的原因是，该指令会自动恢复<code>sstatus</code>寄存器的值，以确保正确的特权级别和中断状态。当<code>sret</code>指令执行后，处理器将从内核态切换回用户态，程序将继续执行用户代码。这意味着<code>userret</code>函数成功完成了从内核切换到用户模式的过程。</p>
<h3 id="l29执行之后a0-和-sscratch-中各是什么值为什么">L29：执行之后，a0 和 sscratch 中各是什么值，为什么？</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">csrrw a0, sscratch, a0     
</span></span></code></pre></div><p>在执行指令后，<code>a0</code>和<code>sscratch</code>中的值发生了互换。</p>
<p>假设原始<code>a0</code>寄存器中的值为 X，而<code>sscratch</code>寄存器中的值为 Y。执行<code>csrrw a0, sscratch, a0</code>指令后，<code>a0</code>寄存器中的值变为 Y，而<code>sscratch</code>寄存器中的值变为 X。</p>
<p>这是因为<code>csrrw</code>指令是一个特权指令，用于将某个 CSR（Control and Status Register）的值读取到目标寄存器，然后将目标寄存器的值写回到该 CSR 中。在这里，<code>csrrw a0, sscratch, a0</code>指令将<code>sscratch</code>寄存器的值读取到<code>a0</code>寄存器中，同时将<code>a0</code>寄存器中的值写回到<code>sscratch</code>寄存器中，从而实现了两者之间的数据交换。</p>
<h3 id="l32-l61-从-trapframe-第几项开始保存为什么是否从该项开始保存了所有的值如果不是为什么">L32-L61: 从 trapframe 第几项开始保存？为什么？是否从该项开始保存了所有的值，如果不是，为什么？</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sd ra, 40(a0)
</span></span><span class="line"><span class="cl">sd sp, 48(a0)
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">sd t5, 272(a0)
</span></span><span class="line"><span class="cl">sd t6, 280(a0)
</span></span></code></pre></div><h3 id="进入-s-态是哪一条指令发生的">进入 S 态是哪一条指令发生的？</h3>
<h3 id="l75-l76-ld-t0-16a0-执行之后t0中的值是什么解释该值的由来">L75-L76: <code>ld t0, 16(a0)</code> 执行之后，<code>t0</code>中的值是什么，解释该值的由来？</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ld t0, 16(a0)
</span></span><span class="line"><span class="cl">jr t0
</span></span></code></pre></div><p><code>ld t0, 16(a0)</code>就是从 <code>trapframe</code> 中恢复 <code>t0</code>寄存器值，<code>t0</code>保存了<code>kernel_trap</code>的入口地址。使用 <code>jr t0</code>，就跳转到了我们早先设定在 <code>trapframe-&gt;kernel_trap</code> 中的地址，也就是 <code>trap.c</code> 之中的 <code>usertrap</code> 函数。这个函数在 <code>main</code> 的初始化之中已经调用了。</p>
]]></content:encoded>
    </item>
    <item>
      <title>yq 为 yaml 文件内容排序</title>
      <link>https://lifeislife.cn/posts/yq%E4%B8%BAyaml%E6%96%87%E4%BB%B6%E5%86%85%E5%AE%B9%E6%8E%92%E5%BA%8F/</link>
      <pubDate>Fri, 01 Sep 2023 21:37:25 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/yq%E4%B8%BAyaml%E6%96%87%E4%BB%B6%E5%86%85%E5%AE%B9%E6%8E%92%E5%BA%8F/</guid>
      <description>&lt;h1 id=&#34;背景&#34;&gt;背景&lt;/h1&gt;
&lt;p&gt;配置 &lt;code&gt;yaml&lt;/code&gt; 文件时会遇到需要将配置的内容按照键值排序的情况，比如下面这样&lt;code&gt;riscv_fork_list.yaml&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;packages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;accumulo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;abseil-cpp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;acpica-tools&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;acpid&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;activemq&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;afflib&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;adcli&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;adwaita-icon-theme&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;aide&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;alsa-lib&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;amtk&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;anaconda&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-sshd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;annobin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;antlr3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-csv&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;aom&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-beanutils&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-daemon&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-el&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-exec&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-jexl&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-poi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-rat&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我想按照 &lt;code&gt;name&lt;/code&gt; 的字母顺序排序，可以使用 &lt;code&gt;yq&lt;/code&gt; 工具来实现。&lt;/p&gt;
&lt;h1 id=&#34;安装-yq&#34;&gt;安装 yq&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    chmod +x /usr/bin/yq
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;使用-yq&#34;&gt;使用 yq&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yq -i &lt;span class=&#34;s1&#34;&gt;&amp;#39;.packages |= sort_by(.name)&amp;#39;&lt;/span&gt; riscv_fork_list.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;packages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;abseil-cpp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;accumulo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;acpica-tools&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;acpid&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;activemq&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;adcli&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;adwaita-icon-theme&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;afflib&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;aide&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;alsa-lib&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;amtk&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;anaconda&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;annobin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;antlr3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;aom&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-beanutils&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-csv&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-daemon&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-el&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-exec&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-commons-jexl&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-poi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-rat&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;apache-sshd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="背景">背景</h1>
<p>配置 <code>yaml</code> 文件时会遇到需要将配置的内容按照键值排序的情况，比如下面这样<code>riscv_fork_list.yaml</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">packages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">accumulo</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">abseil-cpp</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">acpica-tools</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">acpid</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">activemq</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">afflib</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">adcli</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">adwaita-icon-theme</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">aide</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">alsa-lib</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">amtk</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">anaconda</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-sshd</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">annobin</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">antlr3</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-csv</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">aom</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-beanutils</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-daemon</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-el</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-exec</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-jexl</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-poi</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-rat</span><span class="w">
</span></span></span></code></pre></div><p>我想按照 <code>name</code> 的字母顺序排序，可以使用 <code>yq</code> 工具来实现。</p>
<h1 id="安装-yq">安装 yq</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq <span class="o">&amp;&amp;</span><span class="se">\
</span></span></span><span class="line"><span class="cl">    chmod +x /usr/bin/yq
</span></span></code></pre></div><h1 id="使用-yq">使用 yq</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yq -i <span class="s1">&#39;.packages |= sort_by(.name)&#39;</span> riscv_fork_list.yaml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">packages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">abseil-cpp</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">accumulo</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">acpica-tools</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">acpid</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">activemq</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">adcli</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">adwaita-icon-theme</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">afflib</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">aide</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">alsa-lib</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">amtk</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">anaconda</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">annobin</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">antlr3</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">aom</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-beanutils</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-csv</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-daemon</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-el</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-exec</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-commons-jexl</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-poi</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-rat</span><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">apache-sshd</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>解决复制 Markdown 文本到思源笔记无法转义为 Markdown 格式</title>
      <link>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3%E5%A4%8D%E5%88%B6markdown%E6%96%87%E6%9C%AC%E5%88%B0%E6%80%9D%E6%BA%90%E7%AC%94%E8%AE%B0%E6%97%A0%E6%B3%95%E8%BD%AC%E4%B9%89%E4%B8%BAmarkdown%E6%A0%BC%E5%BC%8F/</link>
      <pubDate>Fri, 01 Sep 2023 20:25:05 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3%E5%A4%8D%E5%88%B6markdown%E6%96%87%E6%9C%AC%E5%88%B0%E6%80%9D%E6%BA%90%E7%AC%94%E8%AE%B0%E6%97%A0%E6%B3%95%E8%BD%AC%E4%B9%89%E4%B8%BAmarkdown%E6%A0%BC%E5%BC%8F/</guid>
      <description>&lt;h2 id=&#34;问题描述&#34;&gt;问题描述&lt;/h2&gt;
&lt;p&gt;在 VSCode 中编辑 Markdown 文本，复制到思源笔记后，思源笔记无法转义为 Markdown 格式。会变成一个代码块，但是代码块内的内容并不是复制的内容。&lt;/p&gt;
&lt;p&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//2023/09/01/264c4a2f58447a13c50012a676bf0ed7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/01/264c4a2f58447a13c50012a676bf0ed7.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;但是我需要的是能够转义为 Markdown 的阅读模式。&lt;/p&gt;
&lt;h2 id=&#34;解决方法&#34;&gt;解决方法&lt;/h2&gt;
&lt;p&gt;问题的原因在于 VSCode 复制的文本是带格式的，而思源笔记默认的粘贴模式是纯文本模式，所以会出现上面的问题。&lt;/p&gt;
&lt;p&gt;解决方法就是从 VSCode 复制的内容为纯文本，一种可以把文本复制到 &lt;code&gt;txt&lt;/code&gt; 文件中，再复制，但是比较麻烦。&lt;/p&gt;
&lt;p&gt;第二种方法是使用 VSCode 的插件 &lt;code&gt;Copy Plain Text&lt;/code&gt;，搜索下载后，默认快捷键为 &lt;code&gt;Ctrl+Alt+C&lt;/code&gt;，可以复制为纯文本。&lt;/p&gt;
&lt;p&gt;再次粘贴到思源笔记中，就可以转义为 Markdown 格式了。&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//2023/09/01/4f9aa2d7cb07f061aabbc5977977b6cb.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/01/4f9aa2d7cb07f061aabbc5977977b6cb.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;
</description>
      <content:encoded><![CDATA[<h2 id="问题描述">问题描述</h2>
<p>在 VSCode 中编辑 Markdown 文本，复制到思源笔记后，思源笔记无法转义为 Markdown 格式。会变成一个代码块，但是代码块内的内容并不是复制的内容。</p>
<p>比如上面这段话复制到思源笔记成了下图这样：</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//2023/09/01/264c4a2f58447a13c50012a676bf0ed7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/01/264c4a2f58447a13c50012a676bf0ed7.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>但是我需要的是能够转义为 Markdown 的阅读模式。</p>
<h2 id="解决方法">解决方法</h2>
<p>问题的原因在于 VSCode 复制的文本是带格式的，而思源笔记默认的粘贴模式是纯文本模式，所以会出现上面的问题。</p>
<p>解决方法就是从 VSCode 复制的内容为纯文本，一种可以把文本复制到 <code>txt</code> 文件中，再复制，但是比较麻烦。</p>
<p>第二种方法是使用 VSCode 的插件 <code>Copy Plain Text</code>，搜索下载后，默认快捷键为 <code>Ctrl+Alt+C</code>，可以复制为纯文本。</p>
<p>再次粘贴到思源笔记中，就可以转义为 Markdown 格式了。</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//2023/09/01/4f9aa2d7cb07f061aabbc5977977b6cb.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/09/01/4f9aa2d7cb07f061aabbc5977977b6cb.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>
]]></content:encoded>
    </item>
    <item>
      <title>uCore 实验第 2 章 - 批处理系统</title>
      <link>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC2%E7%AB%A0-%E6%89%B9%E5%A4%84%E7%90%86%E7%B3%BB%E7%BB%9F/</link>
      <pubDate>Thu, 31 Aug 2023 23:16:38 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ucore-%E5%AE%9E%E9%AA%8C%E7%AC%AC2%E7%AB%A0-%E6%89%B9%E5%A4%84%E7%90%86%E7%B3%BB%E7%BB%9F/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;flowchart&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subgraph&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;entry&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;S&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;_entry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_entry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subgraph&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link_app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;S&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;_app_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_app_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subgraph&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subgraph&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;loader&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;loader_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loader_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;run_next_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run_next_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;load_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;load_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;_entry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;loader_init&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;run_next_app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;run_next_app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load_app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;loader_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_app_num&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">flowchart</span> <span class="n">TB</span>
</span></span><span class="line"><span class="cl">    <span class="n">subgraph</span> <span class="n">entry</span><span class="o">.</span><span class="n">S</span>
</span></span><span class="line"><span class="cl">        <span class="n">_entry</span><span class="p">[</span><span class="n">_entry</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">subgraph</span> <span class="n">link_app</span><span class="o">.</span><span class="n">S</span>
</span></span><span class="line"><span class="cl">        <span class="n">_app_num</span><span class="p">[</span><span class="n">_app_num</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">subgraph</span> <span class="n">main</span><span class="o">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl">        <span class="n">main</span><span class="p">[</span><span class="n">main</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">subgraph</span> <span class="n">loader</span><span class="o">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl">        <span class="n">loader_init</span><span class="p">[</span><span class="n">loader_init</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">run_next_app</span><span class="p">[</span><span class="n">run_next_app</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">load_app</span><span class="p">[</span><span class="n">load_app</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">_entry</span> <span class="o">--&gt;</span> <span class="n">main</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span> <span class="o">--&gt;</span> <span class="n">loader_init</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span> <span class="o">--&gt;</span> <span class="n">run_next_app</span>
</span></span><span class="line"><span class="cl">    <span class="n">run_next_app</span> <span class="o">--&gt;</span> <span class="n">load_app</span>
</span></span><span class="line"><span class="cl">    <span class="n">loader_init</span> <span class="o">--&gt;</span> <span class="n">_app_num</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>如何离线安装 VSCode 插件</title>
      <link>https://lifeislife.cn/posts/%E5%A6%82%E4%BD%95%E7%A6%BB%E7%BA%BF%E5%AE%89%E8%A3%85vscode%E6%8F%92%E4%BB%B6/</link>
      <pubDate>Tue, 29 Aug 2023 20:59:19 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%A6%82%E4%BD%95%E7%A6%BB%E7%BA%BF%E5%AE%89%E8%A3%85vscode%E6%8F%92%E4%BB%B6/</guid>
      <description>&lt;h1 id=&#34;背景简介&#34;&gt;背景简介&lt;/h1&gt;
&lt;p&gt;在使用 VSCode 的过程中，我们经常会安装一些插件来提高开发效率。但是，由于某些原因，我们可能无法直接访问 VSCode 的插件市场，这时候我们就需要离线安装插件了。&lt;/p&gt;
&lt;p&gt;这里存在两种情况，一种是为本地的 VSCode 安装插件，另一种是为远程的 VSCode 安装插件。本文将分别介绍这两种情况下的离线安装方法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;远程 VSCode 也就是 VSCode 的&lt;a href=&#34;https://code.visualstudio.com/docs/remote/remote-overview&#34;&gt;Remote Development&lt;/a&gt;功能，可以通过 SSH、Docker、WSL 等方式远程连接到远程主机上的 VSCode。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;方法一使用已安装的插件目录&#34;&gt;方法一：使用已安装的插件目录&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;从已经安装插件的电脑上拷贝所有插件，路径一般为 &lt;code&gt;C:\用户\用户名\.vscode\extensions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;拷贝到离线安装的电脑上的 &lt;code&gt;.vscode/extensions&lt;/code&gt; 文件夹下即可，重启 VScode 即可安装成功。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于远程 VSCode 我们需要知道，插件不区分操作系统，所以我们可以在本地的 Windows 上的 VSCode 上安装插件，然后将插件目录压缩后整个拷贝到远程主机上即可。&lt;/p&gt;
&lt;p&gt;远程主机上的插件目录一般在 &lt;code&gt;~/.vscode-server/extensions&lt;/code&gt; 下。将压缩的文件解药到这个目录下，重启 VSCode 即可。&lt;/p&gt;
&lt;h1 id=&#34;方法二下载离线安装包-vslx-安装&#34;&gt;方法二：下载离线安装包 vslx 安装&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;到 &lt;a href=&#34;https://marketplace.visualstudio.com/vscode&#34;&gt;VScode 插件中心&lt;/a&gt; 搜索需要使用的插件名称&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下载对应的拓展程序文件，下载的文件的后缀是&lt;code&gt;.vslx&lt;/code&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//2023/08/29/f4349bbfc8cf734951fc70e2f5b0eabd.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/29/f4349bbfc8cf734951fc70e2f5b0eabd.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;/li&gt;
&lt;li&gt;
&lt;p&gt;VSCode 中安装


&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//2023/08/29/25b2839add215a8a61449f0ac9b1ca81.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/29/25b2839add215a8a61449f0ac9b1ca81.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;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h1 id="背景简介">背景简介</h1>
<p>在使用 VSCode 的过程中，我们经常会安装一些插件来提高开发效率。但是，由于某些原因，我们可能无法直接访问 VSCode 的插件市场，这时候我们就需要离线安装插件了。</p>
<p>这里存在两种情况，一种是为本地的 VSCode 安装插件，另一种是为远程的 VSCode 安装插件。本文将分别介绍这两种情况下的离线安装方法。</p>
<blockquote>
<p>远程 VSCode 也就是 VSCode 的<a href="https://code.visualstudio.com/docs/remote/remote-overview">Remote Development</a>功能，可以通过 SSH、Docker、WSL 等方式远程连接到远程主机上的 VSCode。</p>
</blockquote>
<h1 id="方法一使用已安装的插件目录">方法一：使用已安装的插件目录</h1>
<ul>
<li>从已经安装插件的电脑上拷贝所有插件，路径一般为 <code>C:\用户\用户名\.vscode\extensions</code></li>
<li>拷贝到离线安装的电脑上的 <code>.vscode/extensions</code> 文件夹下即可，重启 VScode 即可安装成功。</li>
</ul>
<p>对于远程 VSCode 我们需要知道，插件不区分操作系统，所以我们可以在本地的 Windows 上的 VSCode 上安装插件，然后将插件目录压缩后整个拷贝到远程主机上即可。</p>
<p>远程主机上的插件目录一般在 <code>~/.vscode-server/extensions</code> 下。将压缩的文件解药到这个目录下，重启 VSCode 即可。</p>
<h1 id="方法二下载离线安装包-vslx-安装">方法二：下载离线安装包 vslx 安装</h1>
<ul>
<li>
<p>到 <a href="https://marketplace.visualstudio.com/vscode">VScode 插件中心</a> 搜索需要使用的插件名称</p>
</li>
<li>
<p>下载对应的拓展程序文件，下载的文件的后缀是<code>.vslx</code>


<!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//2023/08/29/f4349bbfc8cf734951fc70e2f5b0eabd.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/29/f4349bbfc8cf734951fc70e2f5b0eabd.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>
</li>
<li>
<p>VSCode 中安装


<!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//2023/08/29/25b2839add215a8a61449f0ac9b1ca81.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/29/25b2839add215a8a61449f0ac9b1ca81.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>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Windows 端口映射</title>
      <link>https://lifeislife.cn/posts/windows%E7%AB%AF%E5%8F%A3%E6%98%A0%E5%B0%84/</link>
      <pubDate>Mon, 28 Aug 2023 23:24:53 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/windows%E7%AB%AF%E5%8F%A3%E6%98%A0%E5%B0%84/</guid>
      <description>&lt;h1 id=&#34;命令行&#34;&gt;命令行&lt;/h1&gt;
&lt;p&gt;在 Windows 中，可以使用 netsh 命令来添加、查看和删除端口转发规则。&lt;/p&gt;
&lt;p&gt;要&lt;strong&gt;添加一个端口转发规则&lt;/strong&gt;，可以使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netsh interface portproxy add v4tov4 &lt;span class=&#34;nv&#34;&gt;listenaddress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;local_address&amp;gt; &lt;span class=&#34;nv&#34;&gt;listenport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;local_port&amp;gt; &lt;span class=&#34;nv&#34;&gt;connectaddress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;remote_address&amp;gt; &lt;span class=&#34;nv&#34;&gt;connectport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;remote_port&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;local_address&amp;gt;&lt;/code&gt;是本地监听的地址（可以是 IP 地址或 0.0.0.0 表示所有地址）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;local_port&amp;gt;&lt;/code&gt;是本地监听的端口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;remote_address&amp;gt;&lt;/code&gt;是转发连接到的远程地址。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;remote_port&amp;gt;&lt;/code&gt;是转发连接到的远程端口。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如，要将本地的 8080 端口转发到远程服务器上的 80 端口，可以使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netsh interface portproxy add v4tov4 &lt;span class=&#34;nv&#34;&gt;listenaddress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;127.0.0.1 &lt;span class=&#34;nv&#34;&gt;listenport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8080&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;connectaddress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;192.168.0.100 &lt;span class=&#34;nv&#34;&gt;connectport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要&lt;strong&gt;查看当前的端口转发规则&lt;/strong&gt;，可以使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netsh interface portproxy show v4tov4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要&lt;strong&gt;删除特定的端口转发规则&lt;/strong&gt;，可以使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netsh interface portproxy delete v4tov4 &lt;span class=&#34;nv&#34;&gt;listenaddress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;local_address&amp;gt; &lt;span class=&#34;nv&#34;&gt;listenport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;local_port&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中的&lt;code&gt;&amp;lt;local_address&amp;gt;&lt;/code&gt;和&lt;code&gt;&amp;lt;local_port&amp;gt;&lt;/code&gt;应该与你想删除的规则匹配。&lt;/p&gt;
&lt;p&gt;请注意，执行这些操作通常需要管理员权限。&lt;/p&gt;
&lt;h1 id=&#34;gui&#34;&gt;GUI&lt;/h1&gt;
&lt;p&gt;使用开源工具&lt;a href=&#34;https://github.com/zmjack/PortProxyGUI/releases&#34;&gt;PortProxyGUI&lt;/a&gt;可以在 UI 界面快速增删改查端口映射。&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//2023/08/28/e5bf0ce1f4a25150f69586825c2e7309.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/28/e5bf0ce1f4a25150f69586825c2e7309.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;
</description>
      <content:encoded><![CDATA[<h1 id="命令行">命令行</h1>
<p>在 Windows 中，可以使用 netsh 命令来添加、查看和删除端口转发规则。</p>
<p>要<strong>添加一个端口转发规则</strong>，可以使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">netsh interface portproxy add v4tov4 <span class="nv">listenaddress</span><span class="o">=</span>&lt;local_address&gt; <span class="nv">listenport</span><span class="o">=</span>&lt;local_port&gt; <span class="nv">connectaddress</span><span class="o">=</span>&lt;remote_address&gt; <span class="nv">connectport</span><span class="o">=</span>&lt;remote_port&gt;
</span></span></code></pre></div><p>其中：</p>
<ul>
<li><code>&lt;local_address&gt;</code>是本地监听的地址（可以是 IP 地址或 0.0.0.0 表示所有地址）。</li>
<li><code>&lt;local_port&gt;</code>是本地监听的端口。</li>
<li><code>&lt;remote_address&gt;</code>是转发连接到的远程地址。</li>
<li><code>&lt;remote_port&gt;</code>是转发连接到的远程端口。</li>
</ul>
<p>例如，要将本地的 8080 端口转发到远程服务器上的 80 端口，可以使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">netsh interface portproxy add v4tov4 <span class="nv">listenaddress</span><span class="o">=</span>127.0.0.1 <span class="nv">listenport</span><span class="o">=</span><span class="m">8080</span> <span class="nv">connectaddress</span><span class="o">=</span>192.168.0.100 <span class="nv">connectport</span><span class="o">=</span><span class="m">80</span>
</span></span></code></pre></div><p>要<strong>查看当前的端口转发规则</strong>，可以使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">netsh interface portproxy show v4tov4
</span></span></code></pre></div><p>要<strong>删除特定的端口转发规则</strong>，可以使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">netsh interface portproxy delete v4tov4 <span class="nv">listenaddress</span><span class="o">=</span>&lt;local_address&gt; <span class="nv">listenport</span><span class="o">=</span>&lt;local_port&gt;
</span></span></code></pre></div><p>其中的<code>&lt;local_address&gt;</code>和<code>&lt;local_port&gt;</code>应该与你想删除的规则匹配。</p>
<p>请注意，执行这些操作通常需要管理员权限。</p>
<h1 id="gui">GUI</h1>
<p>使用开源工具<a href="https://github.com/zmjack/PortProxyGUI/releases">PortProxyGUI</a>可以在 UI 界面快速增删改查端口映射。</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//2023/08/28/e5bf0ce1f4a25150f69586825c2e7309.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/28/e5bf0ce1f4a25150f69586825c2e7309.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>
]]></content:encoded>
    </item>
    <item>
      <title>内网穿透远程访问家里的 WSL2</title>
      <link>https://lifeislife.cn/posts/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E8%BF%9C%E7%A8%8B%E8%AE%BF%E9%97%AE%E5%AE%B6%E9%87%8C%E7%9A%84wsl2/</link>
      <pubDate>Mon, 28 Aug 2023 22:45:01 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E8%BF%9C%E7%A8%8B%E8%AE%BF%E9%97%AE%E5%AE%B6%E9%87%8C%E7%9A%84wsl2/</guid>
      <description>&lt;h1 id=&#34;背景简介&#34;&gt;背景简介&lt;/h1&gt;
&lt;p&gt;WSL2 是 Windows 的子系统，可以在 Windows 上运行 Linux，但是 WSL2 是运行在虚拟机中的，所以无法直接访问 WSL2 中的服务，比如 SSH 服务。本文介绍如何使用内网穿透工具&lt;strong&gt;花生壳&lt;/strong&gt;来实现远程访问 WSL2 中的服务。&lt;/p&gt;
&lt;p&gt;实现这一需求需要完成两个功能。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;WSL2 中的服务是运行在虚拟机中的，如何将公网的访问转发到 WSL2 中。&lt;/li&gt;
&lt;li&gt;Windows 没有公网 IP，如何通过公网来访问。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;wsl2-端口转发&#34;&gt;WSL2 端口转发&lt;/h1&gt;
&lt;p&gt;获取 WSL2 的 IP 地址：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;hostname -I &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;172.26.13.98
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Windows 自带的&lt;code&gt;netsh interface portproxy&lt;/code&gt;可以实现端口转发。管理员身份打开 cmd，执行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netsh interface portproxy add v4tov4 &lt;span class=&#34;nv&#34;&gt;listenaddress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.0.0.0 &lt;span class=&#34;nv&#34;&gt;listenport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2222&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;connectaddress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;172.26.13.98 &lt;span class=&#34;nv&#34;&gt;connectport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;listenport：公网访问的端口（改一个不冲突的就行）&lt;/li&gt;
&lt;li&gt;connectaddress：WSL2 的 IP 地址&lt;/li&gt;
&lt;li&gt;connectport：WSL2 中 SSH 服务的端口 (默认为 22，不需要更改)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;开启 Windows 防火墙入站规则，管理员身份打开 cmd，执行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netsh advfirewall firewall add rule &lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;WSL2 &lt;span class=&#34;nv&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;in &lt;span class=&#34;nv&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;allow &lt;span class=&#34;nv&#34;&gt;protocol&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;TCP &lt;span class=&#34;nv&#34;&gt;localport&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2222&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个命令是用于在 Windows 高级防火墙中添加一条规则。下面是对每个参数的解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name=WSL2&lt;/code&gt;：将规则命名为 &amp;ldquo;WSL2&amp;rdquo;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dir=in&lt;/code&gt;：指定规则适用于传入的网络流量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;action=allow&lt;/code&gt;：允许通过该规则的流量通过防火墙。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;protocol=TCP&lt;/code&gt;：指定规则适用于 TCP 协议的流量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;localport=2222&lt;/code&gt;：指定本地端口号为 2222。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;验证端口转发是否成功：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh -p &lt;span class=&#34;m&#34;&gt;2222&lt;/span&gt; user@localhost
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;user 修改成 WSL2 的用户名&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果配置成功，则会成功登录 WSL2。&lt;/p&gt;
&lt;h1 id=&#34;安装配置花生壳&#34;&gt;安装配置花生壳&lt;/h1&gt;
&lt;p&gt;进入官网&lt;a href=&#34;https://hsk.oray.com/download&#34;&gt;下载花生壳客户端&lt;/a&gt;，安装后打开，注册账号，登录。&lt;strong&gt;需要实名认证&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;免费账户可以绑定&lt;strong&gt;2 个映射&lt;/strong&gt;，对我来说暂时够用了，免费流量 1G/月。实测阅读代码不编译的话大概&lt;strong&gt;每天 50M&lt;/strong&gt;左右。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&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//2023/08/28/3423c43e8d319abd89c11afd7be03a11.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/28/3423c43e8d319abd89c11afd7be03a11.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;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//2023/08/28/fddec1e9e0cc03c7208b4244dd35ad01.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/28/fddec1e9e0cc03c7208b4244dd35ad01.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;验证是否配置成功，找一台不在同一个局域网的电脑，使用 SSH 连接 WSL2：&lt;/p&gt;
&lt;p&gt;如果复制出来的访问地址为&lt;code&gt;abcdjsj.goho.co:33445&lt;/code&gt;，那么 SSH 命令修改为如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh -p &lt;span class=&#34;m&#34;&gt;33445&lt;/span&gt;  user@abcdjsj.goho.co
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;user 修改成 WSL2 的用户名&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果配置成功，则会成功登录 WSL2。&lt;/p&gt;
&lt;h1 id=&#34;题外话&#34;&gt;题外话&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;WSL2 的 &lt;strong&gt;IP 会经常变化&lt;/strong&gt;，如果连不上了，可以重新获取一下 IP，然后修改一下各个配置。或者想办法将 WSL2 的 IP 固定下来。&lt;/li&gt;
&lt;li&gt;带宽有限，登录时比较慢，耐心等待。后续准备使用 frp 自建一个穿透服务。&lt;/li&gt;
&lt;li&gt;PC 耗电伤不起啊，一百多瓦赶上三四台 NAS 了。这玩意只能应急，长时间挂机电费都够买个云服务器了。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;常见问题&#34;&gt;常见问题&lt;/h1&gt;
&lt;h2 id=&#34;system-is-booting-up-unprivileged-users-are-not-permitted-to-log-in-yet&#34;&gt;&amp;ldquo;System is booting up. Unprivileged users are not permitted to log in yet&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;登录服务端，也就是 WSL2，执行以下命令&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo rm /run/nologin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="背景简介">背景简介</h1>
<p>WSL2 是 Windows 的子系统，可以在 Windows 上运行 Linux，但是 WSL2 是运行在虚拟机中的，所以无法直接访问 WSL2 中的服务，比如 SSH 服务。本文介绍如何使用内网穿透工具<strong>花生壳</strong>来实现远程访问 WSL2 中的服务。</p>
<p>实现这一需求需要完成两个功能。</p>
<ol>
<li>WSL2 中的服务是运行在虚拟机中的，如何将公网的访问转发到 WSL2 中。</li>
<li>Windows 没有公网 IP，如何通过公网来访问。</li>
</ol>
<h1 id="wsl2-端口转发">WSL2 端口转发</h1>
<p>获取 WSL2 的 IP 地址：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hostname -I <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span>
</span></span><span class="line"><span class="cl">172.26.13.98
</span></span></code></pre></div><p>Windows 自带的<code>netsh interface portproxy</code>可以实现端口转发。管理员身份打开 cmd，执行以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">netsh interface portproxy add v4tov4 <span class="nv">listenaddress</span><span class="o">=</span>0.0.0.0 <span class="nv">listenport</span><span class="o">=</span><span class="m">2222</span> <span class="nv">connectaddress</span><span class="o">=</span>172.26.13.98 <span class="nv">connectport</span><span class="o">=</span><span class="m">22</span>
</span></span></code></pre></div><ul>
<li>listenport：公网访问的端口（改一个不冲突的就行）</li>
<li>connectaddress：WSL2 的 IP 地址</li>
<li>connectport：WSL2 中 SSH 服务的端口 (默认为 22，不需要更改)</li>
</ul>
<p>开启 Windows 防火墙入站规则，管理员身份打开 cmd，执行以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">netsh advfirewall firewall add rule <span class="nv">name</span><span class="o">=</span>WSL2 <span class="nv">dir</span><span class="o">=</span>in <span class="nv">action</span><span class="o">=</span>allow <span class="nv">protocol</span><span class="o">=</span>TCP <span class="nv">localport</span><span class="o">=</span><span class="m">2222</span>
</span></span></code></pre></div><p>这个命令是用于在 Windows 高级防火墙中添加一条规则。下面是对每个参数的解释：</p>
<ul>
<li><code>name=WSL2</code>：将规则命名为 &ldquo;WSL2&rdquo;。</li>
<li><code>dir=in</code>：指定规则适用于传入的网络流量。</li>
<li><code>action=allow</code>：允许通过该规则的流量通过防火墙。</li>
<li><code>protocol=TCP</code>：指定规则适用于 TCP 协议的流量。</li>
<li><code>localport=2222</code>：指定本地端口号为 2222。</li>
</ul>
<p>验证端口转发是否成功：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh -p <span class="m">2222</span> user@localhost
</span></span></code></pre></div><ul>
<li>user 修改成 WSL2 的用户名</li>
</ul>
<p>如果配置成功，则会成功登录 WSL2。</p>
<h1 id="安装配置花生壳">安装配置花生壳</h1>
<p>进入官网<a href="https://hsk.oray.com/download">下载花生壳客户端</a>，安装后打开，注册账号，登录。<strong>需要实名认证</strong></p>
<blockquote>
<p>免费账户可以绑定<strong>2 个映射</strong>，对我来说暂时够用了，免费流量 1G/月。实测阅读代码不编译的话大概<strong>每天 50M</strong>左右。</p>
</blockquote>
<p>打开客户端，添加映射，配置如下：</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//2023/08/28/3423c43e8d319abd89c11afd7be03a11.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/28/3423c43e8d319abd89c11afd7be03a11.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>
<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//2023/08/28/fddec1e9e0cc03c7208b4244dd35ad01.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/28/fddec1e9e0cc03c7208b4244dd35ad01.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>验证是否配置成功，找一台不在同一个局域网的电脑，使用 SSH 连接 WSL2：</p>
<p>如果复制出来的访问地址为<code>abcdjsj.goho.co:33445</code>，那么 SSH 命令修改为如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh -p <span class="m">33445</span>  user@abcdjsj.goho.co
</span></span></code></pre></div><ul>
<li>user 修改成 WSL2 的用户名</li>
</ul>
<p>如果配置成功，则会成功登录 WSL2。</p>
<h1 id="题外话">题外话</h1>
<ol>
<li>WSL2 的 <strong>IP 会经常变化</strong>，如果连不上了，可以重新获取一下 IP，然后修改一下各个配置。或者想办法将 WSL2 的 IP 固定下来。</li>
<li>带宽有限，登录时比较慢，耐心等待。后续准备使用 frp 自建一个穿透服务。</li>
<li>PC 耗电伤不起啊，一百多瓦赶上三四台 NAS 了。这玩意只能应急，长时间挂机电费都够买个云服务器了。</li>
</ol>
<h1 id="常见问题">常见问题</h1>
<h2 id="system-is-booting-up-unprivileged-users-are-not-permitted-to-log-in-yet">&ldquo;System is booting up. Unprivileged users are not permitted to log in yet&rdquo;</h2>
<p>登录服务端，也就是 WSL2，执行以下命令</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo rm /run/nologin
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>PhotoPrism 部署私人相册</title>
      <link>https://lifeislife.cn/posts/photoprism%E9%83%A8%E7%BD%B2%E7%A7%81%E4%BA%BA%E7%9B%B8%E5%86%8C/</link>
      <pubDate>Sun, 20 Aug 2023 09:48:13 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/photoprism%E9%83%A8%E7%BD%B2%E7%A7%81%E4%BA%BA%E7%9B%B8%E5%86%8C/</guid>
      <description>&lt;h1 id=&#34;docker-compose-启动&#34;&gt;Docker-compose 启动&lt;/h1&gt;
&lt;p&gt;下载官方的 &lt;code&gt;docker-compose.yml&lt;/code&gt; 文件，然后修改一下端口和挂载路径，然后启动即可。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://dl.photoprism.app/docker/docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;如果无法下载下载地址可以前往 &lt;a href=&#34;https://docs.photoprism.app/getting-started/docker-compose/&#34;&gt;Docker Compose - PhotoPrism&lt;/a&gt; 查看最新。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据自己需要修改以下参数：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3.5&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;photoprism&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;## Use photoprism/photoprism:preview for testing preview builds:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dockerproxy.com/photoprism/photoprism:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 配置了镜像加速&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;2342:2342&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# HTTP port (host:container)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;PHOTOPRISM_ADMIN_USER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 管理员用户名&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;PHOTOPRISM_ADMIN_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;12345678&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 管理员密码&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;PHOTOPRISM_DETECT_NSFW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 自动检测 NSFW 图片并标记隐私图片&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;PHOTOPRISM_UPLOAD_NSFW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 运行上传 NSFW 图片   &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;## Share hardware devices with FFmpeg and TensorFlow (optional):&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;devices&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;/dev/dri:/dev/dri&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                           &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 如果有核显或者独显可以配置硬件加速&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;/root/sharedfolder/syncthing/Photo_Album:/photoprism/originals/Photo_Album&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 照片存放路径&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;/root/sharedfolder/syncthing/daily:/photoprism/originals/daily&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 照片存放路径&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;/root/sharedfolder/syncthing/baby:/photoprism/originals/baby&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 照片存放路径&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;./storage:/photoprism/storage&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 不要删除 (DO NOT REMOVE)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后启动即可：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;初始化需要时间，等待一分钟左右&lt;/strong&gt;，然后访问 &lt;code&gt;http://{hostip}:2342&lt;/code&gt; 即可看到登录界面。&lt;/p&gt;
&lt;h1 id=&#34;配置&#34;&gt;配置&lt;/h1&gt;
&lt;h2 id=&#34;配置中文界面&#34;&gt;配置中文界面&lt;/h2&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//2023/08/20/eca4f02a6f2595302abe05ac41f5c965.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/eca4f02a6f2595302abe05ac41f5c965.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//2023/08/20/b4c5c56ab3eb0242e35a09bd5036885e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/b4c5c56ab3eb0242e35a09bd5036885e.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;h2 id=&#34;索引照片&#34;&gt;索引照片&lt;/h2&gt;
&lt;p&gt;这个过程会调用 TensorFlow 进行照片的 AI 识别，然后自动进行分类，照片如果很多会很慢。如果只想索引某一个目录就点击图片中的区域选择指定目录，&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//2023/08/20/33a399732f66f9b768007377ffbcb748.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/33a399732f66f9b768007377ffbcb748.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;h1 id=&#34;使用相册&#34;&gt;使用相册&lt;/h1&gt;
&lt;p&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//2023/08/20/c1bc29db426f442362ae17fdd8e29d63.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/c1bc29db426f442362ae17fdd8e29d63.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;索引过程会根据照片的 Exif 信息自动分类，包括时间与地点。后悔从相机导出照片时把地点抹去了。&lt;/p&gt;
&lt;p&gt;照片还是得及时整理呀，这成千上万张照片挨个标记还是很麻烦的，就这样吧，做个图片墙也不错。&lt;/p&gt;
&lt;h1 id=&#34;常见问题&#34;&gt;常见问题&lt;/h1&gt;
&lt;h2 id=&#34;在docker-composeyml中删除已经索引的volume为何图片库中还存在缓存&#34;&gt;在docker-compose.yml中删除已经索引的volume，为何图片库中还存在缓存&lt;/h2&gt;
&lt;p&gt;缓存保存在storage中，如果图片内容不多，可以将该目录删除，重启容器。也可以通过以下命令将缓存删除：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 进入容器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -it photo-prism bash
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;photoprism purge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;photoprism cleanup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;全选图片选择多个图片&#34;&gt;全选图片，选择多个图片&lt;/h2&gt;
&lt;p&gt;可以选择一张图片后按住Shift到最后一张，批量选择图片&lt;/p&gt;
&lt;h2 id=&#34;定时索引照片&#34;&gt;定时索引照片&lt;/h2&gt;
&lt;p&gt;可以使用 &lt;code&gt;crontab&lt;/code&gt; 定时执行 &lt;code&gt;photoprism index&lt;/code&gt; 命令，例如每天凌晨 3 点执行一次：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 编辑定时任务&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;crontab -e
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加以下内容&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; * * * docker &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; -i photo-prism photoprism index
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="docker-compose-启动">Docker-compose 启动</h1>
<p>下载官方的 <code>docker-compose.yml</code> 文件，然后修改一下端口和挂载路径，然后启动即可。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget https://dl.photoprism.app/docker/docker-compose.yml
</span></span></code></pre></div><blockquote>
<p>如果无法下载下载地址可以前往 <a href="https://docs.photoprism.app/getting-started/docker-compose/">Docker Compose - PhotoPrism</a> 查看最新。</p>
</blockquote>
<p>根据自己需要修改以下参数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;3.5&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">photoprism</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c">## Use photoprism/photoprism:preview for testing preview builds:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">dockerproxy.com/photoprism/photoprism:latest</span><span class="w"> </span><span class="c"># 配置了镜像加速</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;2342:2342&#34;</span><span class="w"> </span><span class="c"># HTTP port (host:container)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PHOTOPRISM_ADMIN_USER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;admin&#34;</span><span class="w">                 </span><span class="c"># 管理员用户名</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PHOTOPRISM_ADMIN_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;12345678&#34;</span><span class="w">          </span><span class="c"># 管理员密码</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PHOTOPRISM_DETECT_NSFW</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;true&#34;</span><span class="w">                 </span><span class="c"># 自动检测 NSFW 图片并标记隐私图片</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PHOTOPRISM_UPLOAD_NSFW</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;true&#34;</span><span class="w">                 </span><span class="c"># 运行上传 NSFW 图片   </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c">## Share hardware devices with FFmpeg and TensorFlow (optional):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">devices</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">     </span>- <span class="s2">&#34;/dev/dri:/dev/dri&#34;</span><span class="w">                           </span><span class="c"># 如果有核显或者独显可以配置硬件加速</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;/root/sharedfolder/syncthing/Photo_Album:/photoprism/originals/Photo_Album&#34;</span><span class="w">               </span><span class="c"># 照片存放路径</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;/root/sharedfolder/syncthing/daily:/photoprism/originals/daily&#34;</span><span class="w">               </span><span class="c"># 照片存放路径</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;/root/sharedfolder/syncthing/baby:/photoprism/originals/baby&#34;</span><span class="w">               </span><span class="c"># 照片存放路径</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;./storage:/photoprism/storage&#34;</span><span class="w">                  </span><span class="c"># 不要删除 (DO NOT REMOVE)</span><span class="w">
</span></span></span></code></pre></div><p>然后启动即可：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker-compose up -d
</span></span></code></pre></div><p><strong>初始化需要时间，等待一分钟左右</strong>，然后访问 <code>http://{hostip}:2342</code> 即可看到登录界面。</p>
<h1 id="配置">配置</h1>
<h2 id="配置中文界面">配置中文界面</h2>
<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//2023/08/20/eca4f02a6f2595302abe05ac41f5c965.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/eca4f02a6f2595302abe05ac41f5c965.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//2023/08/20/b4c5c56ab3eb0242e35a09bd5036885e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/b4c5c56ab3eb0242e35a09bd5036885e.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>
<h2 id="索引照片">索引照片</h2>
<p>这个过程会调用 TensorFlow 进行照片的 AI 识别，然后自动进行分类，照片如果很多会很慢。如果只想索引某一个目录就点击图片中的区域选择指定目录，<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//2023/08/20/33a399732f66f9b768007377ffbcb748.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/33a399732f66f9b768007377ffbcb748.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>
<h1 id="使用相册">使用相册</h1>
<p>索引完成就可以点击搜索进行查看所有照片了：</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//2023/08/20/c1bc29db426f442362ae17fdd8e29d63.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/20/c1bc29db426f442362ae17fdd8e29d63.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>索引过程会根据照片的 Exif 信息自动分类，包括时间与地点。后悔从相机导出照片时把地点抹去了。</p>
<p>照片还是得及时整理呀，这成千上万张照片挨个标记还是很麻烦的，就这样吧，做个图片墙也不错。</p>
<h1 id="常见问题">常见问题</h1>
<h2 id="在docker-composeyml中删除已经索引的volume为何图片库中还存在缓存">在docker-compose.yml中删除已经索引的volume，为何图片库中还存在缓存</h2>
<p>缓存保存在storage中，如果图片内容不多，可以将该目录删除，重启容器。也可以通过以下命令将缓存删除：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 进入容器</span>
</span></span><span class="line"><span class="cl">docker <span class="nb">exec</span> -it photo-prism bash
</span></span><span class="line"><span class="cl"><span class="c1"># 删除索引</span>
</span></span><span class="line"><span class="cl">photoprism purge
</span></span><span class="line"><span class="cl"><span class="c1"># 删除文件</span>
</span></span><span class="line"><span class="cl">photoprism cleanup
</span></span></code></pre></div><h2 id="全选图片选择多个图片">全选图片，选择多个图片</h2>
<p>可以选择一张图片后按住Shift到最后一张，批量选择图片</p>
<h2 id="定时索引照片">定时索引照片</h2>
<p>可以使用 <code>crontab</code> 定时执行 <code>photoprism index</code> 命令，例如每天凌晨 3 点执行一次：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 编辑定时任务</span>
</span></span><span class="line"><span class="cl">crontab -e
</span></span><span class="line"><span class="cl"><span class="c1"># 添加以下内容</span>
</span></span><span class="line"><span class="cl"><span class="m">0</span> <span class="m">3</span> * * * docker <span class="nb">exec</span> -i photo-prism photoprism index
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>Docker 部署 Radarr 刮削电影</title>
      <link>https://lifeislife.cn/posts/docker%E9%83%A8%E7%BD%B2radarr%E5%88%AE%E5%89%8A%E7%94%B5%E5%BD%B1/</link>
      <pubDate>Thu, 17 Aug 2023 22:46:26 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/docker%E9%83%A8%E7%BD%B2radarr%E5%88%AE%E5%89%8A%E7%94%B5%E5%BD%B1/</guid>
      <description>&lt;h1 id=&#34;docker-composeyml&#34;&gt;docker-compose.yml&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-YAML&#34; data-lang=&#34;YAML&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;3.7&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;radarr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;radarr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dockerproxy.com/linuxserver/radarr:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;7878:7878&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;PUID=1000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;PGID=1000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;UMASK=002&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;TZ=Asia/Shanghai&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/root/sharedfolder/appdata/radarr:/config&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/root/sharedfolder/media:/movies&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;/root/sharedfolder/downloads/qbittorrent:/downloads&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&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//2023/08/17/9dbfcaa5f918bccfa99ce64cc97a7e16.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/17/9dbfcaa5f918bccfa99ce64cc97a7e16.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;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//2023/08/17/81a242b30d3ab2d29b7f4bf92ba246de.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/17/81a242b30d3ab2d29b7f4bf92ba246de.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;
</description>
      <content:encoded><![CDATA[<h1 id="docker-composeyml">docker-compose.yml</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-YAML" data-lang="YAML"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3.7&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">radarr</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">radarr</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">dockerproxy.com/linuxserver/radarr:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;7878:7878&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">PUID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">PGID=1000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">UMASK=002</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">TZ=Asia/Shanghai</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/root/sharedfolder/appdata/radarr:/config</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/root/sharedfolder/media:/movies</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/root/sharedfolder/downloads/qbittorrent:/downloads</span><span class="w">
</span></span></span></code></pre></div><p>配置中文界面：</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//2023/08/17/9dbfcaa5f918bccfa99ce64cc97a7e16.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/17/9dbfcaa5f918bccfa99ce64cc97a7e16.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>
<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//2023/08/17/81a242b30d3ab2d29b7f4bf92ba246de.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/08/17/81a242b30d3ab2d29b7f4bf92ba246de.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>
]]></content:encoded>
    </item>
    <item>
      <title>SSH 免密登录</title>
      <link>https://lifeislife.cn/posts/ssh%E5%85%8D%E5%AF%86%E7%99%BB%E5%BD%95/</link>
      <pubDate>Sat, 12 Aug 2023 09:22:56 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ssh%E5%85%8D%E5%AF%86%E7%99%BB%E5%BD%95/</guid>
      <description>&lt;h1 id=&#34;生成密钥对&#34;&gt;生成密钥对&lt;/h1&gt;
&lt;p&gt;宿主机任意下目录执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-keygen -t rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Generating public/private rsa key pair.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter file in which to save the key &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;/home/user/.ssh/id_rsa&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: host2servera_id_rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter passphrase &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;empty &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; no passphrase&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter same passphrase again: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your identification has been saved in host2servera_id_rsa.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your public key has been saved in host2servera_id_rsa.pub.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The key fingerprint is:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SHA256:OkWcw+R3x6Z2mzeYQuG033H3N9qIeym3TZKzz6YD8tQ user@ubuntu18
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The key&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;s randomart image is:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;RSA 2048&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;----+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        .        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; .   .   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        B .o. +  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       . oo.o+   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        S  ++ ..o&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       o ..+.A&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      o   +..B+&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       .   &lt;span class=&#34;nv&#34;&gt;oo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;@o+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;           &lt;span class=&#34;nv&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;ss&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+----&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;SHA256&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;-----+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一直回车确定，生成公私钥，保存在&lt;code&gt;~/.ssh&lt;/code&gt;目录下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我在宿主机上生成的公私钥名称为，分别是&lt;code&gt;host2servera_id_rsa&lt;/code&gt;,&lt;code&gt;host2servera_id_rsa.pub&lt;/code&gt;方便我记忆。如果一直回车，那么生成的公私钥名称为&lt;code&gt;id_rsa&lt;/code&gt;，&lt;code&gt;id_rsa.pub&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;发送公钥&#34;&gt;发送公钥&lt;/h1&gt;
&lt;p&gt;将公钥复制到服务器 ServerA 上，以 IP：&lt;code&gt;10.12.193.53&lt;/code&gt; 为例。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-copy-id 10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, to filter out any that are already installed
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/ssh-copy-id: INFO: &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; key&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; remain to be installed -- &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; you are prompted now it is to install the new keys
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;user@10.12.193.53&lt;span class=&#34;s1&#34;&gt;&amp;#39;s password: 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;Number of key(s) added: 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;Now try logging into the machine, with:   &amp;#34;ssh &amp;#39;&lt;/span&gt;10.12.193.53&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;and check to make sure that only the key(s) you wanted were added.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;默认是把 ServerA 的用户当做 user 进行登录。如果有确定的用户如&lt;code&gt;userA&lt;/code&gt;，就使用&lt;code&gt;ssh-copy-id userA@10.12.193.53&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后就可以直接免密码登录了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh user@10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 或者&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh userA@10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果还是需要输入密码，可能&lt;code&gt;ssh-agent&lt;/code&gt;没有启动，执行&lt;code&gt;eval $(ssh-agent)&lt;/code&gt;启动&lt;code&gt;ssh-agent&lt;/code&gt;，然后再次登录即可。然后将私钥添加到&lt;code&gt;ssh-agent&lt;/code&gt;中，执行&lt;code&gt;ssh-add ~/.ssh/host2servera_id_rsa&lt;/code&gt;，然后再次登录即可。&lt;/p&gt;
&lt;h1 id=&#34;配置快捷登录&#34;&gt;配置快捷登录&lt;/h1&gt;
&lt;p&gt;即使免密登录，输入一长串 IP 也太麻烦了，能不能配置更简单的登录方式，比如给服务器起个名字如&lt;code&gt;ServerA&lt;/code&gt;直接使用&lt;code&gt;ssh ServerA&lt;/code&gt;就登录服务器，能。&lt;/p&gt;
&lt;p&gt;打开&lt;code&gt;~/.ssh/config&lt;/code&gt;配置如下内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Ini&#34; data-lang=&#34;Ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Host ServerA&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;HostName 10.12.193.53&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;Port 22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;User userA&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;IdentityFile ~/.ssh/host2servera_id_rsa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后就可以直接使用&lt;code&gt;ssh ServerA&lt;/code&gt;登录了。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="生成密钥对">生成密钥对</h1>
<p>宿主机任意下目录执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ ssh-keygen -t rsa
</span></span><span class="line"><span class="cl">Generating public/private rsa key pair.
</span></span><span class="line"><span class="cl">Enter file in which to save the key <span class="o">(</span>/home/user/.ssh/id_rsa<span class="o">)</span>: host2servera_id_rsa
</span></span><span class="line"><span class="cl">Enter passphrase <span class="o">(</span>empty <span class="k">for</span> no passphrase<span class="o">)</span>: 
</span></span><span class="line"><span class="cl">Enter same passphrase again: 
</span></span><span class="line"><span class="cl">Your identification has been saved in host2servera_id_rsa.
</span></span><span class="line"><span class="cl">Your public key has been saved in host2servera_id_rsa.pub.
</span></span><span class="line"><span class="cl">The key fingerprint is:
</span></span><span class="line"><span class="cl">SHA256:OkWcw+R3x6Z2mzeYQuG033H3N9qIeym3TZKzz6YD8tQ user@ubuntu18
</span></span><span class="line"><span class="cl">The key<span class="err">&#39;</span>s randomart image is:
</span></span><span class="line"><span class="cl">+---<span class="o">[</span>RSA 2048<span class="o">]</span>----+
</span></span><span class="line"><span class="cl"><span class="p">|</span>        .        <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       <span class="o">=</span> .   .   <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>        B .o. +  <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       . oo.o+   <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>        S  ++ ..o<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       o ..+.A<span class="o">=</span><span class="nv">o</span><span class="o">=</span><span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>      o   +..B+<span class="o">=</span>+<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       .   <span class="nv">oo</span><span class="o">=</span>@o+<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>           <span class="nv">o</span><span class="o">=</span><span class="nv">ss</span><span class="o">=</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl">+----<span class="o">[</span>SHA256<span class="o">]</span>-----+
</span></span></code></pre></div><p>一直回车确定，生成公私钥，保存在<code>~/.ssh</code>目录下。</p>
<blockquote>
<p>我在宿主机上生成的公私钥名称为，分别是<code>host2servera_id_rsa</code>,<code>host2servera_id_rsa.pub</code>方便我记忆。如果一直回车，那么生成的公私钥名称为<code>id_rsa</code>，<code>id_rsa.pub</code>。</p>
</blockquote>
<h1 id="发送公钥">发送公钥</h1>
<p>将公钥复制到服务器 ServerA 上，以 IP：<code>10.12.193.53</code> 为例。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ ssh-copy-id 10.12.193.53
</span></span><span class="line"><span class="cl"><span class="c1"># 输入密码</span>
</span></span><span class="line"><span class="cl">/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key<span class="o">(</span>s<span class="o">)</span>, to filter out any that are already installed
</span></span><span class="line"><span class="cl">/usr/bin/ssh-copy-id: INFO: <span class="m">1</span> key<span class="o">(</span>s<span class="o">)</span> remain to be installed -- <span class="k">if</span> you are prompted now it is to install the new keys
</span></span><span class="line"><span class="cl">user@10.12.193.53<span class="s1">&#39;s password: 
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">Number of key(s) added: 1
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">Now try logging into the machine, with:   &#34;ssh &#39;</span>10.12.193.53<span class="err">&#39;</span><span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">and check to make sure that only the key(s) you wanted were added.
</span></span></span></code></pre></div><p>默认是把 ServerA 的用户当做 user 进行登录。如果有确定的用户如<code>userA</code>，就使用<code>ssh-copy-id userA@10.12.193.53</code></p>
<p>然后就可以直接免密码登录了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">ssh user@10.12.193.53
</span></span><span class="line"><span class="cl"><span class="c1"># 或者</span>
</span></span><span class="line"><span class="cl">ssh userA@10.12.193.53
</span></span></code></pre></div><p>如果还是需要输入密码，可能<code>ssh-agent</code>没有启动，执行<code>eval $(ssh-agent)</code>启动<code>ssh-agent</code>，然后再次登录即可。然后将私钥添加到<code>ssh-agent</code>中，执行<code>ssh-add ~/.ssh/host2servera_id_rsa</code>，然后再次登录即可。</p>
<h1 id="配置快捷登录">配置快捷登录</h1>
<p>即使免密登录，输入一长串 IP 也太麻烦了，能不能配置更简单的登录方式，比如给服务器起个名字如<code>ServerA</code>直接使用<code>ssh ServerA</code>就登录服务器，能。</p>
<p>打开<code>~/.ssh/config</code>配置如下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ini" data-lang="Ini"><span class="line"><span class="cl"><span class="na">Host ServerA</span>
</span></span><span class="line"><span class="cl">    <span class="na">HostName 10.12.193.53</span>
</span></span><span class="line"><span class="cl">    <span class="na">Port 22</span>
</span></span><span class="line"><span class="cl">    <span class="na">User userA</span>
</span></span><span class="line"><span class="cl">    <span class="na">IdentityFile ~/.ssh/host2servera_id_rsa</span>
</span></span></code></pre></div><p>然后就可以直接使用<code>ssh ServerA</code>登录了。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Linux 网络配置常用命令</title>
      <link>https://lifeislife.cn/posts/linux%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</link>
      <pubDate>Sat, 05 Aug 2023 15:29:58 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/linux%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</guid>
      <description>&lt;h1 id=&#34;配置网桥-brctl&#34;&gt;配置网桥 brctl&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建一个名为 br0 的网桥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addbr br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除网桥 br0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl delbr br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 列出所有的网桥及其接口信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl show
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将网络接口 `eth0` 添加到网桥 `br0` 中&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addif br0 eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 从网桥 `br0` 中删除网络接口 `eth0`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl delif br0 eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;### 显示网桥 `br0` 的 Spanning Tree Protocol (STP)配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl showstp br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 禁用 Linux 内核中桥接器对数据包进行处理时调用 iptables 的功能。这种配置通常用于提高桥接速度，减少桥接过程中的 CPU 开销。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo sysctl net.bridge.bridge-nf-call-iptables&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo sysctl net.bridge.bridge-nf-call-iptables&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 为虚拟网卡设置IP并启动&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig tap0 192.168.2.1 up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;虚拟网络设备-tunctl&#34;&gt;虚拟网络设备 tunctl&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建一个名为tun0的虚拟网络设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -t tun0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将虚拟网卡设置为任何人都有权限使用：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod &lt;span class=&#34;m&#34;&gt;0666&lt;/span&gt; /dev/net/tun
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除名为tun0的虚拟网络设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -d tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建名为tun0的虚拟网络设备并指定其拥有者和组&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -u user -g group -t tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 列出所有活跃的虚拟网络设备及其接口信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -s 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 指定虚拟网络设备的MAC地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -m &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;mac_address&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; -t tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 列出具有给定设备名称前缀的所有已分配的虚拟网络设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -g &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;device_name_prefix&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 分配虚拟网络设备的文件描述符，将结果输出到标准输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -p -t tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将虚拟网络设备关联到现有的桥接设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -b -t tun0 -g br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 从现有虚拟网络设备解除关联&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -B -t tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 指定虚拟网络设备的最大传输单元(MTU)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip link &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; tun0 mtu &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;value&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启用虚拟网络设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip link &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; tun0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 禁用虚拟网络设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip link &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; tun0 down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示由Linux内核管理的虚拟网络设备的状态信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip link show tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改虚拟网络设备的MAC地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip link &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; dev tun0 address &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;mac_address&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 为虚拟网络设备分配一个IPv4地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip addr add &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;ip_address/cidr&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; dev tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 从虚拟网络设备中删除一个IPv4地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip addr del &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;ip_address/cidr&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; dev tun0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查虚拟网络设备是否已分配IPv4地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip addr show tun0 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep inet
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 使用DLADDR命令获取虚拟网络设备的MAC地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ethtool -P tun0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 列出当前所有活动的网络接口&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip a 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查虚拟网络设备是否已分配IPv6地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip addr show tun0 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep inet6 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;网络接口管理ifconfig&#34;&gt;网络接口管理（ifconfig）&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示所有网络接口信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig -a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 激活指定接口(如 eth0)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 关闭指定接口(如 eth0)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加 IP 地址，例如添加 IP 地址为 192.168.2.100 的网卡 eth0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 192.168.2.100 netmask 255.255.255.0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除 IP 地址，例如删除网卡 eth0 上的 IP 地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 0.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启用或禁用广播地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 broadcast 192.168.2.255 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 -broadcast
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网卡 mtu 大小为 9000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0 mtu &lt;span class=&#34;m&#34;&gt;9000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 增加一个虚拟网络接口 eth0:1，并配置 IP 地址为 192.168.2.100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig eth0:1 192.168.2.100 netmask 255.255.255.0 up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;路由管理route&#34;&gt;路由管理（route）&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示当前路由表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;route -n
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加默认路由&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;route add default gw 192.168.2.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除默认路由&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;route del default
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 增加一个到目标网络的静态路由&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;route add -net 192.168.100.0 netmask 255.255.255.0 gw 192.168.2.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除静态路由&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;route del -net 192.168.100.0 netmask 255.255.255.0 gw 192.168.2.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 清除所有路由缓存项&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;route flush cache
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 IP 地址对应网卡接口的 MAC 地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;arping -I eth0 192.168.2.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;dns-解析nslookup-和-dig&#34;&gt;DNS 解析（nslookup 和 dig）&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 使用域名服务器解析域名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nslookup www.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 指定域名服务器，并解析域名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nslookup www.example.com 8.8.8.8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查询DNS地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dig example.com +nssearch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查询所有的 NS 记录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dig example.com NS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查询某个域名的 MX 记录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dig example.com MX
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;网络诊断工具ping-和-traceroute&#34;&gt;网络诊断工具（ping 和 traceroute）&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 测试与目标主机之间的连通性，查看网络是否可达&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ping 192.168.2.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ping 命令的高级选项，控制发送的数据包数量、大小和时间间隔等参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ping -c &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt; -s &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; -i &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; 192.168.2.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示数据包在网络上的传输路径，检测网络故障&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;traceroute www.google.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示每一跳所经过的路由器名称和 IP 地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;traceroute -n www.google.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;网络流量分析工具tcpdump-和-wireshark&#34;&gt;网络流量分析工具（tcpdump 和 wireshark）&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 监听指定的网络接口上的数据包，显示每个数据包的详细信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tcpdump -i eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 监听指定端口上的数据包&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tcpdump port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示从指定源地址到目标地址的所有网络流量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tcpdump src 192.168.2.100 and dst 192.168.2.200
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示所有 IP 流量，并将结果保存到文件 tcp.pcap 中，以便使用 Wireshark 分析&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tcpdump -i eth0 -w tcp.pcap ip
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 图形化的网络协议分析工具，用于分析网络流量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wireshark
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;其他命令&#34;&gt;其他命令&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示网络连接状态和统计信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netstat -an
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示 TCP/IP 配置参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sysctl net.ipv4.tcp_*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置 TCP/IP 参数，例如设置 SYN 攻击保护&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sysctl -w net.ipv4.tcp_syncookies&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 重新加载 /etc/resolv.conf 文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemd-resolve --flush-caches
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示当前 DNS 服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemd-resolve --status &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;DNS Servers&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 重启网络&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart NetworkManager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="配置网桥-brctl">配置网桥 brctl</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 创建一个名为 br0 的网桥</span>
</span></span><span class="line"><span class="cl">sudo brctl addbr br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除网桥 br0</span>
</span></span><span class="line"><span class="cl">sudo brctl delbr br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 列出所有的网桥及其接口信息</span>
</span></span><span class="line"><span class="cl">sudo brctl show
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将网络接口 `eth0` 添加到网桥 `br0` 中</span>
</span></span><span class="line"><span class="cl">sudo brctl addif br0 eth0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 从网桥 `br0` 中删除网络接口 `eth0`</span>
</span></span><span class="line"><span class="cl">sudo brctl delif br0 eth0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">### 显示网桥 `br0` 的 Spanning Tree Protocol (STP)配置</span>
</span></span><span class="line"><span class="cl">sudo brctl showstp br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 禁用 Linux 内核中桥接器对数据包进行处理时调用 iptables 的功能。这种配置通常用于提高桥接速度，减少桥接过程中的 CPU 开销。</span>
</span></span><span class="line"><span class="cl">sudo sysctl net.bridge.bridge-nf-call-iptables<span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">sudo sysctl net.bridge.bridge-nf-call-iptables<span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 为虚拟网卡设置IP并启动</span>
</span></span><span class="line"><span class="cl">sudo ifconfig tap0 192.168.2.1 up
</span></span></code></pre></div><h1 id="虚拟网络设备-tunctl">虚拟网络设备 tunctl</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 创建一个名为tun0的虚拟网络设备</span>
</span></span><span class="line"><span class="cl">sudo tunctl -t tun0 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将虚拟网卡设置为任何人都有权限使用：</span>
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">0666</span> /dev/net/tun
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除名为tun0的虚拟网络设备</span>
</span></span><span class="line"><span class="cl">sudo tunctl -d tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建名为tun0的虚拟网络设备并指定其拥有者和组</span>
</span></span><span class="line"><span class="cl">sudo tunctl -u user -g group -t tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 列出所有活跃的虚拟网络设备及其接口信息</span>
</span></span><span class="line"><span class="cl">sudo tunctl -s 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定虚拟网络设备的MAC地址</span>
</span></span><span class="line"><span class="cl">sudo tunctl -m <span class="o">[</span>mac_address<span class="o">]</span> -t tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 列出具有给定设备名称前缀的所有已分配的虚拟网络设备</span>
</span></span><span class="line"><span class="cl">sudo tunctl -g <span class="o">[</span>device_name_prefix<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 分配虚拟网络设备的文件描述符，将结果输出到标准输出</span>
</span></span><span class="line"><span class="cl">sudo tunctl -p -t tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将虚拟网络设备关联到现有的桥接设备</span>
</span></span><span class="line"><span class="cl">sudo tunctl -b -t tun0 -g br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 从现有虚拟网络设备解除关联</span>
</span></span><span class="line"><span class="cl">sudo tunctl -B -t tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定虚拟网络设备的最大传输单元(MTU)</span>
</span></span><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> tun0 mtu <span class="o">[</span>value<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 启用虚拟网络设备</span>
</span></span><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> tun0 up
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 禁用虚拟网络设备</span>
</span></span><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> tun0 down
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示由Linux内核管理的虚拟网络设备的状态信息</span>
</span></span><span class="line"><span class="cl">sudo ip link show tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 修改虚拟网络设备的MAC地址</span>
</span></span><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> dev tun0 address <span class="o">[</span>mac_address<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 为虚拟网络设备分配一个IPv4地址</span>
</span></span><span class="line"><span class="cl">sudo ip addr add <span class="o">[</span>ip_address/cidr<span class="o">]</span> dev tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 从虚拟网络设备中删除一个IPv4地址</span>
</span></span><span class="line"><span class="cl">sudo ip addr del <span class="o">[</span>ip_address/cidr<span class="o">]</span> dev tun0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查虚拟网络设备是否已分配IPv4地址</span>
</span></span><span class="line"><span class="cl">ip addr show tun0 <span class="p">|</span> grep inet
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 使用DLADDR命令获取虚拟网络设备的MAC地址</span>
</span></span><span class="line"><span class="cl">sudo ethtool -P tun0 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 列出当前所有活动的网络接口</span>
</span></span><span class="line"><span class="cl">ip a 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查虚拟网络设备是否已分配IPv6地址</span>
</span></span><span class="line"><span class="cl">ip addr show tun0 <span class="p">|</span> grep inet6 
</span></span></code></pre></div><h1 id="网络接口管理ifconfig">网络接口管理（ifconfig）</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 显示所有网络接口信息</span>
</span></span><span class="line"><span class="cl">ifconfig -a
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 激活指定接口(如 eth0)</span>
</span></span><span class="line"><span class="cl">ifconfig eth0 up
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 关闭指定接口(如 eth0)</span>
</span></span><span class="line"><span class="cl">ifconfig eth0 down
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 添加 IP 地址，例如添加 IP 地址为 192.168.2.100 的网卡 eth0</span>
</span></span><span class="line"><span class="cl">ifconfig eth0 192.168.2.100 netmask 255.255.255.0 up
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除 IP 地址，例如删除网卡 eth0 上的 IP 地址</span>
</span></span><span class="line"><span class="cl">ifconfig eth0 0.0.0.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 启用或禁用广播地址</span>
</span></span><span class="line"><span class="cl">ifconfig eth0 broadcast 192.168.2.255 up
</span></span><span class="line"><span class="cl">ifconfig eth0 -broadcast
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置网卡 mtu 大小为 9000</span>
</span></span><span class="line"><span class="cl">ifconfig eth0 mtu <span class="m">9000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 增加一个虚拟网络接口 eth0:1，并配置 IP 地址为 192.168.2.100</span>
</span></span><span class="line"><span class="cl">ifconfig eth0:1 192.168.2.100 netmask 255.255.255.0 up
</span></span></code></pre></div><h1 id="路由管理route">路由管理（route）</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 显示当前路由表</span>
</span></span><span class="line"><span class="cl">route -n
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 添加默认路由</span>
</span></span><span class="line"><span class="cl">route add default gw 192.168.2.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除默认路由</span>
</span></span><span class="line"><span class="cl">route del default
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 增加一个到目标网络的静态路由</span>
</span></span><span class="line"><span class="cl">route add -net 192.168.100.0 netmask 255.255.255.0 gw 192.168.2.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除静态路由</span>
</span></span><span class="line"><span class="cl">route del -net 192.168.100.0 netmask 255.255.255.0 gw 192.168.2.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 清除所有路由缓存项</span>
</span></span><span class="line"><span class="cl">route flush cache
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看 IP 地址对应网卡接口的 MAC 地址</span>
</span></span><span class="line"><span class="cl">arping -I eth0 192.168.2.1
</span></span></code></pre></div><h1 id="dns-解析nslookup-和-dig">DNS 解析（nslookup 和 dig）</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 使用域名服务器解析域名</span>
</span></span><span class="line"><span class="cl">nslookup www.example.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定域名服务器，并解析域名</span>
</span></span><span class="line"><span class="cl">nslookup www.example.com 8.8.8.8
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查询DNS地址</span>
</span></span><span class="line"><span class="cl">dig example.com +nssearch
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查询所有的 NS 记录</span>
</span></span><span class="line"><span class="cl">dig example.com NS
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查询某个域名的 MX 记录</span>
</span></span><span class="line"><span class="cl">dig example.com MX
</span></span></code></pre></div><h1 id="网络诊断工具ping-和-traceroute">网络诊断工具（ping 和 traceroute）</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 测试与目标主机之间的连通性，查看网络是否可达</span>
</span></span><span class="line"><span class="cl">ping 192.168.2.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ping 命令的高级选项，控制发送的数据包数量、大小和时间间隔等参数</span>
</span></span><span class="line"><span class="cl">ping -c <span class="m">5</span> -s <span class="m">100</span> -i <span class="m">1</span> 192.168.2.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示数据包在网络上的传输路径，检测网络故障</span>
</span></span><span class="line"><span class="cl">traceroute www.google.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示每一跳所经过的路由器名称和 IP 地址</span>
</span></span><span class="line"><span class="cl">traceroute -n www.google.com
</span></span></code></pre></div><h1 id="网络流量分析工具tcpdump-和-wireshark">网络流量分析工具（tcpdump 和 wireshark）</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 监听指定的网络接口上的数据包，显示每个数据包的详细信息</span>
</span></span><span class="line"><span class="cl">tcpdump -i eth0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 监听指定端口上的数据包</span>
</span></span><span class="line"><span class="cl">tcpdump port <span class="m">80</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示从指定源地址到目标地址的所有网络流量</span>
</span></span><span class="line"><span class="cl">tcpdump src 192.168.2.100 and dst 192.168.2.200
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示所有 IP 流量，并将结果保存到文件 tcp.pcap 中，以便使用 Wireshark 分析</span>
</span></span><span class="line"><span class="cl">tcpdump -i eth0 -w tcp.pcap ip
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 图形化的网络协议分析工具，用于分析网络流量</span>
</span></span><span class="line"><span class="cl">wireshark
</span></span></code></pre></div><h1 id="其他命令">其他命令</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 显示网络连接状态和统计信息</span>
</span></span><span class="line"><span class="cl">netstat -an
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示 TCP/IP 配置参数</span>
</span></span><span class="line"><span class="cl">sysctl net.ipv4.tcp_*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置 TCP/IP 参数，例如设置 SYN 攻击保护</span>
</span></span><span class="line"><span class="cl">sysctl -w net.ipv4.tcp_syncookies<span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 重新加载 /etc/resolv.conf 文件</span>
</span></span><span class="line"><span class="cl">systemd-resolve --flush-caches
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 显示当前 DNS 服务器</span>
</span></span><span class="line"><span class="cl">systemd-resolve --status <span class="p">|</span> grep <span class="s1">&#39;DNS Servers&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 重启网络</span>
</span></span><span class="line"><span class="cl">systemctl restart NetworkManager
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>QEMU 虚拟机网络配置</title>
      <link>https://lifeislife.cn/posts/qemu%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE/</link>
      <pubDate>Sat, 05 Aug 2023 14:47:54 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE/</guid>
      <description>&lt;h1 id=&#34;quick-setup&#34;&gt;Quick Setup&lt;/h1&gt;
&lt;h2 id=&#34;安装工具&#34;&gt;安装工具&lt;/h2&gt;
&lt;p&gt;安装两个网络管理工具用于建立网桥以及虚拟网卡：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装虚拟网桥工具&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install bridge-utils -y
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# UML（User-mode linux）工具        &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install uml-utilities  -y   
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;配置脚本&#34;&gt;配置脚本&lt;/h2&gt;
&lt;h3 id=&#34;qemu-ifup&#34;&gt;qemu-ifup&lt;/h3&gt;
&lt;p&gt;将下面的脚本保存为文件 &lt;code&gt;qemu-ifup&lt;/code&gt;，并赋予可执行权限：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为了方便复制脚本，在 confluence 页面提供了脚本内容，可以直接复制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /etc/qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv qemu-ifup /etc/qemu &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mv qemu-ifdown /etc/qemu 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因为网卡信息不容易定位，可能一台机器有多个网卡，所以不方便用脚本获取，需要手动设置一下。将下面的&lt;code&gt;NIC&lt;/code&gt;值修改为宿主机可以上网的网卡名称。可以通过&lt;code&gt;ifconfig&lt;/code&gt;命令查看。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置默认网卡信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置用户名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;USER_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;user
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网桥名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BRIDGE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网络信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC_IP&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;inet\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC_NETMAST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;inet\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC_BROADCAST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;inet\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $6}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NETMASK&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;255.255.240.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置默认网关地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;GATEWAY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;10.12.192.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 获取宿主机网卡MAC地址，因为创建的网桥MAC地址是随机的，&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 无法接入公司，需要从开发机网卡将其MAC地址赋值给网桥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MAC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;ether\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查网桥是否已创建，已创建就忽略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; check_bridge&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    echO &lt;span class=&#34;s2&#34;&gt;&amp;#34;Check bridge...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; brctl show &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;^&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&amp;gt; /dev/null&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建网桥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; create_bridge&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Start Create bridge...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    brctl addbr &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    brctl addif &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ifconfig br0 0.0.0.0 promisc up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    dhclient &lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ifconfig &amp;#34;$BRIDGE&amp;#34; &amp;#34;$NIC_IP&amp;#34; netmask &amp;#34;$NIC_NETMAST&amp;#34; broadcast &amp;#34;$NIC_BROADCAST&amp;#34;  hw ether &amp;#34;$MAC&amp;#34; promisc up&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启用IP转发&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; enable_ip_forward&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &amp;gt; /proc/sys/net/ipv4/ip_forward
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网桥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; setup_bridge&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    check_bridge &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt; -eq &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        create_bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    enable_ip_forward
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    setup_bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Creating &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    tunctl -t &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -u &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$USER_NAME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ifconfig &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; 0.0.0.0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Adding &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; to &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    brctl addif &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    sleep &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ifconfig &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;  hw ether &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MAC&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; promisc up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Error: no interface specified.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;qemu-ifdown&#34;&gt;qemu-ifdown&lt;/h3&gt;
&lt;p&gt;以下是&lt;code&gt;qemu-ifdown&lt;/code&gt;脚本，用于在关闭 QEMU 时关闭虚拟网卡，将其从网桥中移除，删除虚拟网卡。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网桥名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BRIDGE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 将tap设备从网桥中移除&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	brctl delif &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BRIDGE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;# 关闭tap设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	ip link link &lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt; down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 删除tap设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ip link del &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	tunctl -d &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Error: no interface specified&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将系统镜像复制一份并修改文件名，QEMU 不能同时使用一个镜像启动两个虚拟机。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp openEuler-22.09-riscv64-qemu.qcow2 openEuler-22.09-riscv64-qemu-vm1.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要修改启动脚本中的镜像文件名，以及启动参数，将&lt;code&gt;drive&lt;/code&gt;以及&lt;code&gt;cmd&lt;/code&gt;变量的内容覆盖为下面的内容，修改&lt;code&gt;mac&lt;/code&gt;为分配给自己的虚拟机的 MAC 地址，&lt;code&gt;script&lt;/code&gt;为上面的脚本&lt;code&gt;qemu-ifup&lt;/code&gt;的路径。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 该脚本用于启动VM0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;drive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;openEuler-22.09-riscv64-qemu.qcow2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;....
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以&lt;code&gt;sudo&lt;/code&gt;权限启动脚本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ./preview_start_vm0.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以下为配置 VM1 过程，VM1 的启动脚本与 VM0 的启动脚本类似，只需要修改&lt;code&gt;drive&lt;/code&gt;以及&lt;code&gt;MAC&lt;/code&gt;，必须保证&lt;code&gt;MAC&lt;/code&gt;与 VM0 的&lt;code&gt;MAC&lt;/code&gt;不同。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 该脚本用于启动VM1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;drive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;openEuler-22.09-riscv64-qemu.qcow2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;....
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-net-device,netdev=tapnet,mac=80:d4:09:62:cd:3c \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;网络通信测试&#34;&gt;网络通信测试&lt;/h2&gt;
&lt;p&gt;当前网络状态如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;HOST:10.12.192.177
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;VM0:10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;VM1:10.12.193.101
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;host--vm0&#34;&gt;HOST &amp;ndash;&amp;gt; VM0&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# user @ ubuntu18 in ~/openeuler/openEuler2209 [18:41:54] &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ping -c &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; 10.12.193.101
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING 10.12.193.101 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.12.193.101&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.101: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.37 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.101: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.897 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.101: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.890 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- 10.12.193.101 ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2002ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.890/1.055/1.378/0.228 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Host &amp;ndash;&amp;gt; VM1 的测试结果与 Host &amp;ndash;&amp;gt; VM0 的测试结果相同。&lt;/p&gt;
&lt;h3 id=&#34;vm0--host&#34;&gt;VM0 &amp;ndash;&amp;gt; HOST&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ping -c 3 10.12.193.53 &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING 10.12.193.53 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.12.193.53&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.716 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.74 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.81 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- 10.12.193.53 ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2009ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.716/1.424/1.812/0.501 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;VM1 &amp;ndash;&amp;gt; Host 的测试结果与 Host &amp;ndash;&amp;gt; VM0 的测试结果相同。&lt;/p&gt;
&lt;h3 id=&#34;vm1--vm0&#34;&gt;VM1 &amp;ndash;&amp;gt; VM0&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ping -c 3 10.12.193.53 &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING 10.12.193.53 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.12.193.53&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.716 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.74 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.81 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- 10.12.193.53 ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2009ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.716/1.424/1.812/0.501 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;VM0 &amp;ndash;&amp;gt; VM1 与 VM1 &amp;ndash;&amp;gt; VM0 的测试结果相同。&lt;/p&gt;
&lt;h3 id=&#34;vm0--github&#34;&gt;VM0 &amp;ndash;&amp;gt; github&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ping -c 4 github.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;221&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;277&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;216&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;218&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- github.com ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 3014ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 215.984/232.733/276.593/25.374 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;host--github&#34;&gt;HOST &amp;ndash;&amp;gt; github&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# user @ ubuntu18 in ~/openeuler/openEuler2209 [17:59:40] &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ping -c &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;  github.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;218&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;216&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;216&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- github.com ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2002ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 216.252/217.087/218.409/0.945 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;原理探究-ongoing&#34;&gt;原理探究 (Ongoing)&lt;/h1&gt;
&lt;h2 id=&#34;step-by-step-解析&#34;&gt;Step by Step 解析&lt;/h2&gt;
&lt;p&gt;查看一下网络接口信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp3s0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::a00:27ff:fe32:e709  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:32:e7:09  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;6017&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;5412928&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;5.4 MB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;1979&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;179467&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;179.4 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lo: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 127.0.0.1  netmask 255.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 ::1  prefixlen &lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;  scopeid 0x10&amp;lt;host&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        loop  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Local Loopback&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;125&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;10142&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.1 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;125&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;10142&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.1 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建一个名为&lt;code&gt;br0&lt;/code&gt;的网桥&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addbr br0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将网桥与宿主机的网卡绑定&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addif br0 enp3s0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启用 &lt;code&gt;br0&lt;/code&gt; 接口，并从 DHCP 服务器获得 IP 地址&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig br0 0.0.0.0 promisc up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dhclient br0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看虚拟网桥列表&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl show br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bridge name	    bridge id		    STP enabled    interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0		            8000.e0be0388eec9  no             enp3s0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看 &lt;code&gt;br0&lt;/code&gt; 的各接口信息&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl showstp br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; bridge id		8000.e0be0388eec9
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.e0be0388eec9
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; root port			0			path cost &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; max age			20.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward delay		15.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;			2.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		300.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello timer		0.00s		&amp;lt;tbd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward timer		0.00s		&amp;lt;tbd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing timer		0.00s		&amp;lt;tbd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; enp3s0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id			8001			&lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; state forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; path cost			&lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated port	&lt;span class=&#34;m&#34;&gt;8001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward delay		15.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;			2.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; max age			20.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		300.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; priority			&lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当前网络拓扑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建一个 &lt;code&gt;tap0&lt;/code&gt; 接口用于&lt;code&gt;VM0&lt;/code&gt;使用，允许 &lt;code&gt;user&lt;/code&gt; 用户访问&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -t tap0 -u user       
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在虚拟网桥中增加 &lt;code&gt;tap0&lt;/code&gt; 接口&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addif br0 tap0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启用 tap0 接口，混杂模式&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig tap0 0.0.0.0 promisc up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将网桥的 MAC 地址修改为宿主机的 MAC 地址，这样就可以接入公司网络了。否则因为内网的 MAC 地址过滤，无法接入公司网络。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig br0  hw ether 08:00:27:32:e7:09  promisc up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看虚拟网桥列表&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo brctl show br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bridge name	    bridge id		    STP enabled    interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0		            8000.08002732e709  no             enp3s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                        tap0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看当前的网桥状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo brctl showstp br0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; bridge id		8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; root port		   0			path cost		   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; max age		  20.00			bridge max age		  20.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		   2.00			bridge hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;	   2.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward delay		  15.00			bridge forward delay	  15.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		 300.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello timer		   0.00			tcn timer		   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; topology change timer	   0.00			gc timer		   7.75
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; flags			
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp2s0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8001			state		     forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		   &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated port	8001			forward delay timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated cost	   0			hold timer		   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; flags			
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tap0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8002			state		     disabled
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		 &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated port	8002			forward delay timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated cost	   0			hold timer		   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; flags				
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;tap0&lt;/code&gt;可能处于&lt;code&gt;disabled&lt;/code&gt;状态，因为还没有虚拟机使用它。启动虚拟机之后会自动切换到&lt;code&gt;forwarding&lt;/code&gt;状态。&lt;/p&gt;
&lt;p&gt;当前网络拓扑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  +---------------------+                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |        tap0         |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |     IP: 0.0.0.0     |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启动 QEMU&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -netdev tap,id=tapnet,ifname=tap0,script=no,downscript=no \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关注这段脚本的网络配置部分：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;详细解释可以查看“QEMU 网络虚拟化章节”，第一个参数 &lt;code&gt;-device virtio-net-device&lt;/code&gt; 定义了名为 &lt;code&gt;virtio-net-device&lt;/code&gt; 的网络设备，并将其连接到一个名为 &lt;code&gt;tapnet&lt;/code&gt; 的网络设备上，指定它的 MAC 地址为 &lt;code&gt;e0:be:03:88:54:e8&lt;/code&gt;。第二个参数 &lt;code&gt;-netdev tap&lt;/code&gt; 用于指定后端实现，使用&lt;code&gt;tap&lt;/code&gt;方式，并且指定唯一 ID 为&lt;code&gt;tapnet&lt;/code&gt;由&lt;code&gt;-device&lt;/code&gt;参数中的子参数&lt;code&gt;netdev&lt;/code&gt;使用，指定&lt;code&gt;ifname=tap0&lt;/code&gt;，表示使用&lt;code&gt;tap0&lt;/code&gt;接口作为虚拟化的后端。&lt;code&gt;script=no&lt;/code&gt;和&lt;code&gt;downscript=no&lt;/code&gt;表示不使用脚本来启动和关闭&lt;code&gt;tap0&lt;/code&gt;接口。&lt;/p&gt;
&lt;p&gt;查看当前的网络接口信息&lt;code&gt;ifconfig&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4419&amp;lt;UP,BROADCAST,RUNNING,PROMISC,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::e2be:3ff:fe88:eec9  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:32:e7:09  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;861148&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;310707296&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;310.7 MB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;17556062&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1516515693&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.5 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp2s0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::4964:61f8:420d:6781  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:32:e7:09  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;894523&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;325547917&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;325.5 MB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;1926&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;17563568&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1516947572&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.5 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lo: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 127.0.0.1  netmask 255.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 ::1  prefixlen &lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;  scopeid 0x10&amp;lt;host&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        loop  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Local Loopback&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;1654925876&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;134933568498&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;134.9 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;1654925876&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;134933568498&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;134.9 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tap0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::f8ae:85ff:fed7:f9cd  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether fa:ae:85:d7:f9:cd  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;557&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;44913&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;44.9 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;7165&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;832171&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;832.1 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;55942&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当前网络拓扑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  +---------------------+                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |        tap0         |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |     IP: 0.0.0.0     |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------|-----------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------|-----------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |        eth0         |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |     IP:10.12.193.53 |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  +---------------------+                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                     VM0 (QEMU)                          |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看当前的网桥状态，可以看到 tap0 已经处于 forwarding 状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo brctl showstp br0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; bridge id		8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp2s0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8001			state		     forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		   &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age 		
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tap0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8002			state		     forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		 &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age 			
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;添加 VM1 过程就忽略了，添加后的网络拓扑如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|    +---------------------+  +-------------------+       |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|    |        tap0         |  |        tap1       |       |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|    |     IP: 0.0.0.0     |  |     IP: 0.0.0.0   |       |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------|-------------------------|---------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                |                         |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                v                         v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------|----------+  +-----------|---------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|   |     eth0         |   |  |   |        eth0       |   |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|   |  IP:10.12.193.53 |   |  |   |  IP:10.12.193.101 |   |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|   +---------------------+|  |   +-------------------+   |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|         VM0 (QEMU)       |  |         VM1 (QEMU)        |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+--------------------------+  +---------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;qemu-网络虚拟化&#34;&gt;QEMU 网络虚拟化&lt;/h2&gt;
&lt;p&gt;QEMU 对于网络的虚拟化需要两个参数来指定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;其中一个用于指定网络的前端驱动，也就是 Guest 中的实现&lt;/li&gt;
&lt;li&gt;另一个用于指定网络的后端实现，也就是在 Host 中的实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;QEMU 支持两种方式来实现网络虚拟化，一种是旧版本上使用的参数为 &lt;code&gt;-net&lt;/code&gt; 配合 &lt;code&gt;-net&lt;/code&gt; ，另一种是在新版本上支持的 &lt;code&gt;-device&lt;/code&gt; 配合 &lt;code&gt;-netdev&lt;/code&gt; 。QEMU 的发展趋势是倾向于用 &lt;code&gt;-device&lt;/code&gt; 一种命令格式来虚拟出不同的设备，其中包括网卡设备。&lt;/p&gt;
&lt;h3 id=&#34;-net---net-legacy&#34;&gt;-net &amp;amp; -net (legacy)&lt;/h3&gt;
&lt;p&gt;虽然仍然支持，但是逐步被废弃，不推荐使用。&lt;/p&gt;
&lt;p&gt;我们以以下命令为例，来说明 &lt;code&gt;-net&lt;/code&gt; 和 &lt;code&gt;-net&lt;/code&gt; 的使用方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;vcpu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;memory&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;drive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;openEuler-22.09-V1-riscv64-qemu.qcow2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fw&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;fw_payload_oe_qemuvirt.elf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -kernel &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios none \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -net nic,mac=52:54:00:12:34:56 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -net tap,ifname=tap0,script=no,downscript=no \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -append &amp;#39;root=/dev/vda1 rw console=ttyS0 swiotlb=1 loglevel=3 systemd.default_timeout_start_sec=600 selinux=0 highres=off mem=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory_append&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;M earlycon&amp;#39; &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中这两个参数即实现了虚拟化网络：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -net nic,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;52:54:00:12:34:56 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -net tap,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-net nic&lt;/code&gt; 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 默认的驱动，这个驱动是 QEMU 中的一个虚拟网卡设备，指定它的 MAC 地址为 &lt;code&gt;52:54:00:12:34:56&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第二个参数 &lt;code&gt;-net tap&lt;/code&gt; 用于指定后端实现，也就是 Host 中的实现，这里使用的是 &lt;code&gt;tap&lt;/code&gt; 驱动，它的网卡名称为 &lt;code&gt;tap0&lt;/code&gt;，并且不执行任何脚本。这两个参数的组合就实现了虚拟化网络。&lt;/p&gt;
&lt;p&gt;更多示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-net nic,model&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;virtio &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-net tap,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap3,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/ect/qemu/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-net nic&lt;/code&gt; 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 &lt;code&gt;virtio&lt;/code&gt; 驱动，这个驱动是 QEMU 中的一个虚拟网卡设备。第二个参数 &lt;code&gt;-net tap&lt;/code&gt; 用于指定后端实现，也就是 Host 中的实现，这里使用的是 &lt;code&gt;tap&lt;/code&gt; 驱动，它的网卡名称为 &lt;code&gt;tap3&lt;/code&gt;，并且执行脚本 &lt;code&gt;/ect/qemu/qemu-ifup&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;解释&lt;code&gt;/ect/qemu/qemu-ifup&lt;/code&gt;
该脚本用于创建网桥，将网桥与宿主机的网卡绑定，然后将虚拟网卡绑定到网桥上，这样虚拟机就可以通过网桥与宿主机通信，宿主机也可以通过网桥与虚拟机通信。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;-device---netdev-recommended&#34;&gt;-device &amp;amp; -netdev （Recommended）&lt;/h3&gt;
&lt;p&gt;这是新版本的 QEMU 支持的命令格式，也是 QEMU 未来的发展趋势，我们以以下命令为例，来说明 &lt;code&gt;-device&lt;/code&gt; 和 &lt;code&gt;-netdev&lt;/code&gt; 的使用方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv64 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -nographic -machine virt &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -smp &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;G &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -bios &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -drive &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;,format&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;qcow2,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -object rng-random,filename&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/urandom,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-vga &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-rng-device,rng&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-blk-device,drive&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/qemu-script/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中这两个参数即实现了虚拟化网络：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/qemu-script/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-device virtio-net-device&lt;/code&gt; 用于指定上述所说的前端驱动，也就是 Guest 中的实现，定义了名为 &lt;code&gt;virtio-net-device&lt;/code&gt; 的网络设备，并将其连接到一个名为 &lt;code&gt;tapnet&lt;/code&gt; 的网络设备上，指定它的 MAC 地址为 &lt;code&gt;e0:be:03:88:54:e8&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第二个参数 &lt;code&gt;-netdev tap&lt;/code&gt; 用于指定后端实现，使用&lt;code&gt;tap&lt;/code&gt;方式，并且指定唯一 ID 为&lt;code&gt;tapnet&lt;/code&gt;由&lt;code&gt;-device&lt;/code&gt;参数中的子参数&lt;code&gt;netdev&lt;/code&gt;使用。网卡名称为&lt;code&gt;tap0&lt;/code&gt;并且执行脚本 &lt;code&gt;~/qemu-script/qemu-ifup&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;-netdev 参数中 id 的使用
-netdev 参数中的 id 用于指定唯一的 ID，这个 ID 会被 &lt;code&gt;-device&lt;/code&gt; 参数中的子参数 &lt;code&gt;netdev&lt;/code&gt; 使用，这样 &lt;code&gt;-device&lt;/code&gt; 参数就知道要将前端驱动连接到哪个后端实现上了。id 可以自定义任意唯一字符串如&lt;code&gt;-netdev tap,id=test&lt;/code&gt;对应&lt;code&gt;-device virtio-net-device,netdev=test&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;更多示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-pci,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-device virtio-net-pci&lt;/code&gt; 定义了名为 &lt;code&gt;virtio-net-pci&lt;/code&gt; 的网络设备，并将其连接到一个名为 &lt;code&gt;tapnet&lt;/code&gt; 的网络设备上，指定它的 MAC 地址为 &lt;code&gt;e0:be:03:88:54:e8&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第二个参数，仔细观察会发现，我们没有定义链接到后端网卡的名称&lt;code&gt;ifname&lt;/code&gt;，这是因为以&lt;code&gt;tap&lt;/code&gt;模式启动 QEMU 时会自动创建&lt;code&gt;tap&lt;/code&gt;设备，具体网卡名称根据当前宿主机的网卡情况而定，默认会创建一个名为&lt;code&gt;tap0&lt;/code&gt;的网卡，如果启动了两个虚拟机，那么第二个虚拟机的网卡名称就是&lt;code&gt;tap1&lt;/code&gt;，以此类推。&lt;/p&gt;
&lt;h3 id=&#34;区分-tap-模式与-bridge-模式&#34;&gt;区分 tap 模式与 bridge 模式&lt;/h3&gt;
&lt;p&gt;我们有时候会用以下的命令进行 QEMU 虚拟机桥接网络的配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;bridgenet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;52:54:00:12:34:57 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev bridge,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;br0,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;bridgenet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这也能为我们创建一个桥接网络，这是因为它和 &lt;code&gt;-netdev tap&lt;/code&gt; 的工作方式是一样的，只是 &lt;code&gt;-netdev bridge&lt;/code&gt; 的简化写法，&lt;code&gt;qemu-bridge-helper&lt;/code&gt; 在背后替我们做了 &lt;code&gt;tap&lt;/code&gt; 设备创建以及将 &lt;code&gt;tap&lt;/code&gt; 设备加入桥接口的所有事情。&lt;/p&gt;
&lt;h3 id=&#34;添加多张网卡&#34;&gt;添加多张网卡&lt;/h3&gt;
&lt;p&gt;如果了解上述内容，添加多张网卡就十分容易实现了，我们只需要再添加一对 &lt;code&gt;-device&lt;/code&gt; 和 &lt;code&gt;-netdev&lt;/code&gt; 参数即可，如下所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv64 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -nographic -machine virt &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -smp &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;G &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -bios &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -drive &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;,format&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;qcow2,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -object rng-random,filename&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/urandom,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-vga &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-rng-device,rng&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-blk-device,drive&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet0,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifdown &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet1,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet1,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifdown &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要注意的是，我们需要为每个 &lt;code&gt;-device&lt;/code&gt; 参数指定一个唯一的 ID，这个 ID 会被 &lt;code&gt;-netdev&lt;/code&gt; 参数中的子参数 &lt;code&gt;netdev&lt;/code&gt; 使用，这样 &lt;code&gt;-device&lt;/code&gt; 参数就知道要将前端驱动连接到哪个后端实现上了。并且每个 &lt;code&gt;tap&lt;/code&gt; 设备只能被一个虚拟机使用，所以每个虚拟机的 &lt;code&gt;tap&lt;/code&gt; 设备名称不能相同。&lt;/p&gt;
&lt;p&gt;登录虚拟机查看网卡信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ifconfig &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eth0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.193.53  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::9e6:287b:30a2:574d  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether e0:be:03:88:54:e8  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;81&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;9871&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;9.6 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;19&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1735&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.6 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eth1: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.193.101  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::4fe0:9e1e:4681:52b7  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 80:d4:09:62:cd:3c  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;76&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;9471&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;9.2 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;15&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1708&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.6 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lo: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 127.0.0.1  netmask 255.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 ::1  prefixlen &lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;  scopeid 0x10&amp;lt;host&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        loop  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Local Loopback&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;不同网络策略工作方式&#34;&gt;不同网络策略工作方式&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;NAT 网络模式
&lt;ul&gt;
&lt;li&gt;NAT 网络以路由器的 NAT 功能为原理，允许虚拟机通过共享主机的 IP 地址访问互联网，但虚拟机之间不能直接通信。通过端口转发可以实现虚拟机之间的连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;桥接网络模式
&lt;ul&gt;
&lt;li&gt;桥接网络模式通过虚拟交换机连接虚拟机和主机，使得虚拟机可以通过局域网访问互联网，并允许虚拟机之间直接通信。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;内部网络模式
&lt;ul&gt;
&lt;li&gt;内部网络模式使得虚拟机可以创建一个完全隔离的网络，虚拟机之间可以直接通信，但无法访问互联网或外部网络。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;仅主机网络模式
&lt;ul&gt;
&lt;li&gt;仅主机网络模式允许虚拟机之间可以通信，并且与主机之间也可以通信，但无法访问互联网或外部网络。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;VM &amp;lt;&amp;gt; VM&lt;/th&gt;
          &lt;th&gt;VM → HOST&lt;/th&gt;
          &lt;th&gt;HOST → VM&lt;/th&gt;
          &lt;th&gt;VM → Internet&lt;/th&gt;
          &lt;th&gt;Internet → VM&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;网络地址转换 NAT&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;NAT 网络&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Bridged Adapter 桥接网卡&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;tuntap-网络设备&#34;&gt;TUN/TAP 网络设备&lt;/h2&gt;
&lt;p&gt;TAP 属于 Linux 内核支持的一种虚拟化网络设备，还有 TUN 也属于这种设备，它们完全由软件模拟实现，TUN/TAP 负责在内核协议栈和用户进程之间传送协议数据单元。TUN 工作在网络层，而 TAP 工作在数据链路层，TUN 负责与应用程序交换 IP 数据包，而 TAP 与应用程序交换以太网帧。所以 TUN 经常涉及路由，而 TAP 常用于网络桥接。&lt;/p&gt;
&lt;h1 id=&#34;ssh-远程登录虚拟机&#34;&gt;SSH 远程登录虚拟机&lt;/h1&gt;
&lt;p&gt;宿主机任意下目录执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-keygen -t rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Generating public/private rsa key pair.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter file in which to save the key &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;/home/user/.ssh/id_rsa&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: host2vm0_id_irsa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter passphrase &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;empty &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; no passphrase&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter same passphrase again: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your identification has been saved in host2vm0_id_irsa.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your public key has been saved in host2vm0_id_irsa.pub.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The key fingerprint is:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SHA256:OkWcw+R3x6Z2mzeYQuG033H3N9qIeym3TZKzz6YD8tQ user@ubuntu18
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The key&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;s randomart image is:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;RSA 2048&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;----+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        .        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; .   .   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        B .o. +  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       . oo.o+   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        S  ++ ..o&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       o ..+.E&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      o   +..B+&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       .   &lt;span class=&#34;nv&#34;&gt;oo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;@o+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;           &lt;span class=&#34;nv&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;**&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+----&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;SHA256&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;-----+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一直回车确定，生成公私钥，保存在&lt;code&gt;~/.ssh&lt;/code&gt;目录下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我在宿主机上生成的公私钥名称为，分别是&lt;code&gt;host2vm0_id_rsa&lt;/code&gt;,&lt;code&gt;host2vm0_id_rsa.pub&lt;/code&gt;方便我记忆。如果一直回车，那么生成的公私钥名称为&lt;code&gt;id_rsa&lt;/code&gt;，&lt;code&gt;id_rsa.pub&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将公钥复制到虚拟机 &lt;code&gt;VM0&lt;/code&gt; 上，以当前虚拟机 &lt;code&gt;VM0&lt;/code&gt; 的 IP：&lt;code&gt;10.12.193.53&lt;/code&gt; 为例。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-copy-id 10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, to filter out any that are already installed
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/ssh-copy-id: INFO: &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; key&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; remain to be installed -- &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; you are prompted now it is to install the new keys
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;user@10.12.193.53&lt;span class=&#34;s1&#34;&gt;&amp;#39;s password: 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;Number of key(s) added: 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;Now try logging into the machine, with:   &amp;#34;ssh &amp;#39;&lt;/span&gt;10.12.193.53&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;and check to make sure that only the key(s) you wanted were added.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后就可以直接免密码登录了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh user@10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;fixed-problems-ongoing&#34;&gt;Fixed Problems (Ongoing)&lt;/h1&gt;
&lt;h2 id=&#34;cannot-ioctl-tunsetiff-tap0-device-or-resource-busy-errno16&#34;&gt;cannot ioctl tunsetiff tap0 device or resource busy (errno=16)&lt;/h2&gt;
&lt;h2 id=&#34;failed-to-initialize-tap-device-operation-not-permitted&#34;&gt;failed to initialize tap device: Operation not permitted&lt;/h2&gt;
&lt;p&gt;同类型错误：failed to create TAP device: Operation not permitted。因为创建虚拟设备 &lt;code&gt;tap&lt;/code&gt; 需要 &lt;code&gt;root&lt;/code&gt; 权限，所以需要使用 &lt;code&gt;sudo&lt;/code&gt; 命令。执行 QEMU 启动是需要添加 &lt;code&gt;sudo&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;qemu-虚拟机启动后网卡处于-down-状态无法获取-ip&#34;&gt;QEMU 虚拟机启动后网卡处于 DOWN 状态，无法获取 IP&lt;/h2&gt;
&lt;p&gt;查看是否是 MAC 地址配置错误，使用下面命令检查：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig eth0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SIOCSIFFLAGS: Cannot assign requested address
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果报错，参考下面章节&lt;strong&gt;SIOCSIFFLAGS: Cannot assign requested address&lt;/strong&gt;解决方法进行解决。&lt;/p&gt;
&lt;h2 id=&#34;虚拟机可以-ping-通外网宿主机无法-ping-外网&#34;&gt;虚拟机可以 ping 通外网，宿主机无法 ping 外网&lt;/h2&gt;
&lt;p&gt;这种情况说明基本网络没有问题，只是 DNS 解析有问题，可以通过修改&lt;code&gt;/etc/resolv.conf&lt;/code&gt;文件解决。&lt;/p&gt;
&lt;p&gt;海宁 DNS 服务器地址：&lt;code&gt;10.12.2.21&lt;/code&gt; 和 &lt;code&gt;10.12.2.22&lt;/code&gt;，我的情况是只能 &lt;code&gt;ping 10.12.2.21&lt;/code&gt;，可以选择自己能 &lt;code&gt;ping&lt;/code&gt; 通的 DNS 服务器地址。如果无法 &lt;code&gt;ping&lt;/code&gt; 通，说明问题不在这，需要自行解决。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改 DNS 服务器地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加以下内容&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 10.12.2.21
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 10.12.2.22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;网络配置错误如何恢复配置之前的状态&#34;&gt;网络配置错误，如何恢复配置之前的状态&lt;/h2&gt;
&lt;p&gt;最简单的方式 - 重启，因为所有操作都是命令行配置，都是临时配置，可以直接重启解决。&lt;/p&gt;
&lt;p&gt;既然有这一小节，说明肯定有时候不方便直接重启，那么就需要手动恢复配置之前的状态。但是能够恢复的&lt;strong&gt;前提是需要记得之前的网卡 IP 地址、子网掩码、网关、广播地址等信息&lt;/strong&gt;。这些信息在局域网里，可能只有 IP 不同，其他信息如果没记住可以查看其他同事的网卡配置即可。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将网桥绑定的网卡从网桥上移除&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl delif br0 enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl delif br0 tap0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 配置宿主机网卡信息，必须一字不差，保持和之前一模一样才能恢复&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip addr add 10.12.192.173/20 broadcast 10.12.207.255 dev enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 必须设置网关&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip route add default via 10.12.192.1 dev enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 重启网络管理器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart NetworkManager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;-netdev-tapidtapnetscriptqemu-scriptqemu-ifupnetwork-script-qemu-scriptqemu-ifup-failed-with-status-256&#34;&gt;-netdev tap,id=tapnet,script=/qemu-script/qemu-ifup,:network script /qemu-script/qemu-ifup failed with status 256&lt;/h2&gt;
&lt;p&gt;可能原因 1: &lt;code&gt;qemu-ifup&lt;/code&gt; 脚本没有执行权限，需要添加执行权限。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x qemu-ifup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可能原因 2: &lt;code&gt;qemu-ifup&lt;/code&gt; 路径不对，必须放到&lt;code&gt;/etc/qemu/&lt;/code&gt;目录下。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /etc/qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv qemu-ifup /etc/qemu &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mv qemu-ifdown /etc/qemu 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;siocsifflags-cannot-assign-requested-address&#34;&gt;SIOCSIFFLAGS: Cannot assign requested address&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig eth0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SIOCSIFFLAGS: Cannot assign requested address
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一般由于 MAC 地址配置错误导致，可以通过修改 MAC 地址为多播地址解决。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig enp2s0 hw ether 00:11:22:33:44:55
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重启网卡&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig enp2s0 down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig enp2s0 up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MAC 地址的第一个字节中的最后一位（即第 7 位）用于标识该地址是单播，多播还是广播地址。如果这个位设置为 0，则表示这是一个单播地址；如果设置为 1，则表示这是一个多播或广播地址。&lt;/p&gt;
&lt;p&gt;使用这种方法，我们可以确定上述每个 MAC 地址是否是单播地址：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd:c2:05:84:c8:2c&lt;/code&gt; - 单播地址&lt;/li&gt;
&lt;li&gt;&lt;code&gt;13:7b:49:fc:a6:aa&lt;/code&gt; - 单播地址&lt;/li&gt;
&lt;li&gt;&lt;code&gt;8f:aa:42:29:e8:68&lt;/code&gt; - 单播地址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;00:11:22:33:44:55&lt;/code&gt; 是多播地址。&lt;/p&gt;
&lt;h2 id=&#34;qemu--device-drive-with-0-bus0-unit0-exists&#34;&gt;qemu -device drive with 0 bus=0 unit=0 exists&lt;/h2&gt;
&lt;p&gt;这个错误通常意味着您尝试在 QEMU VM 中添加一个重复的设备。&lt;/p&gt;
&lt;p&gt;如果您已经在 VM 中添加了驱动器，则可能会出现此问题。您可以检查是否存在两个具有相同 &lt;code&gt;bus&lt;/code&gt; 和 &lt;code&gt;unit&lt;/code&gt; 的设备（在此情况下，都是 0）。解决此问题的方法是删除重复设备或更改其配置以包括唯一的 &lt;code&gt;bus&lt;/code&gt; 和 &lt;code&gt;unit&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果您没有意图添加重复的设备，在运行 QEMU 之前，您可能需要检查您的命令行，以确保正确设置了 &lt;code&gt;-drive&lt;/code&gt; 选项。请注意，当使用 &lt;code&gt;-device&lt;/code&gt; 添加设备时，您还应该避免使用 &lt;code&gt;-drive&lt;/code&gt; 选项，因为它们可能引起冲突。&lt;/p&gt;
&lt;p&gt;如果您需要进一步帮助，建议提供完整的 QEMU 命令和参数列表，以便更好地理解问题并提供更详细的建议。&lt;/p&gt;
&lt;h1 id=&#34;参考资料&#34;&gt;参考资料&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://tomwei7.com/2021/10/09/qemu-network-config/&#34;&gt;QEMU 网络配置 // 围城&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.junmajinlong.com/img/virtual/1594802457384.png&#34;&gt;理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://wzt.ac.cn/2021/05/28/QEMU-networking/&#34;&gt;QEMU 网络配置一把梭 | CataLpa&amp;rsquo;s Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.csdn.net/u014022631/article/details/53411557&#34;&gt;qemu 虚拟机与外部网络的通信 li_Jiejun 的博客-CSDN 博客&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html&#34;&gt;安装 qemu-kvm 以及配置桥接网络&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.csdn.net/rikeyone/article/details/106767540&#34;&gt;QEMU 中的网络虚拟化配置_程序猿 Ricky 的日常干货的博客-CSDN 博客&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/QEMU/&#34;&gt;Nginx Directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/huqingyu/archive/2005/04/03/131102.html&#34;&gt;QEMU 网络配置 - 浙林龙哥 - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://zhuanlan.zhihu.com/p/432022126&#34;&gt;【qemu】qemu 网络配置 - 知乎&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tomwei7.com/2021/10/09/qemu-network-config/&#34;&gt;QEMU 网络配置 // 围城&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html&#34;&gt;安装 qemu-kvm 以及配置桥接网络&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.virt.ltd/blog/archives/37/&#34;&gt;在 qemu 中使用桥接网络 - T^3 Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://wiki.yanick.site/wiki/os/qemu/&#34;&gt;为 QEMU 配置网桥上网 | Yanick&amp;rsquo;s Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.junmajinlong.com/virtual/network/all_about_tun_tap/&#34;&gt;理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://wzt.ac.cn/2021/05/28/QEMU-networking/&#34;&gt;QEMU 网络配置一把梭 | CataLpa&amp;rsquo;s Site&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;附录&#34;&gt;附录&lt;/h1&gt;
</description>
      <content:encoded><![CDATA[<h1 id="quick-setup">Quick Setup</h1>
<h2 id="安装工具">安装工具</h2>
<p>安装两个网络管理工具用于建立网桥以及虚拟网卡：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装虚拟网桥工具</span>
</span></span><span class="line"><span class="cl">sudo apt install bridge-utils -y
</span></span><span class="line"><span class="cl"><span class="c1"># UML（User-mode linux）工具        </span>
</span></span><span class="line"><span class="cl">sudo apt install uml-utilities  -y   
</span></span></code></pre></div><h2 id="配置脚本">配置脚本</h2>
<h3 id="qemu-ifup">qemu-ifup</h3>
<p>将下面的脚本保存为文件 <code>qemu-ifup</code>，并赋予可执行权限：</p>
<blockquote>
<p>为了方便复制脚本，在 confluence 页面提供了脚本内容，可以直接复制。</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /etc/qemu
</span></span><span class="line"><span class="cl">mv qemu-ifup /etc/qemu <span class="o">&amp;&amp;</span> mv qemu-ifdown /etc/qemu 
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifup
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifdown
</span></span></code></pre></div><p>因为网卡信息不容易定位，可能一台机器有多个网卡，所以不方便用脚本获取，需要手动设置一下。将下面的<code>NIC</code>值修改为宿主机可以上网的网卡名称。可以通过<code>ifconfig</code>命令查看。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># 设置默认网卡信息</span>
</span></span><span class="line"><span class="cl"><span class="nv">NIC</span><span class="o">=</span>enp2s0
</span></span><span class="line"><span class="cl"><span class="c1"># 设置用户名</span>
</span></span><span class="line"><span class="cl"><span class="nv">USER_NAME</span><span class="o">=</span>user
</span></span><span class="line"><span class="cl"><span class="c1"># 设置网桥名称</span>
</span></span><span class="line"><span class="cl"><span class="nv">BRIDGE</span><span class="o">=</span>br0
</span></span><span class="line"><span class="cl"><span class="c1"># 设置网络信息</span>
</span></span><span class="line"><span class="cl"><span class="nv">NIC_IP</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;inet\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $2}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">NIC_NETMAST</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;inet\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $4}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">NIC_BROADCAST</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;inet\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $6}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">NETMASK</span><span class="o">=</span>255.255.240.0
</span></span><span class="line"><span class="cl"><span class="c1"># 设置默认网关地址</span>
</span></span><span class="line"><span class="cl"><span class="nv">GATEWAY</span><span class="o">=</span>10.12.192.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 获取宿主机网卡MAC地址，因为创建的网桥MAC地址是随机的，</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 无法接入公司，需要从开发机网卡将其MAC地址赋值给网桥</span>
</span></span><span class="line"><span class="cl"><span class="nv">MAC</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;ether\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $2}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查网桥是否已创建，已创建就忽略</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> check_bridge<span class="o">()</span> 
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    echO <span class="s2">&#34;Check bridge...&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> brctl show <span class="p">|</span> grep <span class="s2">&#34;^</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span> <span class="p">&amp;</span>&gt; /dev/null<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建网桥</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> create_bridge<span class="o">()</span> 
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Start Create bridge...&#34;</span>
</span></span><span class="line"><span class="cl">    brctl addbr <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    brctl addif <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>  <span class="s2">&#34;</span><span class="nv">$NIC</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    ifconfig br0 0.0.0.0 promisc up
</span></span><span class="line"><span class="cl">    dhclient <span class="nv">$BRIDGE</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ifconfig &#34;$BRIDGE&#34; &#34;$NIC_IP&#34; netmask &#34;$NIC_NETMAST&#34; broadcast &#34;$NIC_BROADCAST&#34;  hw ether &#34;$MAC&#34; promisc up</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 启用IP转发</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> enable_ip_forward<span class="o">()</span> 
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="m">1</span> &gt; /proc/sys/net/ipv4/ip_forward
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置网桥</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> setup_bridge<span class="o">()</span>
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    check_bridge <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> -eq <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        create_bridge
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    enable_ip_forward
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    setup_bridge
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Creating </span><span class="nv">$1</span><span class="s2">...&#34;</span>
</span></span><span class="line"><span class="cl">    tunctl -t <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> -u <span class="s2">&#34;</span><span class="nv">$USER_NAME</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    ifconfig <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> 0.0.0.0 up
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Adding </span><span class="nv">$1</span><span class="s2"> to </span><span class="nv">$BRIDGE</span><span class="s2">...&#34;</span>
</span></span><span class="line"><span class="cl">    brctl addif <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    sleep <span class="m">5</span>
</span></span><span class="line"><span class="cl">    ifconfig <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>  hw ether <span class="s2">&#34;</span><span class="nv">$MAC</span><span class="s2">&#34;</span> promisc up
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Error: no interface specified.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><h3 id="qemu-ifdown">qemu-ifdown</h3>
<p>以下是<code>qemu-ifdown</code>脚本，用于在关闭 QEMU 时关闭虚拟网卡，将其从网桥中移除，删除虚拟网卡。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># 设置网桥名称</span>
</span></span><span class="line"><span class="cl"><span class="nv">BRIDGE</span><span class="o">=</span>br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 将tap设备从网桥中移除</span>
</span></span><span class="line"><span class="cl">	brctl delif <span class="si">${</span><span class="nv">BRIDGE</span><span class="si">}</span> <span class="nv">$1</span>
</span></span><span class="line"><span class="cl">	<span class="c1"># 关闭tap设备</span>
</span></span><span class="line"><span class="cl">	ip link link <span class="nv">$1</span> down
</span></span><span class="line"><span class="cl">    <span class="c1"># 删除tap设备</span>
</span></span><span class="line"><span class="cl">    ip link del <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">	tunctl -d <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">	<span class="nb">echo</span> <span class="s2">&#34;Error: no interface specified&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><p>将系统镜像复制一份并修改文件名，QEMU 不能同时使用一个镜像启动两个虚拟机。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp openEuler-22.09-riscv64-qemu.qcow2 openEuler-22.09-riscv64-qemu-vm1.qcow2
</span></span></code></pre></div><p>需要修改启动脚本中的镜像文件名，以及启动参数，将<code>drive</code>以及<code>cmd</code>变量的内容覆盖为下面的内容，修改<code>mac</code>为分配给自己的虚拟机的 MAC 地址，<code>script</code>为上面的脚本<code>qemu-ifup</code>的路径。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 该脚本用于启动VM0</span>
</span></span><span class="line"><span class="cl"><span class="nv">drive</span><span class="o">=</span><span class="s2">&#34;openEuler-22.09-riscv64-qemu.qcow2&#34;</span>
</span></span><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&#34;</span>
</span></span></code></pre></div><p>以<code>sudo</code>权限启动脚本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ./preview_start_vm0.sh
</span></span></code></pre></div><p>以下为配置 VM1 过程，VM1 的启动脚本与 VM0 的启动脚本类似，只需要修改<code>drive</code>以及<code>MAC</code>，必须保证<code>MAC</code>与 VM0 的<code>MAC</code>不同。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 该脚本用于启动VM1</span>
</span></span><span class="line"><span class="cl"><span class="nv">drive</span><span class="o">=</span><span class="s2">&#34;openEuler-22.09-riscv64-qemu.qcow2&#34;</span>
</span></span><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-net-device,netdev=tapnet,mac=80:d4:09:62:cd:3c \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&#34;</span>
</span></span></code></pre></div><h2 id="网络通信测试">网络通信测试</h2>
<p>当前网络状态如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">HOST:10.12.192.177
</span></span><span class="line"><span class="cl">VM0:10.12.193.53
</span></span><span class="line"><span class="cl">VM1:10.12.193.101
</span></span></code></pre></div><h3 id="host--vm0">HOST &ndash;&gt; VM0</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># user @ ubuntu18 in ~/openeuler/openEuler2209 [18:41:54] </span>
</span></span><span class="line"><span class="cl">$ ping -c <span class="m">3</span> 10.12.193.101
</span></span><span class="line"><span class="cl">PING 10.12.193.101 <span class="o">(</span>10.12.193.101<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.101: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.37 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.101: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.897 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.101: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.890 ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- 10.12.193.101 ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2002ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 0.890/1.055/1.378/0.228 ms
</span></span></code></pre></div><p>Host &ndash;&gt; VM1 的测试结果与 Host &ndash;&gt; VM0 的测试结果相同。</p>
<h3 id="vm0--host">VM0 &ndash;&gt; HOST</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ping -c 3 10.12.193.53 </span>
</span></span><span class="line"><span class="cl">PING 10.12.193.53 <span class="o">(</span>10.12.193.53<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.716 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.74 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.81 ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- 10.12.193.53 ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2009ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 0.716/1.424/1.812/0.501 ms
</span></span></code></pre></div><p>VM1 &ndash;&gt; Host 的测试结果与 Host &ndash;&gt; VM0 的测试结果相同。</p>
<h3 id="vm1--vm0">VM1 &ndash;&gt; VM0</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ping -c 3 10.12.193.53 </span>
</span></span><span class="line"><span class="cl">PING 10.12.193.53 <span class="o">(</span>10.12.193.53<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.716 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.74 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.81 ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- 10.12.193.53 ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2009ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 0.716/1.424/1.812/0.501 ms
</span></span></code></pre></div><p>VM0 &ndash;&gt; VM1 与 VM1 &ndash;&gt; VM0 的测试结果相同。</p>
<h3 id="vm0--github">VM0 &ndash;&gt; github</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ping -c 4 github.com</span>
</span></span><span class="line"><span class="cl">PING github.com <span class="o">(</span>192.30.255.113<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">221</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">277</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">216</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">4</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">218</span> ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- github.com ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">4</span> packets transmitted, <span class="m">4</span> received, 0% packet loss, <span class="nb">time</span> 3014ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 215.984/232.733/276.593/25.374 ms
</span></span></code></pre></div><h3 id="host--github">HOST &ndash;&gt; github</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># user @ ubuntu18 in ~/openeuler/openEuler2209 [17:59:40] </span>
</span></span><span class="line"><span class="cl">$ ping -c <span class="m">3</span>  github.com
</span></span><span class="line"><span class="cl">PING github.com <span class="o">(</span>192.30.255.113<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">218</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">216</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">216</span> ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- github.com ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2002ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 216.252/217.087/218.409/0.945 ms
</span></span></code></pre></div><h1 id="原理探究-ongoing">原理探究 (Ongoing)</h1>
<h2 id="step-by-step-解析">Step by Step 解析</h2>
<p>查看一下网络接口信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ifconfig
</span></span><span class="line"><span class="cl">enp3s0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::a00:27ff:fe32:e709  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:32:e7:09  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">6017</span>  bytes <span class="m">5412928</span> <span class="o">(</span>5.4 MB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">1979</span>  bytes <span class="m">179467</span> <span class="o">(</span>179.4 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">lo: <span class="nv">flags</span><span class="o">=</span>73&lt;UP,LOOPBACK,RUNNING&gt;  mtu <span class="m">65536</span>
</span></span><span class="line"><span class="cl">        inet 127.0.0.1  netmask 255.0.0.0
</span></span><span class="line"><span class="cl">        inet6 ::1  prefixlen <span class="m">128</span>  scopeid 0x10&lt;host&gt;
</span></span><span class="line"><span class="cl">        loop  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Local Loopback<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">125</span>  bytes <span class="m">10142</span> <span class="o">(</span>10.1 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">125</span>  bytes <span class="m">10142</span> <span class="o">(</span>10.1 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><p>创建一个名为<code>br0</code>的网桥</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl addbr br0
</span></span></code></pre></div><p>将网桥与宿主机的网卡绑定</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl addif br0 enp3s0
</span></span></code></pre></div><p>启用 <code>br0</code> 接口，并从 DHCP 服务器获得 IP 地址</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ifconfig br0 0.0.0.0 promisc up
</span></span><span class="line"><span class="cl">sudo dhclient br0
</span></span></code></pre></div><p>查看虚拟网桥列表</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl show br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">bridge name	    bridge id		    STP enabled    interfaces
</span></span><span class="line"><span class="cl">br0		            8000.e0be0388eec9  no             enp3s0
</span></span></code></pre></div><p>查看 <code>br0</code> 的各接口信息</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl showstp br0
</span></span><span class="line"><span class="cl">br0
</span></span><span class="line"><span class="cl"> bridge id		8000.e0be0388eec9
</span></span><span class="line"><span class="cl"> designated root	8000.e0be0388eec9
</span></span><span class="line"><span class="cl"> root port			0			path cost <span class="m">0</span>
</span></span><span class="line"><span class="cl"> max age			20.00s
</span></span><span class="line"><span class="cl"> forward delay		15.00s
</span></span><span class="line"><span class="cl"> hello <span class="nb">time</span>			2.00s
</span></span><span class="line"><span class="cl"> ageing <span class="nb">time</span>		300.00s
</span></span><span class="line"><span class="cl"> hello timer		0.00s		&lt;tbd&gt;
</span></span><span class="line"><span class="cl"> forward timer		0.00s		&lt;tbd&gt;
</span></span><span class="line"><span class="cl"> ageing timer		0.00s		&lt;tbd&gt;
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> enp3s0 <span class="o">(</span>1<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id			8001			<span class="nb">local</span> state forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709
</span></span><span class="line"><span class="cl"> path cost			<span class="m">100</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709
</span></span><span class="line"><span class="cl"> designated port	<span class="m">8001</span>
</span></span><span class="line"><span class="cl"> forward delay		15.00s
</span></span><span class="line"><span class="cl"> hello <span class="nb">time</span>			2.00s
</span></span><span class="line"><span class="cl"> max age			20.00s
</span></span><span class="line"><span class="cl"> ageing <span class="nb">time</span>		300.00s
</span></span><span class="line"><span class="cl"> priority			<span class="m">128</span>
</span></span></code></pre></div><p>当前网络拓扑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span></code></pre></div><p>创建一个 <code>tap0</code> 接口用于<code>VM0</code>使用，允许 <code>user</code> 用户访问</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo tunctl -t tap0 -u user       
</span></span></code></pre></div><p>在虚拟网桥中增加 <code>tap0</code> 接口</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl addif br0 tap0
</span></span></code></pre></div><p>启用 tap0 接口，混杂模式</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ifconfig tap0 0.0.0.0 promisc up
</span></span></code></pre></div><p>将网桥的 MAC 地址修改为宿主机的 MAC 地址，这样就可以接入公司网络了。否则因为内网的 MAC 地址过滤，无法接入公司网络。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ifconfig br0  hw ether 08:00:27:32:e7:09  promisc up
</span></span></code></pre></div><p>查看虚拟网桥列表</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo brctl show br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">bridge name	    bridge id		    STP enabled    interfaces
</span></span><span class="line"><span class="cl">br0		            8000.08002732e709  no             enp3s0
</span></span><span class="line"><span class="cl">                                                        tap0
</span></span></code></pre></div><p>查看当前的网桥状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo brctl showstp br0 
</span></span><span class="line"><span class="cl">br0
</span></span><span class="line"><span class="cl"> bridge id		8000.08002732e709
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709
</span></span><span class="line"><span class="cl"> root port		   0			path cost		   <span class="m">0</span>
</span></span><span class="line"><span class="cl"> max age		  20.00			bridge max age		  20.00
</span></span><span class="line"><span class="cl"> hello <span class="nb">time</span>		   2.00			bridge hello <span class="nb">time</span>	   2.00
</span></span><span class="line"><span class="cl"> forward delay		  15.00			bridge forward delay	  15.00
</span></span><span class="line"><span class="cl"> ageing <span class="nb">time</span>		 300.00
</span></span><span class="line"><span class="cl"> hello timer		   0.00			tcn timer		   0.00
</span></span><span class="line"><span class="cl"> topology change timer	   0.00			gc timer		   7.75
</span></span><span class="line"><span class="cl"> flags			
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp2s0 <span class="o">(</span>1<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8001			state		     forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		   <span class="m">4</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age timer	   0.00
</span></span><span class="line"><span class="cl"> designated port	8001			forward delay timer	   0.00
</span></span><span class="line"><span class="cl"> designated cost	   0			hold timer		   0.00
</span></span><span class="line"><span class="cl"> flags			
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tap0 <span class="o">(</span>2<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8002			state		     disabled
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		 <span class="m">100</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age timer	   0.00
</span></span><span class="line"><span class="cl"> designated port	8002			forward delay timer	   0.00
</span></span><span class="line"><span class="cl"> designated cost	   0			hold timer		   0.00
</span></span><span class="line"><span class="cl"> flags				
</span></span></code></pre></div><p><code>tap0</code>可能处于<code>disabled</code>状态，因为还没有虚拟机使用它。启动虚拟机之后会自动切换到<code>forwarding</code>状态。</p>
<p>当前网络拓扑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">|                  +---------------------+                |
</span></span><span class="line"><span class="cl">|                  |        tap0         |                |
</span></span><span class="line"><span class="cl">|                  |     IP: 0.0.0.0     |                |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span></code></pre></div><p>启动 QEMU</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -netdev tap,id=tapnet,ifname=tap0,script=no,downscript=no \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&#34;</span>
</span></span></code></pre></div><p>关注这段脚本的网络配置部分：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,ifname<span class="o">=</span>tap0,script<span class="o">=</span>no,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>详细解释可以查看“QEMU 网络虚拟化章节”，第一个参数 <code>-device virtio-net-device</code> 定义了名为 <code>virtio-net-device</code> 的网络设备，并将其连接到一个名为 <code>tapnet</code> 的网络设备上，指定它的 MAC 地址为 <code>e0:be:03:88:54:e8</code>。第二个参数 <code>-netdev tap</code> 用于指定后端实现，使用<code>tap</code>方式，并且指定唯一 ID 为<code>tapnet</code>由<code>-device</code>参数中的子参数<code>netdev</code>使用，指定<code>ifname=tap0</code>，表示使用<code>tap0</code>接口作为虚拟化的后端。<code>script=no</code>和<code>downscript=no</code>表示不使用脚本来启动和关闭<code>tap0</code>接口。</p>
<p>查看当前的网络接口信息<code>ifconfig</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">br0: <span class="nv">flags</span><span class="o">=</span>4419&lt;UP,BROADCAST,RUNNING,PROMISC,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::e2be:3ff:fe88:eec9  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:32:e7:09  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">861148</span>  bytes <span class="m">310707296</span> <span class="o">(</span>310.7 MB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">17556062</span>  bytes <span class="m">1516515693</span> <span class="o">(</span>1.5 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp2s0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::4964:61f8:420d:6781  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:32:e7:09  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">894523</span>  bytes <span class="m">325547917</span> <span class="o">(</span>325.5 MB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">1926</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">17563568</span>  bytes <span class="m">1516947572</span> <span class="o">(</span>1.5 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">lo: <span class="nv">flags</span><span class="o">=</span>73&lt;UP,LOOPBACK,RUNNING&gt;  mtu <span class="m">65536</span>
</span></span><span class="line"><span class="cl">        inet 127.0.0.1  netmask 255.0.0.0
</span></span><span class="line"><span class="cl">        inet6 ::1  prefixlen <span class="m">128</span>  scopeid 0x10&lt;host&gt;
</span></span><span class="line"><span class="cl">        loop  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Local Loopback<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">1654925876</span>  bytes <span class="m">134933568498</span> <span class="o">(</span>134.9 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">1654925876</span>  bytes <span class="m">134933568498</span> <span class="o">(</span>134.9 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tap0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet6 fe80::f8ae:85ff:fed7:f9cd  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether fa:ae:85:d7:f9:cd  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">557</span>  bytes <span class="m">44913</span> <span class="o">(</span>44.9 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">7165</span>  bytes <span class="m">832171</span> <span class="o">(</span>832.1 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">55942</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><p>当前网络拓扑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">|                  +---------------------+                |
</span></span><span class="line"><span class="cl">|                  |        tap0         |                |
</span></span><span class="line"><span class="cl">|                  |     IP: 0.0.0.0     |                |
</span></span><span class="line"><span class="cl">+---------------------------|-----------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------|-----------------------------+
</span></span><span class="line"><span class="cl">|                  |        eth0         |                |
</span></span><span class="line"><span class="cl">|                  |     IP:10.12.193.53 |                |
</span></span><span class="line"><span class="cl">|                  +---------------------+                |
</span></span><span class="line"><span class="cl">|                     VM0 (QEMU)                          |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span></code></pre></div><p>查看当前的网桥状态，可以看到 tap0 已经处于 forwarding 状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo brctl showstp br0 
</span></span><span class="line"><span class="cl">br0
</span></span><span class="line"><span class="cl"> bridge id		8000.08002732e709
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp2s0 <span class="o">(</span>1<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8001			state		     forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		   <span class="m">4</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age 		
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tap0 <span class="o">(</span>2<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8002			state		     forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		 <span class="m">100</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age 			
</span></span></code></pre></div><p>添加 VM1 过程就忽略了，添加后的网络拓扑如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">|    +---------------------+  +-------------------+       |
</span></span><span class="line"><span class="cl">|    |        tap0         |  |        tap1       |       |
</span></span><span class="line"><span class="cl">|    |     IP: 0.0.0.0     |  |     IP: 0.0.0.0   |       |
</span></span><span class="line"><span class="cl">+---------------|-------------------------|---------------+
</span></span><span class="line"><span class="cl">                |                         |
</span></span><span class="line"><span class="cl">                v                         v
</span></span><span class="line"><span class="cl">+---------------|----------+  +-----------|---------------+
</span></span><span class="line"><span class="cl">|   |     eth0         |   |  |   |        eth0       |   |
</span></span><span class="line"><span class="cl">|   |  IP:10.12.193.53 |   |  |   |  IP:10.12.193.101 |   |
</span></span><span class="line"><span class="cl">|   +---------------------+|  |   +-------------------+   |
</span></span><span class="line"><span class="cl">|         VM0 (QEMU)       |  |         VM1 (QEMU)        |
</span></span><span class="line"><span class="cl">+--------------------------+  +---------------------------+
</span></span></code></pre></div><h2 id="qemu-网络虚拟化">QEMU 网络虚拟化</h2>
<p>QEMU 对于网络的虚拟化需要两个参数来指定：</p>
<ul>
<li>其中一个用于指定网络的前端驱动，也就是 Guest 中的实现</li>
<li>另一个用于指定网络的后端实现，也就是在 Host 中的实现。</li>
</ul>
<p>QEMU 支持两种方式来实现网络虚拟化，一种是旧版本上使用的参数为 <code>-net</code> 配合 <code>-net</code> ，另一种是在新版本上支持的 <code>-device</code> 配合 <code>-netdev</code> 。QEMU 的发展趋势是倾向于用 <code>-device</code> 一种命令格式来虚拟出不同的设备，其中包括网卡设备。</p>
<h3 id="-net---net-legacy">-net &amp; -net (legacy)</h3>
<p>虽然仍然支持，但是逐步被废弃，不推荐使用。</p>
<p>我们以以下命令为例，来说明 <code>-net</code> 和 <code>-net</code> 的使用方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">vcpu</span><span class="o">=</span><span class="m">8</span>
</span></span><span class="line"><span class="cl"><span class="nv">memory</span><span class="o">=</span><span class="m">8</span>
</span></span><span class="line"><span class="cl"><span class="nv">drive</span><span class="o">=</span><span class="s2">&#34;openEuler-22.09-V1-riscv64-qemu.qcow2&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">fw</span><span class="o">=</span><span class="s2">&#34;fw_payload_oe_qemuvirt.elf&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -kernel &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios none \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -net nic,mac=52:54:00:12:34:56 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -net tap,ifname=tap0,script=no,downscript=no \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -append &#39;root=/dev/vda1 rw console=ttyS0 swiotlb=1 loglevel=3 systemd.default_timeout_start_sec=600 selinux=0 highres=off mem=&#34;</span><span class="nv">$memory_append</span><span class="s2">&#34;M earlycon&#39; &#34;</span>
</span></span></code></pre></div><p>其中这两个参数即实现了虚拟化网络：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -net nic,mac<span class="o">=</span>52:54:00:12:34:56 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -net tap,ifname<span class="o">=</span>tap0,script<span class="o">=</span>no,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-net nic</code> 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 默认的驱动，这个驱动是 QEMU 中的一个虚拟网卡设备，指定它的 MAC 地址为 <code>52:54:00:12:34:56</code>。</p>
<p>第二个参数 <code>-net tap</code> 用于指定后端实现，也就是 Host 中的实现，这里使用的是 <code>tap</code> 驱动，它的网卡名称为 <code>tap0</code>，并且不执行任何脚本。这两个参数的组合就实现了虚拟化网络。</p>
<p>更多示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">-net nic,model<span class="o">=</span>virtio <span class="se">\
</span></span></span><span class="line"><span class="cl">-net tap,ifname<span class="o">=</span>tap3,script<span class="o">=</span>/ect/qemu/qemu-ifup,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-net nic</code> 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 <code>virtio</code> 驱动，这个驱动是 QEMU 中的一个虚拟网卡设备。第二个参数 <code>-net tap</code> 用于指定后端实现，也就是 Host 中的实现，这里使用的是 <code>tap</code> 驱动，它的网卡名称为 <code>tap3</code>，并且执行脚本 <code>/ect/qemu/qemu-ifup</code>。</p>
<blockquote>
<p>解释<code>/ect/qemu/qemu-ifup</code>
该脚本用于创建网桥，将网桥与宿主机的网卡绑定，然后将虚拟网卡绑定到网桥上，这样虚拟机就可以通过网桥与宿主机通信，宿主机也可以通过网桥与虚拟机通信。</p>
</blockquote>
<h3 id="-device---netdev-recommended">-device &amp; -netdev （Recommended）</h3>
<p>这是新版本的 QEMU 支持的命令格式，也是 QEMU 未来的发展趋势，我们以以下命令为例，来说明 <code>-device</code> 和 <code>-netdev</code> 的使用方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-riscv64 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -nographic -machine virt <span class="se">\
</span></span></span><span class="line"><span class="cl">  -smp <span class="s2">&#34;</span><span class="nv">$vcpu</span><span class="s2">&#34;</span> -m <span class="s2">&#34;</span><span class="nv">$memory</span><span class="s2">&#34;</span>G <span class="se">\
</span></span></span><span class="line"><span class="cl">  -bios <span class="s2">&#34;</span><span class="nv">$fw</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -drive <span class="nv">file</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$drive</span><span class="s2">&#34;</span>,format<span class="o">=</span>qcow2,id<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -object rng-random,filename<span class="o">=</span>/dev/urandom,id<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-vga <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-rng-device,rng<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-blk-device,drive<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,ifname<span class="o">=</span>tap0,script<span class="o">=</span>~/qemu-script/qemu-ifup,downscript<span class="o">=</span>no <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
</span></span></code></pre></div><p>其中这两个参数即实现了虚拟化网络：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,ifname<span class="o">=</span>tap0,script<span class="o">=</span>~/qemu-script/qemu-ifup,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-device virtio-net-device</code> 用于指定上述所说的前端驱动，也就是 Guest 中的实现，定义了名为 <code>virtio-net-device</code> 的网络设备，并将其连接到一个名为 <code>tapnet</code> 的网络设备上，指定它的 MAC 地址为 <code>e0:be:03:88:54:e8</code>。</p>
<p>第二个参数 <code>-netdev tap</code> 用于指定后端实现，使用<code>tap</code>方式，并且指定唯一 ID 为<code>tapnet</code>由<code>-device</code>参数中的子参数<code>netdev</code>使用。网卡名称为<code>tap0</code>并且执行脚本 <code>~/qemu-script/qemu-ifup</code>。</p>
<blockquote>
<p>-netdev 参数中 id 的使用
-netdev 参数中的 id 用于指定唯一的 ID，这个 ID 会被 <code>-device</code> 参数中的子参数 <code>netdev</code> 使用，这样 <code>-device</code> 参数就知道要将前端驱动连接到哪个后端实现上了。id 可以自定义任意唯一字符串如<code>-netdev tap,id=test</code>对应<code>-device virtio-net-device,netdev=test</code></p>
</blockquote>
<p>更多示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-pci,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,script<span class="o">=</span>no,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-device virtio-net-pci</code> 定义了名为 <code>virtio-net-pci</code> 的网络设备，并将其连接到一个名为 <code>tapnet</code> 的网络设备上，指定它的 MAC 地址为 <code>e0:be:03:88:54:e8</code>。</p>
<p>第二个参数，仔细观察会发现，我们没有定义链接到后端网卡的名称<code>ifname</code>，这是因为以<code>tap</code>模式启动 QEMU 时会自动创建<code>tap</code>设备，具体网卡名称根据当前宿主机的网卡情况而定，默认会创建一个名为<code>tap0</code>的网卡，如果启动了两个虚拟机，那么第二个虚拟机的网卡名称就是<code>tap1</code>，以此类推。</p>
<h3 id="区分-tap-模式与-bridge-模式">区分 tap 模式与 bridge 模式</h3>
<p>我们有时候会用以下的命令进行 QEMU 虚拟机桥接网络的配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>bridgenet,mac<span class="o">=</span>52:54:00:12:34:57 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev bridge,ifname<span class="o">=</span>br0,id<span class="o">=</span>bridgenet
</span></span></code></pre></div><p>这也能为我们创建一个桥接网络，这是因为它和 <code>-netdev tap</code> 的工作方式是一样的，只是 <code>-netdev bridge</code> 的简化写法，<code>qemu-bridge-helper</code> 在背后替我们做了 <code>tap</code> 设备创建以及将 <code>tap</code> 设备加入桥接口的所有事情。</p>
<h3 id="添加多张网卡">添加多张网卡</h3>
<p>如果了解上述内容，添加多张网卡就十分容易实现了，我们只需要再添加一对 <code>-device</code> 和 <code>-netdev</code> 参数即可，如下所示：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-riscv64 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -nographic -machine virt <span class="se">\
</span></span></span><span class="line"><span class="cl">  -smp <span class="s2">&#34;</span><span class="nv">$vcpu</span><span class="s2">&#34;</span> -m <span class="s2">&#34;</span><span class="nv">$memory</span><span class="s2">&#34;</span>G <span class="se">\
</span></span></span><span class="line"><span class="cl">  -bios <span class="s2">&#34;</span><span class="nv">$fw</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -drive <span class="nv">file</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$drive</span><span class="s2">&#34;</span>,format<span class="o">=</span>qcow2,id<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -object rng-random,filename<span class="o">=</span>/dev/urandom,id<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-vga <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-rng-device,rng<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-blk-device,drive<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet0,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet0,script<span class="o">=</span>/etc/qemu/qemu-ifup,downscript<span class="o">=</span>/etc/qemu/qemu-ifdown <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet1,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet1,script<span class="o">=</span>/etc/qemu/qemu-ifup,downscript<span class="o">=</span>/etc/qemu/qemu-ifdown <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
</span></span></code></pre></div><p>需要注意的是，我们需要为每个 <code>-device</code> 参数指定一个唯一的 ID，这个 ID 会被 <code>-netdev</code> 参数中的子参数 <code>netdev</code> 使用，这样 <code>-device</code> 参数就知道要将前端驱动连接到哪个后端实现上了。并且每个 <code>tap</code> 设备只能被一个虚拟机使用，所以每个虚拟机的 <code>tap</code> 设备名称不能相同。</p>
<p>登录虚拟机查看网卡信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ifconfig </span>
</span></span><span class="line"><span class="cl">eth0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.193.53  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::9e6:287b:30a2:574d  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether e0:be:03:88:54:e8  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">81</span>  bytes <span class="m">9871</span> <span class="o">(</span>9.6 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">19</span>  bytes <span class="m">1735</span> <span class="o">(</span>1.6 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">eth1: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.193.101  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::4fe0:9e1e:4681:52b7  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 80:d4:09:62:cd:3c  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">76</span>  bytes <span class="m">9471</span> <span class="o">(</span>9.2 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">15</span>  bytes <span class="m">1708</span> <span class="o">(</span>1.6 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">lo: <span class="nv">flags</span><span class="o">=</span>73&lt;UP,LOOPBACK,RUNNING&gt;  mtu <span class="m">65536</span>
</span></span><span class="line"><span class="cl">        inet 127.0.0.1  netmask 255.0.0.0
</span></span><span class="line"><span class="cl">        inet6 ::1  prefixlen <span class="m">128</span>  scopeid 0x10&lt;host&gt;
</span></span><span class="line"><span class="cl">        loop  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Local Loopback<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><h2 id="不同网络策略工作方式">不同网络策略工作方式</h2>
<ul>
<li>NAT 网络模式
<ul>
<li>NAT 网络以路由器的 NAT 功能为原理，允许虚拟机通过共享主机的 IP 地址访问互联网，但虚拟机之间不能直接通信。通过端口转发可以实现虚拟机之间的连接。</li>
</ul>
</li>
<li>桥接网络模式
<ul>
<li>桥接网络模式通过虚拟交换机连接虚拟机和主机，使得虚拟机可以通过局域网访问互联网，并允许虚拟机之间直接通信。</li>
</ul>
</li>
<li>内部网络模式
<ul>
<li>内部网络模式使得虚拟机可以创建一个完全隔离的网络，虚拟机之间可以直接通信，但无法访问互联网或外部网络。</li>
</ul>
</li>
<li>仅主机网络模式
<ul>
<li>仅主机网络模式允许虚拟机之间可以通信，并且与主机之间也可以通信，但无法访问互联网或外部网络。</li>
</ul>
</li>
</ul>
<table>
  <thead>
      <tr>
          <th></th>
          <th>VM &lt;&gt; VM</th>
          <th>VM → HOST</th>
          <th>HOST → VM</th>
          <th>VM → Internet</th>
          <th>Internet → VM</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>网络地址转换 NAT</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
      </tr>
      <tr>
          <td>NAT 网络</td>
          <td>√</td>
          <td>√</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
      </tr>
      <tr>
          <td>Bridged Adapter 桥接网卡</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
      </tr>
  </tbody>
</table>
<h2 id="tuntap-网络设备">TUN/TAP 网络设备</h2>
<p>TAP 属于 Linux 内核支持的一种虚拟化网络设备，还有 TUN 也属于这种设备，它们完全由软件模拟实现，TUN/TAP 负责在内核协议栈和用户进程之间传送协议数据单元。TUN 工作在网络层，而 TAP 工作在数据链路层，TUN 负责与应用程序交换 IP 数据包，而 TAP 与应用程序交换以太网帧。所以 TUN 经常涉及路由，而 TAP 常用于网络桥接。</p>
<h1 id="ssh-远程登录虚拟机">SSH 远程登录虚拟机</h1>
<p>宿主机任意下目录执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ ssh-keygen -t rsa
</span></span><span class="line"><span class="cl">Generating public/private rsa key pair.
</span></span><span class="line"><span class="cl">Enter file in which to save the key <span class="o">(</span>/home/user/.ssh/id_rsa<span class="o">)</span>: host2vm0_id_irsa
</span></span><span class="line"><span class="cl">Enter passphrase <span class="o">(</span>empty <span class="k">for</span> no passphrase<span class="o">)</span>: 
</span></span><span class="line"><span class="cl">Enter same passphrase again: 
</span></span><span class="line"><span class="cl">Your identification has been saved in host2vm0_id_irsa.
</span></span><span class="line"><span class="cl">Your public key has been saved in host2vm0_id_irsa.pub.
</span></span><span class="line"><span class="cl">The key fingerprint is:
</span></span><span class="line"><span class="cl">SHA256:OkWcw+R3x6Z2mzeYQuG033H3N9qIeym3TZKzz6YD8tQ user@ubuntu18
</span></span><span class="line"><span class="cl">The key<span class="err">&#39;</span>s randomart image is:
</span></span><span class="line"><span class="cl">+---<span class="o">[</span>RSA 2048<span class="o">]</span>----+
</span></span><span class="line"><span class="cl"><span class="p">|</span>        .        <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       <span class="o">=</span> .   .   <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>        B .o. +  <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       . oo.o+   <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>        S  ++ ..o<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       o ..+.E<span class="o">=</span><span class="nv">o</span><span class="o">=</span><span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>      o   +..B+<span class="o">=</span>+<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       .   <span class="nv">oo</span><span class="o">=</span>@o+<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>           <span class="nv">o</span><span class="o">=</span>**<span class="o">=</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl">+----<span class="o">[</span>SHA256<span class="o">]</span>-----+
</span></span></code></pre></div><p>一直回车确定，生成公私钥，保存在<code>~/.ssh</code>目录下。</p>
<blockquote>
<p>我在宿主机上生成的公私钥名称为，分别是<code>host2vm0_id_rsa</code>,<code>host2vm0_id_rsa.pub</code>方便我记忆。如果一直回车，那么生成的公私钥名称为<code>id_rsa</code>，<code>id_rsa.pub</code>。</p>
</blockquote>
<p>将公钥复制到虚拟机 <code>VM0</code> 上，以当前虚拟机 <code>VM0</code> 的 IP：<code>10.12.193.53</code> 为例。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ ssh-copy-id 10.12.193.53
</span></span><span class="line"><span class="cl"><span class="c1"># 输入密码</span>
</span></span><span class="line"><span class="cl">/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key<span class="o">(</span>s<span class="o">)</span>, to filter out any that are already installed
</span></span><span class="line"><span class="cl">/usr/bin/ssh-copy-id: INFO: <span class="m">1</span> key<span class="o">(</span>s<span class="o">)</span> remain to be installed -- <span class="k">if</span> you are prompted now it is to install the new keys
</span></span><span class="line"><span class="cl">user@10.12.193.53<span class="s1">&#39;s password: 
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">Number of key(s) added: 1
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">Now try logging into the machine, with:   &#34;ssh &#39;</span>10.12.193.53<span class="err">&#39;</span><span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">and check to make sure that only the key(s) you wanted were added.
</span></span></span></code></pre></div><p>然后就可以直接免密码登录了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">ssh user@10.12.193.53
</span></span></code></pre></div><h1 id="fixed-problems-ongoing">Fixed Problems (Ongoing)</h1>
<h2 id="cannot-ioctl-tunsetiff-tap0-device-or-resource-busy-errno16">cannot ioctl tunsetiff tap0 device or resource busy (errno=16)</h2>
<h2 id="failed-to-initialize-tap-device-operation-not-permitted">failed to initialize tap device: Operation not permitted</h2>
<p>同类型错误：failed to create TAP device: Operation not permitted。因为创建虚拟设备 <code>tap</code> 需要 <code>root</code> 权限，所以需要使用 <code>sudo</code> 命令。执行 QEMU 启动是需要添加 <code>sudo</code>。</p>
<h2 id="qemu-虚拟机启动后网卡处于-down-状态无法获取-ip">QEMU 虚拟机启动后网卡处于 DOWN 状态，无法获取 IP</h2>
<p>查看是否是 MAC 地址配置错误，使用下面命令检查：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig eth0 up
</span></span><span class="line"><span class="cl">SIOCSIFFLAGS: Cannot assign requested address
</span></span></code></pre></div><p>如果报错，参考下面章节<strong>SIOCSIFFLAGS: Cannot assign requested address</strong>解决方法进行解决。</p>
<h2 id="虚拟机可以-ping-通外网宿主机无法-ping-外网">虚拟机可以 ping 通外网，宿主机无法 ping 外网</h2>
<p>这种情况说明基本网络没有问题，只是 DNS 解析有问题，可以通过修改<code>/etc/resolv.conf</code>文件解决。</p>
<p>海宁 DNS 服务器地址：<code>10.12.2.21</code> 和 <code>10.12.2.22</code>，我的情况是只能 <code>ping 10.12.2.21</code>，可以选择自己能 <code>ping</code> 通的 DNS 服务器地址。如果无法 <code>ping</code> 通，说明问题不在这，需要自行解决。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 修改 DNS 服务器地址</span>
</span></span><span class="line"><span class="cl">sudo vim /etc/resolv.conf
</span></span><span class="line"><span class="cl"><span class="c1"># 添加以下内容</span>
</span></span><span class="line"><span class="cl">nameserver 10.12.2.21
</span></span><span class="line"><span class="cl">nameserver 10.12.2.22
</span></span></code></pre></div><h2 id="网络配置错误如何恢复配置之前的状态">网络配置错误，如何恢复配置之前的状态</h2>
<p>最简单的方式 - 重启，因为所有操作都是命令行配置，都是临时配置，可以直接重启解决。</p>
<p>既然有这一小节，说明肯定有时候不方便直接重启，那么就需要手动恢复配置之前的状态。但是能够恢复的<strong>前提是需要记得之前的网卡 IP 地址、子网掩码、网关、广播地址等信息</strong>。这些信息在局域网里，可能只有 IP 不同，其他信息如果没记住可以查看其他同事的网卡配置即可。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 将网桥绑定的网卡从网桥上移除</span>
</span></span><span class="line"><span class="cl">sudo brctl delif br0 enp2s0
</span></span><span class="line"><span class="cl">sudo brctl delif br0 tap0
</span></span><span class="line"><span class="cl"><span class="c1"># 配置宿主机网卡信息，必须一字不差，保持和之前一模一样才能恢复</span>
</span></span><span class="line"><span class="cl">sudo ip addr add 10.12.192.173/20 broadcast 10.12.207.255 dev enp2s0
</span></span><span class="line"><span class="cl"><span class="c1"># 必须设置网关</span>
</span></span><span class="line"><span class="cl">sudo ip route add default via 10.12.192.1 dev enp2s0
</span></span><span class="line"><span class="cl"><span class="c1"># 重启网络管理器</span>
</span></span><span class="line"><span class="cl">systemctl restart NetworkManager
</span></span></code></pre></div><h2 id="-netdev-tapidtapnetscriptqemu-scriptqemu-ifupnetwork-script-qemu-scriptqemu-ifup-failed-with-status-256">-netdev tap,id=tapnet,script=/qemu-script/qemu-ifup,:network script /qemu-script/qemu-ifup failed with status 256</h2>
<p>可能原因 1: <code>qemu-ifup</code> 脚本没有执行权限，需要添加执行权限。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod +x qemu-ifup
</span></span></code></pre></div><p>可能原因 2: <code>qemu-ifup</code> 路径不对，必须放到<code>/etc/qemu/</code>目录下。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /etc/qemu
</span></span><span class="line"><span class="cl">mv qemu-ifup /etc/qemu <span class="o">&amp;&amp;</span> mv qemu-ifdown /etc/qemu 
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifup
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifdown
</span></span></code></pre></div><h2 id="siocsifflags-cannot-assign-requested-address">SIOCSIFFLAGS: Cannot assign requested address</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig eth0 up
</span></span><span class="line"><span class="cl">SIOCSIFFLAGS: Cannot assign requested address
</span></span></code></pre></div><p>一般由于 MAC 地址配置错误导致，可以通过修改 MAC 地址为多播地址解决。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig enp2s0 hw ether 00:11:22:33:44:55
</span></span></code></pre></div><p>重启网卡</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig enp2s0 down
</span></span><span class="line"><span class="cl">sudo ifconfig enp2s0 up
</span></span></code></pre></div><p>MAC 地址的第一个字节中的最后一位（即第 7 位）用于标识该地址是单播，多播还是广播地址。如果这个位设置为 0，则表示这是一个单播地址；如果设置为 1，则表示这是一个多播或广播地址。</p>
<p>使用这种方法，我们可以确定上述每个 MAC 地址是否是单播地址：</p>
<ul>
<li><code>cd:c2:05:84:c8:2c</code> - 单播地址</li>
<li><code>13:7b:49:fc:a6:aa</code> - 单播地址</li>
<li><code>8f:aa:42:29:e8:68</code> - 单播地址</li>
</ul>
<p><code>00:11:22:33:44:55</code> 是多播地址。</p>
<h2 id="qemu--device-drive-with-0-bus0-unit0-exists">qemu -device drive with 0 bus=0 unit=0 exists</h2>
<p>这个错误通常意味着您尝试在 QEMU VM 中添加一个重复的设备。</p>
<p>如果您已经在 VM 中添加了驱动器，则可能会出现此问题。您可以检查是否存在两个具有相同 <code>bus</code> 和 <code>unit</code> 的设备（在此情况下，都是 0）。解决此问题的方法是删除重复设备或更改其配置以包括唯一的 <code>bus</code> 和 <code>unit</code>。</p>
<p>如果您没有意图添加重复的设备，在运行 QEMU 之前，您可能需要检查您的命令行，以确保正确设置了 <code>-drive</code> 选项。请注意，当使用 <code>-device</code> 添加设备时，您还应该避免使用 <code>-drive</code> 选项，因为它们可能引起冲突。</p>
<p>如果您需要进一步帮助，建议提供完整的 QEMU 命令和参数列表，以便更好地理解问题并提供更详细的建议。</p>
<h1 id="参考资料">参考资料</h1>
<ol>
<li><a href="https://tomwei7.com/2021/10/09/qemu-network-config/">QEMU 网络配置 // 围城</a></li>
<li><a href="https://www.junmajinlong.com/img/virtual/1594802457384.png">理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙</a></li>
<li><a href="https://wzt.ac.cn/2021/05/28/QEMU-networking/">QEMU 网络配置一把梭 | CataLpa&rsquo;s Site</a></li>
<li><a href="https://blog.csdn.net/u014022631/article/details/53411557">qemu 虚拟机与外部网络的通信 li_Jiejun 的博客-CSDN 博客</a></li>
<li><a href="https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html">安装 qemu-kvm 以及配置桥接网络</a></li>
<li><a href="https://blog.csdn.net/rikeyone/article/details/106767540">QEMU 中的网络虚拟化配置_程序猿 Ricky 的日常干货的博客-CSDN 博客</a></li>
<li><a href="https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/QEMU/">Nginx Directory</a></li>
<li><a href="https://www.cnblogs.com/huqingyu/archive/2005/04/03/131102.html">QEMU 网络配置 - 浙林龙哥 - 博客园</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/432022126">【qemu】qemu 网络配置 - 知乎</a></li>
<li><a href="https://tomwei7.com/2021/10/09/qemu-network-config/">QEMU 网络配置 // 围城</a></li>
<li><a href="https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html">安装 qemu-kvm 以及配置桥接网络</a></li>
<li><a href="https://blog.virt.ltd/blog/archives/37/">在 qemu 中使用桥接网络 - T^3 Blog</a></li>
<li><a href="http://wiki.yanick.site/wiki/os/qemu/">为 QEMU 配置网桥上网 | Yanick&rsquo;s Wiki</a></li>
<li><a href="https://www.junmajinlong.com/virtual/network/all_about_tun_tap/">理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙</a></li>
<li><a href="https://wzt.ac.cn/2021/05/28/QEMU-networking/">QEMU 网络配置一把梭 | CataLpa&rsquo;s Site</a></li>
</ol>
<h1 id="附录">附录</h1>
]]></content:encoded>
    </item>
    <item>
      <title>使用 Yadm 管理并同步配置文件 Dotfile</title>
      <link>https://lifeislife.cn/posts/%E4%BD%BF%E7%94%A8yadm%E7%AE%A1%E7%90%86%E5%B9%B6%E5%90%8C%E6%AD%A5%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6dotfile/</link>
      <pubDate>Sun, 30 Jul 2023 13:39:04 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E4%BD%BF%E7%94%A8yadm%E7%AE%A1%E7%90%86%E5%B9%B6%E5%90%8C%E6%AD%A5%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6dotfile/</guid>
      <description>&lt;p&gt;Dotfiles 就是我们在使用软件的时候，软件为了存储我们个人偏好设置而建立的一个以 &lt;code&gt;.&lt;/code&gt; 开头的文件。例如，vim 的配置文件就是 &lt;code&gt;.vimrc&lt;/code&gt;，zsh 的配置文件就是 &lt;code&gt;.zshrc&lt;/code&gt;。这些文件通常存储在用户的 home 目录中。但是，在不同的电脑上工作时，如果需要使用相同的配置，我们可以考虑使用版本控制工具来管理这些文件。或者在一台新电脑上想快速配置好环境，也可以使用版本控制工具来管理这些文件。Yadm 就可以帮助我们完成这些需求。&lt;/p&gt;
&lt;h1 id=&#34;安装-yadm&#34;&gt;安装 yadm&lt;/h1&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;p&gt;安装 &lt;code&gt;yadm&lt;/code&gt; 非常简单，只需在终端输入以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install yadm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;初始化-yadm-仓库&#34;&gt;初始化 yadm 仓库&lt;/h2&gt;
&lt;p&gt;创建一个新的 yadm 仓库很容易，只需在 home 目录中运行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在，yadm 已经创建了一个空白的 git 仓库。&lt;/p&gt;
&lt;p&gt;存储库位置&lt;code&gt;/home/nic/.local/share/yadm/&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;添加-dotfile-文件&#34;&gt;添加 dotfile 文件&lt;/h2&gt;
&lt;p&gt;要将现有的 dotfile 添加到 yadm 仓库中，请使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm add ~/.zshrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一旦您完成了对要添加的文件的更改并将它们添加到 yadm 仓库中，您需要提交它们。可以使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm commit -m &amp;#34;Add .zshrc file to yadm repository&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;建立远程仓库&#34;&gt;建立远程仓库&lt;/h2&gt;
&lt;p&gt;使用 yadm 还可以将 dotfile 文件同步到 GitHub 等 Git 托管服务中。&lt;/p&gt;
&lt;p&gt;登录 Github，创建一个新的仓库。例如，您可以创建一个名为 &lt;code&gt;dotfile&lt;/code&gt; 的仓库。现在，您需要将本地仓库与远程仓库连接起来。要将本地仓库连接到远程仓库，请使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm remote add origin https://github.com/[用户名]/dotfile.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在 yadm 已经连接到您在 Github 上创建的仓库。要将本地代码上传到远程仓库，请使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm push
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;使用多台电脑时如何同步配置&#34;&gt;使用多台电脑时如何同步配置&lt;/h1&gt;
&lt;p&gt;假设需要在多个计算机之间共享 &lt;code&gt;dotfile&lt;/code&gt; 文件。只需按照以下步骤即可：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在另一台计算机上安装 yadm 并初始化 yadm 仓库。&lt;/li&gt;
&lt;li&gt;将远程仓库克隆到该计算机的 yadm 仓库中：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm clone https://github.com/[用户名]/dotfile.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;此时，您的 dotfile 文件应在计算机上自动更新。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;执行完 clone 命令后实际上就是 yadm 会把远程仓库的文件都拷贝到本地。&lt;/p&gt;
&lt;h2 id=&#34;如果本地有修改和远程有冲突怎么办&#34;&gt;如果本地有修改和远程有冲突怎么办&lt;/h2&gt;
&lt;p&gt;如果本地有修改，远程也有修改，那么就会产生冲突。这时候需要先解决冲突，然后再提交。&lt;/p&gt;
&lt;h2 id=&#34;每台电脑的配置不一样怎么办&#34;&gt;每台电脑的配置不一样怎么办&lt;/h2&gt;
&lt;p&gt;比如我们会在&lt;code&gt;.zshrc&lt;/code&gt;中配置一些环境变量，但是每台电脑的环境变量可能不一样。这时候我们可以在&lt;code&gt;.zshrc&lt;/code&gt;中添加一些判断，比如我们可以简单的判断一下主机名，然后根据主机名来加载不同的配置。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$HOSTNAME&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;xxx&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# xxx的配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$HOSTNAME&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;yyy&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# yyy的配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但有一些配置文件可能不支持这样的添加语句，比如&lt;code&gt;.gitconfig&lt;/code&gt;，这时候我们可以使用 yadm 的 Alternate Files 功能解决。&lt;/p&gt;
&lt;p&gt;Alternate Files 是一个用于管理同一文件不同版本的功能，有时在不同的主机、操作系统和用户需要不同的文件。Alternate Files 允许使用同一个文件名字，在文件名后添加一个带有条件的后缀，例如##os.Linux,hostname.host1，class.work，yadm 会根据当前系统的特定条件自动选择适当的版本，并创建符号链接。如果没有符合条件的版本，它将选择默认版本。&lt;/p&gt;
&lt;p&gt;就以&lt;code&gt;.gitconfig&lt;/code&gt;配置文件为例，我们通常在公司的电脑和家里的电脑配置不太一样，比如公司使用下面的配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;user&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;email&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 公司邮箱
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 公司用户名
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在家里使用下面的配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;user&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;email&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 日常使用邮箱
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 日常使用用户名
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置文件不支持条件语句，所以无法通过直接在同一个配置文件里完成不同环境的配置。这就用到了 Alternate Files 功能，我们可以复制两个&lt;code&gt;.gitconfig&lt;/code&gt;文件后面添加一个条件后缀，比如&lt;code&gt;.gitconfig##class.work&lt;/code&gt;，然后在&lt;code&gt;.gitconfig##class.work&lt;/code&gt;文件中添加公司的配置，&lt;code&gt;.gitconfig&lt;/code&gt;文件中添加家里的配置。这样 yadm 就会根据当前的主机名自动选择合适的配置文件。并把这三个文件都加入到 yadm 仓库中。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm add ~/.gitconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm add ~/.gitconfig##class.work
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm add ~/.gitconfig##class.home
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那么 yadm 是如何实现不同环境的切换呢？我们还需要进一步配置，有注意到我们的配置文件名中的&lt;code&gt;class&lt;/code&gt;属性吗，这是 yadm 支持的条件属性之一。我们可以通过&lt;code&gt;yadm config&lt;/code&gt;命令来配置当前电脑的属性。比如我们配置公司电脑的&lt;code&gt;class&lt;/code&gt;属性为&lt;code&gt;work&lt;/code&gt;，家里的电脑的&lt;code&gt;class&lt;/code&gt;属性为&lt;code&gt;home&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yadm config local.class work
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们执行完此命令后，yadm 会自动为&lt;code&gt;.gitconfig&lt;/code&gt;文件添加一个软链接，链接到&lt;code&gt;.gitconfig##class.work&lt;/code&gt;文件。这样我们就可以在公司电脑上使用公司的配置了。如果我们在家里的电脑上执行&lt;code&gt;yadm config local.class home&lt;/code&gt;，那么 yadm 会自动为&lt;code&gt;.gitconfig&lt;/code&gt;文件添加一个软链接，链接到&lt;code&gt;.gitconfig##class.home&lt;/code&gt;文件。这样我们就可以在家里的电脑上使用家里的配置了。&lt;/p&gt;
&lt;p&gt;yadm 支持以下这些条件：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;属性&lt;/th&gt;
          &lt;th&gt;意义&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;arch, a&lt;/td&gt;
          &lt;td&gt;如果值匹配架构则有效。通过运行 uname -m 计算架构。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;class, c&lt;/td&gt;
          &lt;td&gt;如果值匹配 local.class 配置，则有效。必须使用“yadm config local.class &lt;class&gt;”手动设置 Class。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;default&lt;/td&gt;
          &lt;td&gt;当没有其他备选项有效时有效。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;distro, d&lt;/td&gt;
          &lt;td&gt;如果值与发行版匹配，则有效。通过运行 lsb_release -si或检查/etc/os-release来计算分布。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;distro_family, f&lt;/td&gt;
          &lt;td&gt;如果值匹配发行版系列，则有效。通过检查/etc/os-release 中的 ID_LIKE 行计算发行版系列。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;extension, e&lt;/td&gt;
          &lt;td&gt;一种特殊的“条件”，不影响选择过程。它的目的是允许备选文件以特定扩展名结尾，例如使编辑器正确突出显示内容。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;hostname, h&lt;/td&gt;
          &lt;td&gt;如果值匹配短主机名，则有效。通过运行 uname -n，并去除任何域来计算主机名。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;os，o&lt;/td&gt;
          &lt;td&gt;如果值与操作系统匹配，则有效。通过运行 uname -s 计算 OS。*&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;template, t&lt;/td&gt;
          &lt;td&gt;当值与支持的模板处理器匹配时有效。有关更多详细信息，请参见模板部分。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;user, u&lt;/td&gt;
          &lt;td&gt;如果值匹配当前用户，则有效。通过运行 id -u -n 计算当前用户。&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;再举个例子，上面用的 class 条件是必须通过&lt;code&gt;yadm config local.class &amp;lt;class&amp;gt;&lt;/code&gt;手动设置的。但是有一些条件不需要手动设置 yadm 可以自动识别。比如 os 属性。我们将&lt;code&gt;.zshrc&lt;/code&gt;文件复制两份份，命名为&lt;code&gt;.zshrc##os.Linux&lt;/code&gt;和&lt;code&gt;.zshrc##os.Darwin&lt;/code&gt;，然后在&lt;code&gt;.zshrc##os.Linux&lt;/code&gt;文件中添加一些 Linux 系统的配置，&lt;code&gt;.zshrc##os.Darwin&lt;/code&gt;文件中添加一些 Mac 系统的配置。并把这三个文件都加入到 &lt;code&gt;yadm&lt;/code&gt; 仓库中。这样 &lt;code&gt;yadm&lt;/code&gt; 就会根据当前的系统自动选择合适的配置文件。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>Dotfiles 就是我们在使用软件的时候，软件为了存储我们个人偏好设置而建立的一个以 <code>.</code> 开头的文件。例如，vim 的配置文件就是 <code>.vimrc</code>，zsh 的配置文件就是 <code>.zshrc</code>。这些文件通常存储在用户的 home 目录中。但是，在不同的电脑上工作时，如果需要使用相同的配置，我们可以考虑使用版本控制工具来管理这些文件。或者在一台新电脑上想快速配置好环境，也可以使用版本控制工具来管理这些文件。Yadm 就可以帮助我们完成这些需求。</p>
<h1 id="安装-yadm">安装 yadm</h1>
<h2 id="安装">安装</h2>
<p>安装 <code>yadm</code> 非常简单，只需在终端输入以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sudo apt-get install yadm
</span></span></code></pre></div><h2 id="初始化-yadm-仓库">初始化 yadm 仓库</h2>
<p>创建一个新的 yadm 仓库很容易，只需在 home 目录中运行以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">yadm init
</span></span></code></pre></div><p>现在，yadm 已经创建了一个空白的 git 仓库。</p>
<p>存储库位置<code>/home/nic/.local/share/yadm/</code></p>
<h2 id="添加-dotfile-文件">添加 dotfile 文件</h2>
<p>要将现有的 dotfile 添加到 yadm 仓库中，请使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">yadm add ~/.zshrc
</span></span></code></pre></div><p>一旦您完成了对要添加的文件的更改并将它们添加到 yadm 仓库中，您需要提交它们。可以使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">yadm commit -m &#34;Add .zshrc file to yadm repository&#34;
</span></span></code></pre></div><h2 id="建立远程仓库">建立远程仓库</h2>
<p>使用 yadm 还可以将 dotfile 文件同步到 GitHub 等 Git 托管服务中。</p>
<p>登录 Github，创建一个新的仓库。例如，您可以创建一个名为 <code>dotfile</code> 的仓库。现在，您需要将本地仓库与远程仓库连接起来。要将本地仓库连接到远程仓库，请使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">yadm remote add origin https://github.com/[用户名]/dotfile.git
</span></span></code></pre></div><p>现在 yadm 已经连接到您在 Github 上创建的仓库。要将本地代码上传到远程仓库，请使用以下命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">yadm push
</span></span></code></pre></div><h1 id="使用多台电脑时如何同步配置">使用多台电脑时如何同步配置</h1>
<p>假设需要在多个计算机之间共享 <code>dotfile</code> 文件。只需按照以下步骤即可：</p>
<ol>
<li>在另一台计算机上安装 yadm 并初始化 yadm 仓库。</li>
<li>将远程仓库克隆到该计算机的 yadm 仓库中：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">yadm clone https://github.com/[用户名]/dotfile.git
</span></span></code></pre></div><ol start="3">
<li>此时，您的 dotfile 文件应在计算机上自动更新。</li>
</ol>
<p>执行完 clone 命令后实际上就是 yadm 会把远程仓库的文件都拷贝到本地。</p>
<h2 id="如果本地有修改和远程有冲突怎么办">如果本地有修改和远程有冲突怎么办</h2>
<p>如果本地有修改，远程也有修改，那么就会产生冲突。这时候需要先解决冲突，然后再提交。</p>
<h2 id="每台电脑的配置不一样怎么办">每台电脑的配置不一样怎么办</h2>
<p>比如我们会在<code>.zshrc</code>中配置一些环境变量，但是每台电脑的环境变量可能不一样。这时候我们可以在<code>.zshrc</code>中添加一些判断，比如我们可以简单的判断一下主机名，然后根据主机名来加载不同的配置。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="nv">$HOSTNAME</span> <span class="o">=</span> <span class="s2">&#34;xxx&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># xxx的配置</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="o">[</span> <span class="nv">$HOSTNAME</span> <span class="o">=</span> <span class="s2">&#34;yyy&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># yyy的配置</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><p>但有一些配置文件可能不支持这样的添加语句，比如<code>.gitconfig</code>，这时候我们可以使用 yadm 的 Alternate Files 功能解决。</p>
<p>Alternate Files 是一个用于管理同一文件不同版本的功能，有时在不同的主机、操作系统和用户需要不同的文件。Alternate Files 允许使用同一个文件名字，在文件名后添加一个带有条件的后缀，例如##os.Linux,hostname.host1，class.work，yadm 会根据当前系统的特定条件自动选择适当的版本，并创建符号链接。如果没有符合条件的版本，它将选择默认版本。</p>
<p>就以<code>.gitconfig</code>配置文件为例，我们通常在公司的电脑和家里的电脑配置不太一样，比如公司使用下面的配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>user<span class="o">]</span>
</span></span><span class="line"><span class="cl">	<span class="nv">email</span> <span class="o">=</span> 公司邮箱
</span></span><span class="line"><span class="cl">	<span class="nv">name</span> <span class="o">=</span> 公司用户名
</span></span></code></pre></div><p>在家里使用下面的配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>user<span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="nv">email</span> <span class="o">=</span> 日常使用邮箱
</span></span><span class="line"><span class="cl">    <span class="nv">name</span> <span class="o">=</span> 日常使用用户名
</span></span></code></pre></div><p>配置文件不支持条件语句，所以无法通过直接在同一个配置文件里完成不同环境的配置。这就用到了 Alternate Files 功能，我们可以复制两个<code>.gitconfig</code>文件后面添加一个条件后缀，比如<code>.gitconfig##class.work</code>，然后在<code>.gitconfig##class.work</code>文件中添加公司的配置，<code>.gitconfig</code>文件中添加家里的配置。这样 yadm 就会根据当前的主机名自动选择合适的配置文件。并把这三个文件都加入到 yadm 仓库中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yadm add ~/.gitconfig
</span></span><span class="line"><span class="cl">yadm add ~/.gitconfig##class.work
</span></span><span class="line"><span class="cl">yadm add ~/.gitconfig##class.home
</span></span></code></pre></div><p>那么 yadm 是如何实现不同环境的切换呢？我们还需要进一步配置，有注意到我们的配置文件名中的<code>class</code>属性吗，这是 yadm 支持的条件属性之一。我们可以通过<code>yadm config</code>命令来配置当前电脑的属性。比如我们配置公司电脑的<code>class</code>属性为<code>work</code>，家里的电脑的<code>class</code>属性为<code>home</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yadm config local.class work
</span></span></code></pre></div><p>我们执行完此命令后，yadm 会自动为<code>.gitconfig</code>文件添加一个软链接，链接到<code>.gitconfig##class.work</code>文件。这样我们就可以在公司电脑上使用公司的配置了。如果我们在家里的电脑上执行<code>yadm config local.class home</code>，那么 yadm 会自动为<code>.gitconfig</code>文件添加一个软链接，链接到<code>.gitconfig##class.home</code>文件。这样我们就可以在家里的电脑上使用家里的配置了。</p>
<p>yadm 支持以下这些条件：</p>
<table>
  <thead>
      <tr>
          <th>属性</th>
          <th>意义</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>arch, a</td>
          <td>如果值匹配架构则有效。通过运行 uname -m 计算架构。</td>
      </tr>
      <tr>
          <td>class, c</td>
          <td>如果值匹配 local.class 配置，则有效。必须使用“yadm config local.class <class>”手动设置 Class。</td>
      </tr>
      <tr>
          <td>default</td>
          <td>当没有其他备选项有效时有效。</td>
      </tr>
      <tr>
          <td>distro, d</td>
          <td>如果值与发行版匹配，则有效。通过运行 lsb_release -si或检查/etc/os-release来计算分布。</td>
      </tr>
      <tr>
          <td>distro_family, f</td>
          <td>如果值匹配发行版系列，则有效。通过检查/etc/os-release 中的 ID_LIKE 行计算发行版系列。</td>
      </tr>
      <tr>
          <td>extension, e</td>
          <td>一种特殊的“条件”，不影响选择过程。它的目的是允许备选文件以特定扩展名结尾，例如使编辑器正确突出显示内容。</td>
      </tr>
      <tr>
          <td>hostname, h</td>
          <td>如果值匹配短主机名，则有效。通过运行 uname -n，并去除任何域来计算主机名。</td>
      </tr>
      <tr>
          <td>os，o</td>
          <td>如果值与操作系统匹配，则有效。通过运行 uname -s 计算 OS。*</td>
      </tr>
      <tr>
          <td>template, t</td>
          <td>当值与支持的模板处理器匹配时有效。有关更多详细信息，请参见模板部分。</td>
      </tr>
      <tr>
          <td>user, u</td>
          <td>如果值匹配当前用户，则有效。通过运行 id -u -n 计算当前用户。</td>
      </tr>
  </tbody>
</table>
<p>再举个例子，上面用的 class 条件是必须通过<code>yadm config local.class &lt;class&gt;</code>手动设置的。但是有一些条件不需要手动设置 yadm 可以自动识别。比如 os 属性。我们将<code>.zshrc</code>文件复制两份份，命名为<code>.zshrc##os.Linux</code>和<code>.zshrc##os.Darwin</code>，然后在<code>.zshrc##os.Linux</code>文件中添加一些 Linux 系统的配置，<code>.zshrc##os.Darwin</code>文件中添加一些 Mac 系统的配置。并把这三个文件都加入到 <code>yadm</code> 仓库中。这样 <code>yadm</code> 就会根据当前的系统自动选择合适的配置文件。</p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU启动RISC-V架构OpenEuler并配置OSC环境</title>
      <link>https://lifeislife.cn/posts/qemu%E5%90%AF%E5%8A%A8risc-v%E6%9E%B6%E6%9E%84openeuler%E5%B9%B6%E9%85%8D%E7%BD%AEosc%E7%8E%AF%E5%A2%83/</link>
      <pubDate>Sun, 23 Jul 2023 19:28:29 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E5%90%AF%E5%8A%A8risc-v%E6%9E%B6%E6%9E%84openeuler%E5%B9%B6%E9%85%8D%E7%BD%AEosc%E7%8E%AF%E5%A2%83/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;基于Ubuntu 18.04，QEMU 8.0.2，OpenEuler 22.09&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;安装qemu&#34;&gt;安装QEMU&lt;/h1&gt;
&lt;h2 id=&#34;安装基础编译工具&#34;&gt;安装基础编译工具&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install build-essential autoconf automake autotools-dev pkg-config bc curl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 gawk git bison flex texinfo gperf libtool patchutils mingw-w64 libmpc-dev &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 libmpfr-dev libgmp-dev libexpat-dev libfdt-dev zlib1g-dev libglib2.0-dev &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 libpixman-1-dev libncurses5-dev libncursesw5-dev meson libvirglrenderer-dev libsdl2-dev  -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo add-apt-repository ppa:deadsnakes/ppa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install python3.8 python3-pip  -y
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install -f
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pip3 install meson
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;下载qemu&#34;&gt;下载QEMU&lt;/h2&gt;
&lt;p&gt;建立文件夹用于编译：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p qemu-build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;建立文件夹用于安装：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p /home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可登录&lt;a href=&#34;https://www.qemu.org/download/&#34;&gt;官网&lt;/a&gt;将版本号换成最新版本即可：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget  &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://download.qemu.org/qemu-8.0.2.tar.xz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xf qemu-8.0.2.tar.xz --strip-components&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;安装qemu-1&#34;&gt;安装QEMU&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./configure --target-list&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --enable-kvm --enable-sdl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make install -j &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置环境变量&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;export QEMU_HOME=/home/user/program/riscv64-qemu&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;export PATH=$QEMU_HOME/bin:$PATH&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;下载-openeuler-risc-v-系统镜像&#34;&gt;下载 OpenEuler RISC-V 系统镜像&lt;/h1&gt;
&lt;p&gt;建立目录：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p /home/user/openeuler
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;根据自己的用户名修改user&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下载&lt;a href=&#34;https://repo.openeuler.org/openEuler-preview/RISC-V/openEuler-22.09-riscv64/QEMU/&#34;&gt;OpenEuler 22.09版本&lt;/a&gt;，下载目录下所有文件/home/user/openeuler。如需下载其他版本请进入其他目录选择下载即可。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;也可以根据自己的情况进入&lt;a href=&#34;https://www.openeuler.org/zh/mirror/list/&#34;&gt;镜像站列表&lt;/a&gt;选择下载速度更快的镜像站下载&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;最新的23.03版本需要在&lt;a href=&#34;https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-23.03-V1-riscv64/&#34;&gt;中科院镜像站&lt;/a&gt;下载&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;文件说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fw_payload_oe_qemuvirt.elf&lt;/code&gt;: 利用 openSBI 将 kernel-5.10 的 image 作为 payload 所制作的 QEMU 启动所需文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openEuler-22.09-qemu-xfce.qcow2.tar.zst&lt;/code&gt;: openEuler RISC-V QEMU GUI 镜像压缩包&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preview_start_vm_xfce.sh&lt;/code&gt;: GUI 虚拟机启动脚本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openeuler-22.09-qemu.qcow2.tar.zst&lt;/code&gt;: openEuler RISC-V QEMU headless 镜像压缩包&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preview_start_vm.sh&lt;/code&gt;: headless 虚拟机启动脚本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解压：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; sudo apt-get install zstd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; tar -I &lt;span class=&#34;s1&#34;&gt;&amp;#39;zstdmt&amp;#39;&lt;/span&gt; -xvf openEuler-22.09-riscv64-qemu.qcow2.tar.zst
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行启动脚本&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x preview_start_vm.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bash preview_start_vm.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;登录系统&#34;&gt;登录系统&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;用户名: &lt;code&gt;root&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;默认密码: &lt;code&gt;openEuler12#$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openEuler 22.09
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Kernel 5.10.0 on an riscv64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4penEuler-riscv6
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; login: openEuler 22.09
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Kernel 5.10.0 on an riscv64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openEuler-riscv64 login: root
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Password: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Welcome to 5.10.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;System information as of time:   Mon Jul  &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; 07:52:19 PM CST &lt;span class=&#34;m&#34;&gt;2023&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;System load:   0.17
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Processes:   &lt;span class=&#34;m&#34;&gt;117&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Memory used:   .6%
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Swap used:   0.0%
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Usage On:   6%
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Users online:   &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pwd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;远程登录系统&#34;&gt;远程登录系统&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh -p &lt;span class=&#34;m&#34;&gt;12055&lt;/span&gt; root@localhost
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;配置系统&#34;&gt;配置系统&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;以下操作均在root用户下执行，如果切换了用户会有提示。因为系统初始状态没有普通用户，也没有sudo，所以需要使用root完成一些基础配置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;修改root密码&#34;&gt;修改root密码&lt;/h2&gt;
&lt;p&gt;原密码太复杂，修改简单密码&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;passwd root
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入两次密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;添加普通用户&#34;&gt;添加普通用户&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加用户 user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;useradd -s /bin/bash -d /home/user -m user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;passwd user
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入两次密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加管理员权限&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;usermod -aG wheel user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;修改时间&#34;&gt;修改时间&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NTP=ntp.aliyun.com&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/systemd/timesyncd.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart systemd-timesyncd.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看&lt;code&gt;timesyncd&lt;/code&gt;运行状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl status systemd-timesyncd.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;date&lt;/code&gt;命令可查看当前系统时间。验证是否配置成功。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;时间务必正确设置&lt;/strong&gt;，错误的时间会影响诸如https的TLS认证等过程。&lt;/p&gt;
&lt;h2 id=&#34;配置dns&#34;&gt;配置DNS&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 119.29.29.29
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;配置软件包源&#34;&gt;配置软件包源&lt;/h2&gt;
&lt;p&gt;配置文件为 /etc/yum.repos.d/openEuler.repo 下&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv /etc/yum.repos.d/openEuler.repo  /etc/yum.repos.d/openEuler.repo.bk &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/yum.repos.d/openEuler.repo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;# just for test
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;[mainline]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;name=mainline
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;gpgcheck=0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;# just for test
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;[epol]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;name=epol
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;gpgcheck=0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;[extra]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;name=extra
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/extra/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;gpgcheck=0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;EOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;需要注意的是，因为OpenEuler还在快速发展中，镜像地址可能会发生变化，所以需确认地址是否能够正常访问，如无法访问会导致404错误&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[repoid]中的repoid为软件仓库（repository）的ID号，所有.repo配置文件中的各repoid不能重复，必须唯一。示例中repoid设置为base。
name为软件仓库描述的字符串。
baseurl为软件仓库的地址。
enabled为是否启用该软件源仓库，可选值为1和0。默认值为1，表示启用该软件源仓库。
gpgcheck可设置为1或0，1表示进行gpg（GNU Private Guard）校验，0表示不进行gpg校验，gpgcheck可以确定rpm包的来源是有效和安全的。
gpgkey为验证签名用的公钥。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;磁盘扩容&#34;&gt;磁盘扩容&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在宿主机上安装 &lt;code&gt;qemu-img&lt;/code&gt; 工具:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install qemu-utils
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;在 openEuler RISC-V 虚拟机上安装 &lt;code&gt;growpart&lt;/code&gt; 工具:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dnf install cloud-utils-growpart
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;关闭QEMU虚拟机&lt;/li&gt;
&lt;li&gt;把 qcow2 文件的容量加200GB：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ qemu-img resize *.qcow2 +200G
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Image resized.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ qemu-img info *.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;image: openEuler-preview.riscv64.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;file format: qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;virtual size: &lt;span class=&#34;m&#34;&gt;220&lt;/span&gt; GiB 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;disk size: 9.58 GiB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cluster_size: &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Format specific information:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    compat: 1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    compression type: zlib
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    lazy refcounts: &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    refcount bits: &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    corrupt: &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    extended l2: &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;QEMU 启动 openEuler RISC-V。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;启动以后，我们先看看分区情况：可以看到根目录对应的分区只使用了 10G。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-RISCV-rare ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# lsblk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vda    254:0    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  220G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─vda1 254:1    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;   10G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;扩展分区 &lt;code&gt;vda1&lt;/code&gt;，执行&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;growpart /dev/vda1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行 &lt;code&gt;lsblk&lt;/code&gt; 可以看到 / 所在的 &lt;code&gt;vda1&lt;/code&gt; 分区已经扩展到了预期大小&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-RISCV-rare ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# growpart /dev/vda 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CHANGED: &lt;span class=&#34;nv&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2048&lt;/span&gt; old: &lt;span class=&#34;nv&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20969472&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20971520&lt;/span&gt; new: &lt;span class=&#34;nv&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;419428319&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;419430367&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-RISCV-rare ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# lsblk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vda    254:0    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  220G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─vda1 254:1    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  220G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;扩展文件系统：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;resize2fs /dev/vda1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;bug&#34;&gt;BUG&lt;/h1&gt;
&lt;h2 id=&#34;network-backend-user-is-not-compiled-into-this-binary&#34;&gt;network backend ‘user‘ is not compiled into this binary&lt;/h2&gt;
&lt;p&gt;git clone &lt;a href=&#34;https://gitlab.freedesktop.org/slirp/libslirp.git&#34;&gt;https://gitlab.freedesktop.org/slirp/libslirp.git&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb&#34;&gt;http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install libslirp-dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重新编译QEMU：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./configure --target-list&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --enable-kvm --enable-sdl --enable-slirp&lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make install -j &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;参考文档&#34;&gt;参考文档&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/openeuler-mirror/RISC-V/blob/master/doc/tutorials/vm-qemu-oErv.md&#34;&gt;RISC-V/doc/tutorials/vm-qemu-oErv.md at master · openeuler-mirror/RISC-V · GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.openeuler.org/whitepaper/openEuler-whitepaper-2209.pdf&#34;&gt;openEuler 22.09技术白皮书&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<blockquote>
<p>基于Ubuntu 18.04，QEMU 8.0.2，OpenEuler 22.09</p>
</blockquote>
<h1 id="安装qemu">安装QEMU</h1>
<h2 id="安装基础编译工具">安装基础编译工具</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt install build-essential autoconf automake autotools-dev pkg-config bc curl <span class="se">\
</span></span></span><span class="line"><span class="cl">                 gawk git bison flex texinfo gperf libtool patchutils mingw-w64 libmpc-dev <span class="se">\
</span></span></span><span class="line"><span class="cl">                 libmpfr-dev libgmp-dev libexpat-dev libfdt-dev zlib1g-dev libglib2.0-dev <span class="se">\
</span></span></span><span class="line"><span class="cl">                 libpixman-1-dev libncurses5-dev libncursesw5-dev meson libvirglrenderer-dev libsdl2-dev  -y
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo add-apt-repository ppa:deadsnakes/ppa
</span></span><span class="line"><span class="cl">sudo apt install python3.8 python3-pip  -y
</span></span><span class="line"><span class="cl">sudo apt install -f
</span></span><span class="line"><span class="cl">pip3 install meson
</span></span></code></pre></div><h2 id="下载qemu">下载QEMU</h2>
<p>建立文件夹用于编译：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">&amp;&amp;</span> mkdir -p qemu-build
</span></span></code></pre></div><p>建立文件夹用于安装：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">&amp;&amp;</span> mkdir -p /home/user/program/riscv64-qemu
</span></span></code></pre></div><p>可登录<a href="https://www.qemu.org/download/">官网</a>将版本号换成最新版本即可：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> wget  <span class="s2">&#34;https://download.qemu.org/qemu-8.0.2.tar.xz&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">tar -xf qemu-8.0.2.tar.xz --strip-components<span class="o">=</span><span class="m">1</span> 
</span></span></code></pre></div><h2 id="安装qemu-1">安装QEMU</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> ./configure --target-list<span class="o">=</span>riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu <span class="se">\
</span></span></span><span class="line"><span class="cl">               --enable-kvm --enable-sdl <span class="se">\
</span></span></span><span class="line"><span class="cl">               --prefix<span class="o">=</span>/home/user/program/riscv64-qemu
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">make install -j <span class="k">$(</span>nproc<span class="k">)</span>
</span></span></code></pre></div><p>配置环境变量</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;export QEMU_HOME=/home/user/program/riscv64-qemu&#39;</span> &gt;&gt; ~/.bashrc <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s1">&#39;export PATH=$QEMU_HOME/bin:$PATH&#39;</span> &gt;&gt; ~/.bashrc
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">source</span> ~/.bashrc
</span></span></code></pre></div><h1 id="下载-openeuler-risc-v-系统镜像">下载 OpenEuler RISC-V 系统镜像</h1>
<p>建立目录：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">&amp;&amp;</span> mkdir -p /home/user/openeuler
</span></span></code></pre></div><blockquote>
<p>根据自己的用户名修改user</p>
</blockquote>
<p>下载<a href="https://repo.openeuler.org/openEuler-preview/RISC-V/openEuler-22.09-riscv64/QEMU/">OpenEuler 22.09版本</a>，下载目录下所有文件/home/user/openeuler。如需下载其他版本请进入其他目录选择下载即可。</p>
<blockquote>
<p>也可以根据自己的情况进入<a href="https://www.openeuler.org/zh/mirror/list/">镜像站列表</a>选择下载速度更快的镜像站下载</p>
</blockquote>
<blockquote>
<p>最新的23.03版本需要在<a href="https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-23.03-V1-riscv64/">中科院镜像站</a>下载</p>
</blockquote>
<p>文件说明：</p>
<ul>
<li><code>fw_payload_oe_qemuvirt.elf</code>: 利用 openSBI 将 kernel-5.10 的 image 作为 payload 所制作的 QEMU 启动所需文件</li>
<li><code>openEuler-22.09-qemu-xfce.qcow2.tar.zst</code>: openEuler RISC-V QEMU GUI 镜像压缩包</li>
<li><code>preview_start_vm_xfce.sh</code>: GUI 虚拟机启动脚本</li>
<li><code>openeuler-22.09-qemu.qcow2.tar.zst</code>: openEuler RISC-V QEMU headless 镜像压缩包</li>
<li><code>preview_start_vm.sh</code>: headless 虚拟机启动脚本</li>
</ul>
<p>解压：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"> sudo apt-get install zstd
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"> tar -I <span class="s1">&#39;zstdmt&#39;</span> -xvf openEuler-22.09-riscv64-qemu.qcow2.tar.zst
</span></span></code></pre></div><p>执行启动脚本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">chmod +x preview_start_vm.sh
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">bash preview_start_vm.sh
</span></span></code></pre></div><h1 id="登录系统">登录系统</h1>
<ul>
<li>用户名: <code>root</code></li>
<li>默认密码: <code>openEuler12#$</code></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">openEuler 22.09
</span></span><span class="line"><span class="cl">Kernel 5.10.0 on an riscv64
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">4penEuler-riscv6
</span></span><span class="line"><span class="cl"> login: openEuler 22.09
</span></span><span class="line"><span class="cl">Kernel 5.10.0 on an riscv64
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">openEuler-riscv64 login: root
</span></span><span class="line"><span class="cl">Password: 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Welcome to 5.10.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">System information as of time:   Mon Jul  <span class="m">3</span> 07:52:19 PM CST <span class="m">2023</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">System load:   0.17
</span></span><span class="line"><span class="cl">Processes:   <span class="m">117</span>
</span></span><span class="line"><span class="cl">Memory used:   .6%
</span></span><span class="line"><span class="cl">Swap used:   0.0%
</span></span><span class="line"><span class="cl">Usage On:   6%
</span></span><span class="line"><span class="cl">Users online:   <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ls</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># pwd</span>
</span></span></code></pre></div><h3 id="远程登录系统">远程登录系统</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">ssh -p <span class="m">12055</span> root@localhost
</span></span></code></pre></div><h1 id="配置系统">配置系统</h1>
<blockquote>
<p>以下操作均在root用户下执行，如果切换了用户会有提示。因为系统初始状态没有普通用户，也没有sudo，所以需要使用root完成一些基础配置。</p>
</blockquote>
<h2 id="修改root密码">修改root密码</h2>
<p>原密码太复杂，修改简单密码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">passwd root
</span></span><span class="line"><span class="cl"><span class="c1"># 输入两次密码</span>
</span></span></code></pre></div><h2 id="添加普通用户">添加普通用户</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 添加用户 user</span>
</span></span><span class="line"><span class="cl">useradd -s /bin/bash -d /home/user -m user
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">passwd user
</span></span><span class="line"><span class="cl"><span class="c1"># 输入两次密码</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 添加管理员权限</span>
</span></span><span class="line"><span class="cl">usermod -aG wheel user
</span></span></code></pre></div><h2 id="修改时间">修改时间</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;NTP=ntp.aliyun.com&#34;</span> &gt;&gt; /etc/systemd/timesyncd.conf
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">systemctl restart systemd-timesyncd.service
</span></span></code></pre></div><p>查看<code>timesyncd</code>运行状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">systemctl status systemd-timesyncd.service
</span></span></code></pre></div><p><code>date</code>命令可查看当前系统时间。验证是否配置成功。</p>
<p><strong>时间务必正确设置</strong>，错误的时间会影响诸如https的TLS认证等过程。</p>
<h2 id="配置dns">配置DNS</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">vim /etc/resolv.conf
</span></span><span class="line"><span class="cl">nameserver 119.29.29.29
</span></span></code></pre></div><h2 id="配置软件包源">配置软件包源</h2>
<p>配置文件为 /etc/yum.repos.d/openEuler.repo 下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">mv /etc/yum.repos.d/openEuler.repo  /etc/yum.repos.d/openEuler.repo.bk <span class="o">&amp;&amp;</span> sudo bash -c <span class="s2">&#34;cat &lt;&lt; EOF &gt; /etc/yum.repos.d/openEuler.repo
</span></span></span><span class="line"><span class="cl"><span class="s2"># just for test
</span></span></span><span class="line"><span class="cl"><span class="s2">[mainline]
</span></span></span><span class="line"><span class="cl"><span class="s2">name=mainline
</span></span></span><span class="line"><span class="cl"><span class="s2">baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
</span></span></span><span class="line"><span class="cl"><span class="s2">enabled=1
</span></span></span><span class="line"><span class="cl"><span class="s2">gpgcheck=0
</span></span></span><span class="line"><span class="cl"><span class="s2"># just for test
</span></span></span><span class="line"><span class="cl"><span class="s2">[epol]
</span></span></span><span class="line"><span class="cl"><span class="s2">name=epol
</span></span></span><span class="line"><span class="cl"><span class="s2">baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
</span></span></span><span class="line"><span class="cl"><span class="s2">enabled=1
</span></span></span><span class="line"><span class="cl"><span class="s2">gpgcheck=0
</span></span></span><span class="line"><span class="cl"><span class="s2">[extra]
</span></span></span><span class="line"><span class="cl"><span class="s2">name=extra
</span></span></span><span class="line"><span class="cl"><span class="s2">baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/extra/
</span></span></span><span class="line"><span class="cl"><span class="s2">enabled=1
</span></span></span><span class="line"><span class="cl"><span class="s2">gpgcheck=0
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF&#34;</span>
</span></span></code></pre></div><blockquote>
<p>需要注意的是，因为OpenEuler还在快速发展中，镜像地址可能会发生变化，所以需确认地址是否能够正常访问，如无法访问会导致404错误</p>
</blockquote>
<blockquote>
<p>[repoid]中的repoid为软件仓库（repository）的ID号，所有.repo配置文件中的各repoid不能重复，必须唯一。示例中repoid设置为base。
name为软件仓库描述的字符串。
baseurl为软件仓库的地址。
enabled为是否启用该软件源仓库，可选值为1和0。默认值为1，表示启用该软件源仓库。
gpgcheck可设置为1或0，1表示进行gpg（GNU Private Guard）校验，0表示不进行gpg校验，gpgcheck可以确定rpm包的来源是有效和安全的。
gpgkey为验证签名用的公钥。</p>
</blockquote>
<h2 id="磁盘扩容">磁盘扩容</h2>
<ol>
<li>在宿主机上安装 <code>qemu-img</code> 工具:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">apt install qemu-utils
</span></span></code></pre></div><ol start="2">
<li>在 openEuler RISC-V 虚拟机上安装 <code>growpart</code> 工具:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">dnf install cloud-utils-growpart
</span></span></code></pre></div><ol start="3">
<li>关闭QEMU虚拟机</li>
<li>把 qcow2 文件的容量加200GB：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ qemu-img resize *.qcow2 +200G
</span></span><span class="line"><span class="cl">Image resized.
</span></span><span class="line"><span class="cl">$ qemu-img info *.qcow2
</span></span><span class="line"><span class="cl">image: openEuler-preview.riscv64.qcow2
</span></span><span class="line"><span class="cl">file format: qcow2
</span></span><span class="line"><span class="cl">virtual size: <span class="m">220</span> GiB 
</span></span><span class="line"><span class="cl">disk size: 9.58 GiB
</span></span><span class="line"><span class="cl">cluster_size: <span class="m">65536</span>
</span></span><span class="line"><span class="cl">Format specific information:
</span></span><span class="line"><span class="cl">    compat: 1.1
</span></span><span class="line"><span class="cl">    compression type: zlib
</span></span><span class="line"><span class="cl">    lazy refcounts: <span class="nb">false</span>
</span></span><span class="line"><span class="cl">    refcount bits: <span class="m">16</span>
</span></span><span class="line"><span class="cl">    corrupt: <span class="nb">false</span>
</span></span><span class="line"><span class="cl">    extended l2: <span class="nb">false</span>
</span></span></code></pre></div><ol start="5">
<li>QEMU 启动 openEuler RISC-V。</li>
</ol>
<p>启动以后，我们先看看分区情况：可以看到根目录对应的分区只使用了 10G。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-RISCV-rare ~<span class="o">]</span><span class="c1"># lsblk</span>
</span></span><span class="line"><span class="cl">NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
</span></span><span class="line"><span class="cl">vda    254:0    <span class="m">0</span>  220G  <span class="m">0</span> disk
</span></span><span class="line"><span class="cl">└─vda1 254:1    <span class="m">0</span>   10G  <span class="m">0</span> part /
</span></span></code></pre></div><ol>
<li>扩展分区 <code>vda1</code>，执行</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">growpart /dev/vda1
</span></span></code></pre></div><p>执行 <code>lsblk</code> 可以看到 / 所在的 <code>vda1</code> 分区已经扩展到了预期大小</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-RISCV-rare ~<span class="o">]</span><span class="c1"># growpart /dev/vda 1</span>
</span></span><span class="line"><span class="cl">CHANGED: <span class="nv">partition</span><span class="o">=</span><span class="m">1</span> <span class="nv">start</span><span class="o">=</span><span class="m">2048</span> old: <span class="nv">size</span><span class="o">=</span><span class="m">20969472</span> <span class="nv">end</span><span class="o">=</span><span class="m">20971520</span> new: <span class="nv">size</span><span class="o">=</span><span class="m">419428319</span> <span class="nv">end</span><span class="o">=</span><span class="m">419430367</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-RISCV-rare ~<span class="o">]</span><span class="c1"># lsblk</span>
</span></span><span class="line"><span class="cl">NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
</span></span><span class="line"><span class="cl">vda    254:0    <span class="m">0</span>  220G  <span class="m">0</span> disk
</span></span><span class="line"><span class="cl">└─vda1 254:1    <span class="m">0</span>  220G  <span class="m">0</span> part /
</span></span></code></pre></div><ol>
<li>扩展文件系统：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">resize2fs /dev/vda1
</span></span></code></pre></div><h1 id="bug">BUG</h1>
<h2 id="network-backend-user-is-not-compiled-into-this-binary">network backend ‘user‘ is not compiled into this binary</h2>
<p>git clone <a href="https://gitlab.freedesktop.org/slirp/libslirp.git">https://gitlab.freedesktop.org/slirp/libslirp.git</a></p>
<p><a href="http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb">http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt-get install libslirp-dev
</span></span></code></pre></div><p>重新编译QEMU：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> rm -rf build
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> ./configure --target-list<span class="o">=</span>riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu <span class="se">\
</span></span></span><span class="line"><span class="cl">               --enable-kvm --enable-sdl --enable-slirp<span class="se">\
</span></span></span><span class="line"><span class="cl">               --prefix<span class="o">=</span>/home/user/program/riscv64-qemu
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">make install -j <span class="k">$(</span>nproc<span class="k">)</span>
</span></span></code></pre></div><h1 id="参考文档">参考文档</h1>
<p><a href="https://github.com/openeuler-mirror/RISC-V/blob/master/doc/tutorials/vm-qemu-oErv.md">RISC-V/doc/tutorials/vm-qemu-oErv.md at master · openeuler-mirror/RISC-V · GitHub</a></p>
<p><a href="https://www.openeuler.org/whitepaper/openEuler-whitepaper-2209.pdf">openEuler 22.09技术白皮书</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>SSH 登录 OpenStack 实例</title>
      <link>https://lifeislife.cn/posts/ssh-%E7%99%BB%E5%BD%95-openstack-%E5%AE%9E%E4%BE%8B/</link>
      <pubDate>Wed, 28 Jun 2023 22:20:05 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ssh-%E7%99%BB%E5%BD%95-openstack-%E5%AE%9E%E4%BE%8B/</guid>
      <description>&lt;h1 id=&#34;基础配置&#34;&gt;基础配置&lt;/h1&gt;
&lt;p&gt;添加安全组规则，允许 Ping 和 SSH 访问虚拟机：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openstack security group rule create --proto icmp default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@allone:~# openstack security group rule create --proto icmp default
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------------+---------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Field             &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Value                                                                                                                                                   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------------+-------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; created_at        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 2023-06-28T06:26:10Z                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; description       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;                                                                                                                                                         &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; direction         &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; ingress                                                                                                                                                 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; ether_type        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; IPv4                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; id                &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; fe9adfc3-dc42-4680-8ecd-ed5a667e1215                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; location          &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;cloud&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, project.domain_id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;, project.domain_name&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Default&amp;#39;&lt;/span&gt;, project.id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;6396365541a74b6b8ea8812d1af05e70&amp;#39;&lt;/span&gt;, project.name&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;, &lt;span class=&#34;nv&#34;&gt;region_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span class=&#34;nv&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; name              &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; None                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; port_range_max    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; None                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; port_range_min    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; None                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; project_id        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 6396365541a74b6b8ea8812d1af05e70                                                                                                                        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; protocol          &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; icmp                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; remote_group_id   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; None                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; remote_ip_prefix  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 0.0.0.0/0                                                                                                                                               &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; revision_number   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;                                                                                                                                                       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; security_group_id &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; f10a3927-5e76-47b4-8691-4169348845ae                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; tags              &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;                                                                                                                                                      &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; updated_at        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 2023-06-28T06:26:10Z                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------------+--------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openstack security group rule create --proto tcp --dst-port &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt; default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@allone:~# openstack security group rule  create --proto tcp --dst-port &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt; default
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------------+--------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Field             &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Value                                                                                                                                                   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------------+--------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; created_at        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 2023-06-28T06:26:15Z                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; description       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;                                                                                                                                                         &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; direction         &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; ingress                                                                                                                                                 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; ether_type        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; IPv4                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; id                &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; af699cf9-5fc0-45e2-a009-0bb7828e2d1a                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; location          &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;cloud&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, project.domain_id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;, project.domain_name&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Default&amp;#39;&lt;/span&gt;, project.id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;6396365541a74b6b8ea8812d1af05e70&amp;#39;&lt;/span&gt;, project.name&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;, &lt;span class=&#34;nv&#34;&gt;region_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span class=&#34;nv&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; name              &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; None                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; port_range_max    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;                                                                                                                                                      &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; port_range_min    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;                                                                                                                                                      &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; project_id        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 6396365541a74b6b8ea8812d1af05e70                                                                                                                        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; protocol          &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; tcp                                                                                                                                                     &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; remote_group_id   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; None                                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; remote_ip_prefix  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 0.0.0.0/0                                                                                                                                               &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; revision_number   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;                                                                                                                                                       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; security_group_id &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; f10a3927-5e76-47b4-8691-4169348845ae                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; tags              &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;                                                                                                                                                      &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; updated_at        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 2023-06-28T06:26:15Z                                                                                                                                    &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------------+-----------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;命令行方式&#34;&gt;命令行方式&lt;/h1&gt;
&lt;h2 id=&#34;生成秘钥&#34;&gt;生成秘钥&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh-keygen -q -N “”
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-q&lt;/code&gt; 选项表示静默模式，即在生成密钥对的过程中不会输出任何提示信息或警告。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-N&lt;/code&gt; 选项后面可以跟一个密码作为参数。该密码将用于保护生成的私钥文件。如果不指定 &lt;code&gt;-N&lt;/code&gt; 参数，则私钥文件将不受密码保护。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;该命令会在&lt;code&gt;~/.ssh/&lt;/code&gt;目录中自动生成一对公私钥。默认私钥名称：id_rsa，默认公钥名称：id_rsa.pub&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openstack keypair create --public-key ~/.ssh/id_rsa.pub mykey
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;向 OpenStack 添加公钥，用于创建实例时选择：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@allone:~# openstack keypair create --public-key ~/.ssh/id_rsa.pub mykey
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------+-------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Field       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Value                                           &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------+-------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; fingerprint &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 11:36:75:e0:c3:98:4c:97:90:30:f5:69:e1:17:a9:4b &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; name        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; mykey                                           &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; user_id     &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 9027da91a2134825a421d78db11011d0                &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+-------------+-------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openstack keypair list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@allone:~# openstack keypair list
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------+-------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Name                &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; Fingerprint                                     &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------+-------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; mykey               &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 11:36:75:e0:c3:98:4c:97:90:30:f5:69:e1:17:a9:4b &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; ubuntu cloud server &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; 67:b4:8a:64:83:4e:47:d0:7c:87:46:34:3b:03:e6:17 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------+-------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh ubuntu@10.0.2.111
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中，&lt;code&gt;ubuntun&lt;/code&gt;是实例的用户名，&lt;code&gt;10.0.2.111&lt;/code&gt;是实例的 IP 地址。&lt;/p&gt;
&lt;h1 id=&#34;web-界面方式&#34;&gt;WEB 界面方式&lt;/h1&gt;
&lt;h2 id=&#34;创建密钥对&#34;&gt;创建密钥对&lt;/h2&gt;
&lt;p&gt;Project-Key Pairs-Create Keypairs&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/17-53-21-d58cbf697d71c5fff58fda7a8145ceae-20230628175315-733a20.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-53-21-d58cbf697d71c5fff58fda7a8145ceae-20230628175315-733a20.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;为密钥对起个名字：sshkey，并选择一个类型：SSH Key&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/17-53-37-56223c56c675b84ea1d375399db38175-20230628175333-06df78.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-53-37-56223c56c675b84ea1d375399db38175-20230628175333-06df78.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;点击创建后会弹出下载私钥的窗口，这时候需要将私钥下载到本地。并将他移动到 ssh 目录下方便管理。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 切换root用户，因为我们一直都是用root用户操作的OpenStack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo su
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv sshkey.pem ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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/17-53-54-d452e4565933506f89744f60f01d7956-20230628175349-ffaa20.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-53-54-d452e4565933506f89744f60f01d7956-20230628175349-ffaa20.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;h2 id=&#34;使用公钥创建实例&#34;&gt;使用公钥创建实例&lt;/h2&gt;
&lt;p&gt;Project-Instances-Launch Instance&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/17-54-08-24889d77a26052dab99f758c54ce9483-20230628175403-9acef3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-54-08-24889d77a26052dab99f758c54ce9483-20230628175403-9acef3.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;在 Key Pair 中选择刚刚创建的 sshkey。这里实际就是将创建的密钥对中的公钥放到了我们的实例中，这样我们就可以拿着本地的私钥去访问实例。&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/17-54-19-5ac37e246ffbd6007356092eac047d6b-20230628175415-7f105e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-54-19-5ac37e246ffbd6007356092eac047d6b-20230628175415-7f105e.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;h2 id=&#34;登录实例&#34;&gt;登录实例&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh -i ~/.ssh/sshkey.pem  ubuntun@10.0.2.111
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;~/.ssh/sshkey.pem&lt;/code&gt;是我们下载的私钥文件，&lt;code&gt;ubuntun&lt;/code&gt;是实例的用户名，&lt;code&gt;10.0.2.111&lt;/code&gt;是实例的 IP 地址。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="基础配置">基础配置</h1>
<p>添加安全组规则，允许 Ping 和 SSH 访问虚拟机：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">openstack security group rule create --proto icmp default
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@allone:~# openstack security group rule create --proto icmp default
</span></span><span class="line"><span class="cl">+-------------------+---------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> Field             <span class="p">|</span> Value                                                                                                                                                   <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-------------------+-------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> created_at        <span class="p">|</span> 2023-06-28T06:26:10Z                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> description       <span class="p">|</span>                                                                                                                                                         <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> direction         <span class="p">|</span> ingress                                                                                                                                                 <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> ether_type        <span class="p">|</span> IPv4                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> id                <span class="p">|</span> fe9adfc3-dc42-4680-8ecd-ed5a667e1215                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> location          <span class="p">|</span> <span class="nv">cloud</span><span class="o">=</span><span class="s1">&#39;&#39;</span>, project.domain_id<span class="o">=</span>, project.domain_name<span class="o">=</span><span class="s1">&#39;Default&#39;</span>, project.id<span class="o">=</span><span class="s1">&#39;6396365541a74b6b8ea8812d1af05e70&#39;</span>, project.name<span class="o">=</span><span class="s1">&#39;admin&#39;</span>, <span class="nv">region_name</span><span class="o">=</span><span class="s1">&#39;&#39;</span>, <span class="nv">zone</span><span class="o">=</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> name              <span class="p">|</span> None                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> port_range_max    <span class="p">|</span> None                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> port_range_min    <span class="p">|</span> None                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> project_id        <span class="p">|</span> 6396365541a74b6b8ea8812d1af05e70                                                                                                                        <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> protocol          <span class="p">|</span> icmp                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> remote_group_id   <span class="p">|</span> None                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> remote_ip_prefix  <span class="p">|</span> 0.0.0.0/0                                                                                                                                               <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> revision_number   <span class="p">|</span> <span class="m">0</span>                                                                                                                                                       <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> security_group_id <span class="p">|</span> f10a3927-5e76-47b4-8691-4169348845ae                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> tags              <span class="p">|</span> <span class="o">[]</span>                                                                                                                                                      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> updated_at        <span class="p">|</span> 2023-06-28T06:26:10Z                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-------------------+--------------------------------+
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">openstack security group rule create --proto tcp --dst-port <span class="m">22</span> default
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@allone:~# openstack security group rule  create --proto tcp --dst-port <span class="m">22</span> default
</span></span><span class="line"><span class="cl">+-------------------+--------------------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> Field             <span class="p">|</span> Value                                                                                                                                                   <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-------------------+--------------------------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> created_at        <span class="p">|</span> 2023-06-28T06:26:15Z                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> description       <span class="p">|</span>                                                                                                                                                         <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> direction         <span class="p">|</span> ingress                                                                                                                                                 <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> ether_type        <span class="p">|</span> IPv4                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> id                <span class="p">|</span> af699cf9-5fc0-45e2-a009-0bb7828e2d1a                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> location          <span class="p">|</span> <span class="nv">cloud</span><span class="o">=</span><span class="s1">&#39;&#39;</span>, project.domain_id<span class="o">=</span>, project.domain_name<span class="o">=</span><span class="s1">&#39;Default&#39;</span>, project.id<span class="o">=</span><span class="s1">&#39;6396365541a74b6b8ea8812d1af05e70&#39;</span>, project.name<span class="o">=</span><span class="s1">&#39;admin&#39;</span>, <span class="nv">region_name</span><span class="o">=</span><span class="s1">&#39;&#39;</span>, <span class="nv">zone</span><span class="o">=</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> name              <span class="p">|</span> None                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> port_range_max    <span class="p">|</span> <span class="m">22</span>                                                                                                                                                      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> port_range_min    <span class="p">|</span> <span class="m">22</span>                                                                                                                                                      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> project_id        <span class="p">|</span> 6396365541a74b6b8ea8812d1af05e70                                                                                                                        <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> protocol          <span class="p">|</span> tcp                                                                                                                                                     <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> remote_group_id   <span class="p">|</span> None                                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> remote_ip_prefix  <span class="p">|</span> 0.0.0.0/0                                                                                                                                               <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> revision_number   <span class="p">|</span> <span class="m">0</span>                                                                                                                                                       <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> security_group_id <span class="p">|</span> f10a3927-5e76-47b4-8691-4169348845ae                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> tags              <span class="p">|</span> <span class="o">[]</span>                                                                                                                                                      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> updated_at        <span class="p">|</span> 2023-06-28T06:26:15Z                                                                                                                                    <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-------------------+-----------------+
</span></span></code></pre></div><h1 id="命令行方式">命令行方式</h1>
<h2 id="生成秘钥">生成秘钥</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh-keygen -q -N “”
</span></span></code></pre></div><blockquote>
<ul>
<li><code>-q</code> 选项表示静默模式，即在生成密钥对的过程中不会输出任何提示信息或警告。</li>
<li><code>-N</code> 选项后面可以跟一个密码作为参数。该密码将用于保护生成的私钥文件。如果不指定 <code>-N</code> 参数，则私钥文件将不受密码保护。</li>
</ul>
</blockquote>
<p>该命令会在<code>~/.ssh/</code>目录中自动生成一对公私钥。默认私钥名称：id_rsa，默认公钥名称：id_rsa.pub</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">openstack keypair create --public-key ~/.ssh/id_rsa.pub mykey
</span></span></code></pre></div><p>向 OpenStack 添加公钥，用于创建实例时选择：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@allone:~# openstack keypair create --public-key ~/.ssh/id_rsa.pub mykey
</span></span><span class="line"><span class="cl">+-------------+-------------------------------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> Field       <span class="p">|</span> Value                                           <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-------------+-------------------------------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> fingerprint <span class="p">|</span> 11:36:75:e0:c3:98:4c:97:90:30:f5:69:e1:17:a9:4b <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> name        <span class="p">|</span> mykey                                           <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> user_id     <span class="p">|</span> 9027da91a2134825a421d78db11011d0                <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-------------+-------------------------------------------------+
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">openstack keypair list
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@allone:~# openstack keypair list
</span></span><span class="line"><span class="cl">+---------------------+-------------------------------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> Name                <span class="p">|</span> Fingerprint                                     <span class="p">|</span>
</span></span><span class="line"><span class="cl">+---------------------+-------------------------------------------------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> mykey               <span class="p">|</span> 11:36:75:e0:c3:98:4c:97:90:30:f5:69:e1:17:a9:4b <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span> ubuntu cloud server <span class="p">|</span> 67:b4:8a:64:83:4e:47:d0:7c:87:46:34:3b:03:e6:17 <span class="p">|</span>
</span></span><span class="line"><span class="cl">+---------------------+-------------------------------------------------+
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh ubuntu@10.0.2.111
</span></span></code></pre></div><p>其中，<code>ubuntun</code>是实例的用户名，<code>10.0.2.111</code>是实例的 IP 地址。</p>
<h1 id="web-界面方式">WEB 界面方式</h1>
<h2 id="创建密钥对">创建密钥对</h2>
<p>Project-Key Pairs-Create Keypairs</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/17-53-21-d58cbf697d71c5fff58fda7a8145ceae-20230628175315-733a20.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-53-21-d58cbf697d71c5fff58fda7a8145ceae-20230628175315-733a20.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>为密钥对起个名字：sshkey，并选择一个类型：SSH Key</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/17-53-37-56223c56c675b84ea1d375399db38175-20230628175333-06df78.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-53-37-56223c56c675b84ea1d375399db38175-20230628175333-06df78.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>点击创建后会弹出下载私钥的窗口，这时候需要将私钥下载到本地。并将他移动到 ssh 目录下方便管理。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 切换root用户，因为我们一直都是用root用户操作的OpenStack</span>
</span></span><span class="line"><span class="cl">sudo su
</span></span><span class="line"><span class="cl">mv sshkey.pem ~/.ssh
</span></span></code></pre></div><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/17-53-54-d452e4565933506f89744f60f01d7956-20230628175349-ffaa20.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-53-54-d452e4565933506f89744f60f01d7956-20230628175349-ffaa20.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>
<h2 id="使用公钥创建实例">使用公钥创建实例</h2>
<p>Project-Instances-Launch Instance</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/17-54-08-24889d77a26052dab99f758c54ce9483-20230628175403-9acef3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-54-08-24889d77a26052dab99f758c54ce9483-20230628175403-9acef3.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>在 Key Pair 中选择刚刚创建的 sshkey。这里实际就是将创建的密钥对中的公钥放到了我们的实例中，这样我们就可以拿着本地的私钥去访问实例。</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/17-54-19-5ac37e246ffbd6007356092eac047d6b-20230628175415-7f105e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/17-54-19-5ac37e246ffbd6007356092eac047d6b-20230628175415-7f105e.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>
<h2 id="登录实例">登录实例</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh -i ~/.ssh/sshkey.pem  ubuntun@10.0.2.111
</span></span></code></pre></div><p>其中<code>~/.ssh/sshkey.pem</code>是我们下载的私钥文件，<code>ubuntun</code>是实例的用户名，<code>10.0.2.111</code>是实例的 IP 地址。</p>
]]></content:encoded>
    </item>
    <item>
      <title>VirtualBox Ubuntu 无法联网</title>
      <link>https://lifeislife.cn/posts/virtualbox-ubuntu%E6%97%A0%E6%B3%95%E8%81%94%E7%BD%91/</link>
      <pubDate>Mon, 26 Jun 2023 22:38:02 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/virtualbox-ubuntu%E6%97%A0%E6%B3%95%E8%81%94%E7%BD%91/</guid>
      <description>&lt;h1 id=&#34;解决方案&#34;&gt;解决方案&lt;/h1&gt;
&lt;p&gt;VirtualBox Ubuntu 无法联网，重启后可以联网但是几分钟后断开网络。笔者的情况是因为 NetworkManager 自动修改了网络配置导致无法联网，具体现象是开机后网卡信息如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;user@allone:~$ ifconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brq64ff9b38-fa: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether ce:29:de:12:35:06  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp0s3: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::2e8f:2be6:3752:dec4  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:18:31:21  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;947&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;584483&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;584.4 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;800&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;116611&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;116.6 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时网络可以正常使用，经过一两分钟后网络信息如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;user@allone:~$ ifconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brq64ff9b38-fa: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether ce:29:de:12:35:06  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp0s3: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::2e8f:2be6:3752:dec4  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:18:31:21  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;947&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;584483&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;584.4 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;800&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;116611&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;116.6 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是默认虚拟网卡的 IP 地址丢失，而不知名网卡 brq64ff9b38-fa 却拥有了 IP，此时网络不可用。如果有类似情况，可以使用以下方式尝试解决，如果是其他问题。请酌情参考。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;关闭 NetworkManager&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl stop NetworkManager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl disable NetworkManager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl mask NetworkManager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;开启 systemd-networkd&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl unmask systemd-networkd.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; systemd-networkd.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo systemctl start systemd-networkd.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;配置 Netplan&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;编辑/etc/netplan/01-network-manager-all.yaml&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;network:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    version: &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    renderer: networkd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ethernets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       enp0s3:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           dhcp4: yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           dhcp6: yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           addresses: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;10.0.2.15/23&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           nameservers:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               addresses: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;8.8.8.8, 8.8.4.4&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           optional: &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中 enp0s3 为网卡名称，addresses 为网卡对应的 IP，均可以通过 ifconfig 查询。&lt;/p&gt;
&lt;p&gt;以下是一些背景知识，以及问题回溯有兴趣可以继续阅读。&lt;/p&gt;
&lt;h1 id=&#34;背景知识&#34;&gt;背景知识&lt;/h1&gt;
&lt;h2 id=&#34;networkmanager-与-systemd-networked&#34;&gt;NetworkManager 与 systemd-networked&lt;/h2&gt;
&lt;p&gt;NetworkManager 是一项后端服务，用于控制 Ubuntu 操作系统上的网络接口。NetworkManager 的替代方法是 systemd-networked，这两者只能使用一个。在 Ubuntu 桌面上，NetworkManager 是通过图形用户界面管理网络界面的默认服务。因此，如果要通过 GUI 配置 IP 地址，则应启用 NetworkManager。如果用的是无桌面的 Server 版，就可以使用 systemd-networked 来管理网络。&lt;/p&gt;
&lt;p&gt;这两种方式都可以通过配置 netplan，即/etc/netplan/01-network-manager-all.yaml 来管理网络，但是在 renderer 属性中配置有所不同，使用 NetworkManager 时配置如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;network:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    version: &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    renderer: NetworkManager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而使用 systemd-networked 时，配置如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;network:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    version: &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    renderer: networkd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有时无法联网可能是因为配置与实际使用的网络管理方式不匹配导致的。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="解决方案">解决方案</h1>
<p>VirtualBox Ubuntu 无法联网，重启后可以联网但是几分钟后断开网络。笔者的情况是因为 NetworkManager 自动修改了网络配置导致无法联网，具体现象是开机后网卡信息如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">user@allone:~$ ifconfig
</span></span><span class="line"><span class="cl">brq64ff9b38-fa: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        ether ce:29:de:12:35:06  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp0s3: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
</span></span><span class="line"><span class="cl">        inet6 fe80::2e8f:2be6:3752:dec4  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:18:31:21  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">947</span>  bytes <span class="m">584483</span> <span class="o">(</span>584.4 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">800</span>  bytes <span class="m">116611</span> <span class="o">(</span>116.6 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><p>此时网络可以正常使用，经过一两分钟后网络信息如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">user@allone:~$ ifconfig
</span></span><span class="line"><span class="cl">brq64ff9b38-fa: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
</span></span><span class="line"><span class="cl">        ether ce:29:de:12:35:06  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp0s3: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet6 fe80::2e8f:2be6:3752:dec4  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:18:31:21  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">947</span>  bytes <span class="m">584483</span> <span class="o">(</span>584.4 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">800</span>  bytes <span class="m">116611</span> <span class="o">(</span>116.6 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><p>也就是默认虚拟网卡的 IP 地址丢失，而不知名网卡 brq64ff9b38-fa 却拥有了 IP，此时网络不可用。如果有类似情况，可以使用以下方式尝试解决，如果是其他问题。请酌情参考。</p>
<ol>
<li>关闭 NetworkManager</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo systemctl stop NetworkManager
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo systemctl disable NetworkManager
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo systemctl mask NetworkManager
</span></span></code></pre></div><ol start="2">
<li>开启 systemd-networkd</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo systemctl unmask systemd-networkd.service
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> systemd-networkd.service
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo systemctl start systemd-networkd.service
</span></span></code></pre></div><ol start="3">
<li>配置 Netplan</li>
</ol>
<p>编辑/etc/netplan/01-network-manager-all.yaml</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">network:
</span></span><span class="line"><span class="cl">    version: <span class="m">2</span>
</span></span><span class="line"><span class="cl">    renderer: networkd
</span></span><span class="line"><span class="cl">    ethernets:
</span></span><span class="line"><span class="cl">       enp0s3:
</span></span><span class="line"><span class="cl">           dhcp4: yes
</span></span><span class="line"><span class="cl">           dhcp6: yes
</span></span><span class="line"><span class="cl">           addresses: <span class="o">[</span>10.0.2.15/23<span class="o">]</span>
</span></span><span class="line"><span class="cl">           nameservers:
</span></span><span class="line"><span class="cl">               addresses: <span class="o">[</span>8.8.8.8, 8.8.4.4<span class="o">]</span>
</span></span><span class="line"><span class="cl">           optional: <span class="nb">true</span>
</span></span></code></pre></div><p>其中 enp0s3 为网卡名称，addresses 为网卡对应的 IP，均可以通过 ifconfig 查询。</p>
<p>以下是一些背景知识，以及问题回溯有兴趣可以继续阅读。</p>
<h1 id="背景知识">背景知识</h1>
<h2 id="networkmanager-与-systemd-networked">NetworkManager 与 systemd-networked</h2>
<p>NetworkManager 是一项后端服务，用于控制 Ubuntu 操作系统上的网络接口。NetworkManager 的替代方法是 systemd-networked，这两者只能使用一个。在 Ubuntu 桌面上，NetworkManager 是通过图形用户界面管理网络界面的默认服务。因此，如果要通过 GUI 配置 IP 地址，则应启用 NetworkManager。如果用的是无桌面的 Server 版，就可以使用 systemd-networked 来管理网络。</p>
<p>这两种方式都可以通过配置 netplan，即/etc/netplan/01-network-manager-all.yaml 来管理网络，但是在 renderer 属性中配置有所不同，使用 NetworkManager 时配置如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">network:
</span></span><span class="line"><span class="cl">    version: <span class="m">2</span>
</span></span><span class="line"><span class="cl">    renderer: NetworkManager
</span></span></code></pre></div><p>而使用 systemd-networked 时，配置如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">network:
</span></span><span class="line"><span class="cl">    version: <span class="m">2</span>
</span></span><span class="line"><span class="cl">    renderer: networkd
</span></span></code></pre></div><p>有时无法联网可能是因为配置与实际使用的网络管理方式不匹配导致的。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Virtual Box 的不同虚拟机网络模式</title>
      <link>https://lifeislife.cn/posts/virtual-box%E7%9A%84%E4%B8%8D%E5%90%8C%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%BC%8F/</link>
      <pubDate>Sat, 17 Jun 2023 16:48:02 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/virtual-box%E7%9A%84%E4%B8%8D%E5%90%8C%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%BC%8F/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;💻 NAT 网络模式
&lt;ul&gt;
&lt;li&gt;NAT 网络以路由器的 NAT 功能为原理，允许虚拟机通过共享主机的 IP 地址访问互联网，但虚拟机之间不能直接通信。通过端口转发可以实现虚拟机之间的连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🔗 桥接网络模式
&lt;ul&gt;
&lt;li&gt;桥接网络模式通过虚拟交换机连接虚拟机和主机，使得虚拟机可以通过局域网访问互联网，并允许虚拟机之间直接通信。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🔒 内部网络模式
&lt;ul&gt;
&lt;li&gt;内部网络模式使得虚拟机可以创建一个完全隔离的网络，虚拟机之间可以直接通信，但无法访问互联网或外部网络。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🏠 仅主机网络模式
&lt;ul&gt;
&lt;li&gt;仅主机网络模式允许虚拟机之间可以通信，并且与主机之间也可以通信，但无法访问互联网或外部网络。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;虚拟机 ↔ 虚拟机&lt;/th&gt;
          &lt;th&gt;虚拟机 → 宿主机&lt;/th&gt;
          &lt;th&gt;宿主机 → 虚拟机&lt;/th&gt;
          &lt;th&gt;虚拟机 → 互联网&lt;/th&gt;
          &lt;th&gt;互联网 → 虚拟机&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;网络地址转换 NAT&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;NAT 网络&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;×&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Bridged Adapter 桥接网卡&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
          &lt;td&gt;√&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
      <content:encoded><![CDATA[<ul>
<li>💻 NAT 网络模式
<ul>
<li>NAT 网络以路由器的 NAT 功能为原理，允许虚拟机通过共享主机的 IP 地址访问互联网，但虚拟机之间不能直接通信。通过端口转发可以实现虚拟机之间的连接。</li>
</ul>
</li>
<li>🔗 桥接网络模式
<ul>
<li>桥接网络模式通过虚拟交换机连接虚拟机和主机，使得虚拟机可以通过局域网访问互联网，并允许虚拟机之间直接通信。</li>
</ul>
</li>
<li>🔒 内部网络模式
<ul>
<li>内部网络模式使得虚拟机可以创建一个完全隔离的网络，虚拟机之间可以直接通信，但无法访问互联网或外部网络。</li>
</ul>
</li>
<li>🏠 仅主机网络模式
<ul>
<li>仅主机网络模式允许虚拟机之间可以通信，并且与主机之间也可以通信，但无法访问互联网或外部网络。</li>
</ul>
</li>
</ul>
<table>
  <thead>
      <tr>
          <th></th>
          <th>虚拟机 ↔ 虚拟机</th>
          <th>虚拟机 → 宿主机</th>
          <th>宿主机 → 虚拟机</th>
          <th>虚拟机 → 互联网</th>
          <th>互联网 → 虚拟机</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>网络地址转换 NAT</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
      </tr>
      <tr>
          <td>NAT 网络</td>
          <td>√</td>
          <td>√</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
      </tr>
      <tr>
          <td>Bridged Adapter 桥接网卡</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
      </tr>
  </tbody>
</table>
]]></content:encoded>
    </item>
    <item>
      <title>计算机网络 - 数据链路层</title>
      <link>https://lifeislife.cn/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82/</link>
      <pubDate>Sun, 11 Jun 2023 10:26:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82/</guid>
      <description>&lt;h2 id=&#34;数据链路层&#34;&gt;数据链路层&lt;/h2&gt;
&lt;h5 id=&#34;基本概念&#34;&gt;基本概念&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;结点&lt;/strong&gt;：主机，路由器
&lt;strong&gt;链路&lt;/strong&gt;：网络中两个结点之间的&lt;code&gt;物理通道&lt;/code&gt;，链路的传输介质主要有双绞线、光纤和微波。分为有线链路、无线链路。
&lt;strong&gt;数据链路&lt;/strong&gt;：网络中两个结点之间的&lt;code&gt;逻辑通道&lt;/code&gt;，把实现控制数据传输&lt;code&gt;协议&lt;/code&gt;的硬件和软件加到链路上就构成数据链路。
&lt;strong&gt;帧&lt;/strong&gt;：链路层的协议数据单元，封装网络层数据报。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;数据链路层负责通过一条链路从一个结点向另一个物理链路直接相连的相邻结点传送数据报。&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;数据链路层的功能&#34;&gt;数据链路层的功能&lt;/h3&gt;
&lt;h4 id=&#34;功能概述&#34;&gt;功能概述&lt;/h4&gt;
&lt;p&gt;数据链路层在物理层提供服务的基础上&lt;code&gt;向网络层提供服务&lt;/code&gt;，其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层。其主要作用是&lt;code&gt;加强物理层传输原始比特流的功能&lt;/code&gt;，将物理层提供的可能出错的物理连 接改造成为逻辑上无差错的数据链路，使之对网络层表现为一条无差错的链路。&lt;/p&gt;
&lt;ul&gt;
&lt;li&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;/li&gt;
&lt;li&gt;链路管理
&lt;ul&gt;
&lt;li&gt;连接的建立，维持，释放&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;组帧&lt;/li&gt;
&lt;li&gt;差错控制&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;封装成帧&#34;&gt;封装成帧&lt;/h4&gt;
&lt;p&gt;封装成帧就是在一段数据的前后部分&lt;code&gt;添加首部和尾部&lt;/code&gt;，这样就构成了一个帧。接收端在收到物理层上交的比特流后，就能根据首部和尾部的&lt;code&gt;标记(帧定界符)&lt;/code&gt;，从收到的比特流中识别帧的&lt;code&gt;开始&lt;/code&gt;和&lt;code&gt;结束&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;首部和尾部包含许多的控制信息，他们的一个重要作用：&lt;code&gt;帧定界&lt;/code&gt;（确定帧的界限）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;帧同步&lt;/strong&gt;：接收方应当能从接收到的二进制比特流中&lt;code&gt;区分出帧的起始和终止&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//2023/06/11/af63ec599debe847b08e6d41fe404e9e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/af63ec599debe847b08e6d41fe404e9e.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;h4 id=&#34;差错控制&#34;&gt;差错控制&lt;/h4&gt;
&lt;p&gt;传输中的差错都是由噪声引起的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全局性，由于线路本身电气特性所产生的&lt;code&gt;随机噪声&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;局部性，由于外界短暂的原因造成的&lt;code&gt;冲击噪声&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;差错又分为&lt;code&gt;位错&lt;/code&gt;和&lt;code&gt;帧错&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;位错，比特位出错，1 变 0,0 变 1&lt;/li&gt;
&lt;li&gt;帧错，包括丢失，重复，失序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;发现差错的帧后就将错误值丢弃，如果没有差错控制，将会浪费大量资源，因为传输过程中一直传输了错误的信息。&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;差错控制&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检错编码
&lt;ul&gt;
&lt;li&gt;奇偶校验码&lt;/li&gt;
&lt;li&gt;循环冗余 CRC&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;纠错编码
&lt;ul&gt;
&lt;li&gt;海明码&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里提到的编码和物理层的编码与调制不同，物理层的编码针对单个比特，解决传输同步问题。这里的编码针对的是一组比特，通过冗余码的技术检测传输中是否出错。&lt;/p&gt;
&lt;h5 id=&#34;奇偶校验码&#34;&gt;奇偶校验码&lt;/h5&gt;
&lt;p&gt;奇校验码：在信息元前加上 1 位后使得 1 的个数为奇数个
偶检验码：在信息元前加上 1 位后使得 1 的个数为偶数个&lt;/p&gt;
&lt;p&gt;该检测方式只能检测出奇数个的位错，检错能力为 50%&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;如果一个字符S的ASCI编码从低到高依次为1100101，采用奇校验，在下述收到的传输后字符中，哪种错误不能检测？
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;A.11000011B.11001010 C.11001100 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;D.11010011
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;答：因为采用奇校验，所以在首位加上一个1使得所有1个数为奇数变成11100101，ABC选项中1的个数都是偶数个，明显发生了变化，所以能检测出错误，但是D选项的1也是奇数个，将无法判断是否出现差错。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h5 id=&#34;crc-循环冗余码&#34;&gt;CRC 循环冗余码&lt;/h5&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//2023/06/11/41f5d4aa394ce6d74c8b134c9768ac22.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/41f5d4aa394ce6d74c8b134c9768ac22.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//2023/06/11/7c99a28025636d12adcdf50e5d06e1ce.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/7c99a28025636d12adcdf50e5d06e1ce.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;h3 id=&#34;局域网广域网&#34;&gt;局域网、广域网&lt;/h3&gt;
&lt;h3 id=&#34;数据链路层的设备&#34;&gt;数据链路层的设备&lt;/h3&gt;
&lt;h2 id=&#34;流量控制与可靠传输&#34;&gt;流量控制与可靠传输&lt;/h2&gt;
&lt;h3 id=&#34;单帧滑动窗口与停止等待协议&#34;&gt;单帧滑动窗口与停止等待协议&lt;/h3&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//2023/06/11/5d99df99892aa63951510153134014b1.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/5d99df99892aa63951510153134014b1.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;!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//2023/06/11/fa9be9766df59bc680fd8441e2f21224.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/fa9be9766df59bc680fd8441e2f21224.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;!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//2023/06/11/a2bc7aeff36d97f1670360ab1beed0a9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/a2bc7aeff36d97f1670360ab1beed0a9.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;sr-选择重传协议&#34;&gt;SR 选择重传协议&lt;/h3&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//2023/06/11/7aabcc8d17c084dbc9d8082955e37869.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/7aabcc8d17c084dbc9d8082955e37869.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;!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//2023/06/11/7c6ba4ec2494cf60ed6293173d595530.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/7c6ba4ec2494cf60ed6293173d595530.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;h4 id=&#34;滑动窗口最大值&#34;&gt;滑动窗口最大值&lt;/h4&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//2023/06/11/0e9683cca18816515bfe2e5df3421954.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/0e9683cca18816515bfe2e5df3421954.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;
</description>
      <content:encoded><![CDATA[<h2 id="数据链路层">数据链路层</h2>
<h5 id="基本概念">基本概念</h5>
<p><strong>结点</strong>：主机，路由器
<strong>链路</strong>：网络中两个结点之间的<code>物理通道</code>，链路的传输介质主要有双绞线、光纤和微波。分为有线链路、无线链路。
<strong>数据链路</strong>：网络中两个结点之间的<code>逻辑通道</code>，把实现控制数据传输<code>协议</code>的硬件和软件加到链路上就构成数据链路。
<strong>帧</strong>：链路层的协议数据单元，封装网络层数据报。</p>
<p><code>数据链路层负责通过一条链路从一个结点向另一个物理链路直接相连的相邻结点传送数据报。</code></p>
<h3 id="数据链路层的功能">数据链路层的功能</h3>
<h4 id="功能概述">功能概述</h4>
<p>数据链路层在物理层提供服务的基础上<code>向网络层提供服务</code>，其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层。其主要作用是<code>加强物理层传输原始比特流的功能</code>，将物理层提供的可能出错的物理连 接改造成为逻辑上无差错的数据链路，使之对网络层表现为一条无差错的链路。</p>
<ul>
<li>为网络层提供服务
<ul>
<li>无确认无连接服务</li>
<li>有确认无连接服务</li>
<li>有确认面向连接服务</li>
</ul>
</li>
<li>链路管理
<ul>
<li>连接的建立，维持，释放</li>
</ul>
</li>
<li>组帧</li>
<li>差错控制</li>
</ul>
<h4 id="封装成帧">封装成帧</h4>
<p>封装成帧就是在一段数据的前后部分<code>添加首部和尾部</code>，这样就构成了一个帧。接收端在收到物理层上交的比特流后，就能根据首部和尾部的<code>标记(帧定界符)</code>，从收到的比特流中识别帧的<code>开始</code>和<code>结束</code>。</p>
<p>首部和尾部包含许多的控制信息，他们的一个重要作用：<code>帧定界</code>（确定帧的界限）。</p>
<p><strong>帧同步</strong>：接收方应当能从接收到的二进制比特流中<code>区分出帧的起始和终止</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//2023/06/11/af63ec599debe847b08e6d41fe404e9e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/af63ec599debe847b08e6d41fe404e9e.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>
<h4 id="差错控制">差错控制</h4>
<p>传输中的差错都是由噪声引起的。</p>
<ul>
<li>全局性，由于线路本身电气特性所产生的<code>随机噪声</code></li>
<li>局部性，由于外界短暂的原因造成的<code>冲击噪声</code></li>
</ul>
<p>差错又分为<code>位错</code>和<code>帧错</code></p>
<ul>
<li>位错，比特位出错，1 变 0,0 变 1</li>
<li>帧错，包括丢失，重复，失序</li>
</ul>
<p><code>发现差错的帧后就将错误值丢弃，如果没有差错控制，将会浪费大量资源，因为传输过程中一直传输了错误的信息。</code></p>
<p>差错控制</p>
<ul>
<li>检错编码
<ul>
<li>奇偶校验码</li>
<li>循环冗余 CRC</li>
</ul>
</li>
<li>纠错编码
<ul>
<li>海明码</li>
</ul>
</li>
</ul>
<p>这里提到的编码和物理层的编码与调制不同，物理层的编码针对单个比特，解决传输同步问题。这里的编码针对的是一组比特，通过冗余码的技术检测传输中是否出错。</p>
<h5 id="奇偶校验码">奇偶校验码</h5>
<p>奇校验码：在信息元前加上 1 位后使得 1 的个数为奇数个
偶检验码：在信息元前加上 1 位后使得 1 的个数为偶数个</p>
<p>该检测方式只能检测出奇数个的位错，检错能力为 50%</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">如果一个字符S的ASCI编码从低到高依次为1100101，采用奇校验，在下述收到的传输后字符中，哪种错误不能检测？
</span></span><span class="line"><span class="cl">A.11000011B.11001010 C.11001100 
</span></span><span class="line"><span class="cl">D.11010011
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">答：因为采用奇校验，所以在首位加上一个1使得所有1个数为奇数变成11100101，ABC选项中1的个数都是偶数个，明显发生了变化，所以能检测出错误，但是D选项的1也是奇数个，将无法判断是否出现差错。
</span></span></code></pre></div><h5 id="crc-循环冗余码">CRC 循环冗余码</h5>
<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//2023/06/11/41f5d4aa394ce6d74c8b134c9768ac22.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/41f5d4aa394ce6d74c8b134c9768ac22.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//2023/06/11/7c99a28025636d12adcdf50e5d06e1ce.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/7c99a28025636d12adcdf50e5d06e1ce.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>
<h3 id="局域网广域网">局域网、广域网</h3>
<h3 id="数据链路层的设备">数据链路层的设备</h3>
<h2 id="流量控制与可靠传输">流量控制与可靠传输</h2>
<h3 id="单帧滑动窗口与停止等待协议">单帧滑动窗口与停止等待协议</h3>
<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//2023/06/11/5d99df99892aa63951510153134014b1.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/5d99df99892aa63951510153134014b1.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>


<!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//2023/06/11/fa9be9766df59bc680fd8441e2f21224.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/fa9be9766df59bc680fd8441e2f21224.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>


<!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//2023/06/11/a2bc7aeff36d97f1670360ab1beed0a9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/a2bc7aeff36d97f1670360ab1beed0a9.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="sr-选择重传协议">SR 选择重传协议</h3>
<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//2023/06/11/7aabcc8d17c084dbc9d8082955e37869.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/7aabcc8d17c084dbc9d8082955e37869.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>


<!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//2023/06/11/7c6ba4ec2494cf60ed6293173d595530.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/7c6ba4ec2494cf60ed6293173d595530.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>
<h4 id="滑动窗口最大值">滑动窗口最大值</h4>
<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//2023/06/11/0e9683cca18816515bfe2e5df3421954.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/11/0e9683cca18816515bfe2e5df3421954.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>
]]></content:encoded>
    </item>
    <item>
      <title>Ubuntu 22.04 系统安装水星 wifi 驱动 Mercury MW310UH</title>
      <link>https://lifeislife.cn/posts/ubuntu-22-04-%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E6%B0%B4%E6%98%9F-wifi-%E9%A9%B1%E5%8A%A8mercury-mw310uh/</link>
      <pubDate>Sun, 11 Jun 2023 10:00:02 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ubuntu-22-04-%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85%E6%B0%B4%E6%98%9F-wifi-%E9%A9%B1%E5%8A%A8mercury-mw310uh/</guid>
      <description>&lt;h1 id=&#34;确认网卡信息&#34;&gt;确认网卡信息&lt;/h1&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lsusb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;得到 USB 设备信息&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Bus &lt;span class=&#34;m&#34;&gt;001&lt;/span&gt; Device 013: ID 0bda:a192 Realtek Semiconductor Corp. Disk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;安装网卡驱动&#34;&gt;安装网卡驱动&lt;/h1&gt;
&lt;p&gt;根据设备 ID，用关键词网上搜素一下相关驱动，得到有这个驱动可用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install build-essential git dkms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://gitee.com/BrightXu/rtl8192fu.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; rtl8192fu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -j&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo make install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo modprobe 8192fu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看是否安装成功&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;usb-devices
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果有 Driver=rtl8192fu 字段说明安装成功。如果桌面右上角无线连接图标可用，说明可以使用无线网络了。如果不可用继续往下看。&lt;/p&gt;
&lt;h1 id=&#34;修改设备模式&#34;&gt;修改设备模式&lt;/h1&gt;
&lt;p&gt;如果使用&lt;code&gt;lsusb&lt;/code&gt;命令查看设备，发现设备末尾依然是 Disk 模式，说明这个设备是磁盘设备，还不能当做网络适配器使用，需要修改其模式。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install -y usb-modeswitch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /lib/udev/rules.d/40-usb_modeswitch.rules
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在最后 LABEL 之前加上&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Realtek 8192F Wifi AC USB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ATTR&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;idVendor&lt;span class=&#34;o&#34;&gt;}==&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;0bda&amp;#34;&lt;/span&gt;, ATTR&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;idProduct&lt;span class=&#34;o&#34;&gt;}==&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;a192&amp;#34;&lt;/span&gt;, &lt;span class=&#34;nv&#34;&gt;RUN&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/sbin/usb_modeswitch -K -v 0bda -p a192&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;LABEL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;modeswitch_rules_end&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo usb_modeswitch -KW -v 0bda -p a192
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;关闭安全启动&#34;&gt;关闭安全启动&lt;/h1&gt;
&lt;p&gt;安全启动模式下无法使用第三方的驱动，所以需要在开机时进入 BIOS 将安全启动关闭，每个主板不一样，自行搜索。&lt;/p&gt;
&lt;h1 id=&#34;参考&#34;&gt;参考&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.csdn.net/KeyBordkiller/article/details/124498463&#34;&gt;linux 系统下 usb 网卡的驱动安装_linux usb 网卡-CSDN 博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.guohuawei.com/archives/install-realtek-wifi-5ghz-driver-for-ubuntu-2204.html&#34;&gt;为 ubuntu 22.04 系统安装水星 realtek 5g wifi 驱动 - 郭华伟的博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.csdn.net/weixin_45392081/article/details/125930289&#34;&gt;ubuntu 18.04 usb 无线网卡无法使用&amp;ndash;ID 0bda:a192 Realtek Semiconductor Corp._放羊 Wa 的博客-CSDN 博客&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="确认网卡信息">确认网卡信息</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">lsusb
</span></span></code></pre></div><p>得到 USB 设备信息</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> Bus <span class="m">001</span> Device 013: ID 0bda:a192 Realtek Semiconductor Corp. Disk
</span></span></code></pre></div><h1 id="安装网卡驱动">安装网卡驱动</h1>
<p>根据设备 ID，用关键词网上搜素一下相关驱动，得到有这个驱动可用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install build-essential git dkms
</span></span><span class="line"><span class="cl">git clone https://gitee.com/BrightXu/rtl8192fu.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> rtl8192fu
</span></span><span class="line"><span class="cl">make -j<span class="k">$(</span>nproc<span class="k">)</span>
</span></span><span class="line"><span class="cl">sudo make install
</span></span><span class="line"><span class="cl">sudo modprobe 8192fu
</span></span></code></pre></div><p>查看是否安装成功</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">usb-devices
</span></span></code></pre></div><p>如果有 Driver=rtl8192fu 字段说明安装成功。如果桌面右上角无线连接图标可用，说明可以使用无线网络了。如果不可用继续往下看。</p>
<h1 id="修改设备模式">修改设备模式</h1>
<p>如果使用<code>lsusb</code>命令查看设备，发现设备末尾依然是 Disk 模式，说明这个设备是磁盘设备，还不能当做网络适配器使用，需要修改其模式。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install -y usb-modeswitch
</span></span><span class="line"><span class="cl">sudo vim /lib/udev/rules.d/40-usb_modeswitch.rules
</span></span></code></pre></div><p>在最后 LABEL 之前加上</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Realtek 8192F Wifi AC USB</span>
</span></span><span class="line"><span class="cl">ATTR<span class="o">{</span>idVendor<span class="o">}==</span><span class="s2">&#34;0bda&#34;</span>, ATTR<span class="o">{</span>idProduct<span class="o">}==</span><span class="s2">&#34;a192&#34;</span>, <span class="nv">RUN</span><span class="o">+=</span><span class="s2">&#34;/usr/sbin/usb_modeswitch -K -v 0bda -p a192&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">LABEL</span><span class="o">=</span><span class="s2">&#34;modeswitch_rules_end&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo usb_modeswitch -KW -v 0bda -p a192
</span></span></code></pre></div><h1 id="关闭安全启动">关闭安全启动</h1>
<p>安全启动模式下无法使用第三方的驱动，所以需要在开机时进入 BIOS 将安全启动关闭，每个主板不一样，自行搜索。</p>
<h1 id="参考">参考</h1>
<p><a href="https://blog.csdn.net/KeyBordkiller/article/details/124498463">linux 系统下 usb 网卡的驱动安装_linux usb 网卡-CSDN 博客</a></p>
<p><a href="https://www.guohuawei.com/archives/install-realtek-wifi-5ghz-driver-for-ubuntu-2204.html">为 ubuntu 22.04 系统安装水星 realtek 5g wifi 驱动 - 郭华伟的博客</a></p>
<p><a href="https://blog.csdn.net/weixin_45392081/article/details/125930289">ubuntu 18.04 usb 无线网卡无法使用&ndash;ID 0bda:a192 Realtek Semiconductor Corp._放羊 Wa 的博客-CSDN 博客</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>云计算基础技术汇总</title>
      <link>https://lifeislife.cn/posts/%E4%BA%91%E8%AE%A1%E7%AE%97%E5%9F%BA%E7%A1%80%E6%8A%80%E6%9C%AF%E6%B1%87%E6%80%BB/</link>
      <pubDate>Fri, 09 Jun 2023 21:42:59 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E4%BA%91%E8%AE%A1%E7%AE%97%E5%9F%BA%E7%A1%80%E6%8A%80%E6%9C%AF%E6%B1%87%E6%80%BB/</guid>
      <description>&lt;h1 id=&#34;云计算服务类型&#34;&gt;云计算服务类型&lt;/h1&gt;
&lt;p&gt;传统架构=&amp;gt;Iaas=&amp;gt;Paas=&amp;gt;Saas&lt;/p&gt;
&lt;p&gt;自己烧饭=&amp;gt; 叮咚买菜=&amp;gt; 美团外卖=&amp;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/09-36-45-b25eafa700db3dce390dab2aeb3af4f9-20230531093640-f17dbe.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-36-45-b25eafa700db3dce390dab2aeb3af4f9-20230531093640-f17dbe.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;h1 id=&#34;云计算部署形式以及应用&#34;&gt;云计算部署形式以及应用&lt;/h1&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;类型&lt;/th&gt;
          &lt;th&gt;描述&lt;/th&gt;
          &lt;th&gt;优点&lt;/th&gt;
          &lt;th&gt;缺点&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;私有云&lt;/td&gt;
          &lt;td&gt;利用已有设备自我构建，云端资源只给内部人员使用。&lt;/td&gt;
          &lt;td&gt;安全性高&lt;/td&gt;
          &lt;td&gt;维护成本高&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;社区云、行业云&lt;/td&gt;
          &lt;td&gt;为特定行业构建共享基础设施的云。&lt;/td&gt;
          &lt;td&gt;有一套用户体系&lt;/td&gt;
          &lt;td&gt;维护成本高&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;公有云&lt;/td&gt;
          &lt;td&gt;构建大型基础设施云出租给公众。&lt;/td&gt;
          &lt;td&gt;用户来说成本低，服务多&lt;/td&gt;
          &lt;td&gt;安全性低&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;混合云&lt;/td&gt;
          &lt;td&gt;两种或者两种以上的云组成的云服务&lt;/td&gt;
          &lt;td&gt;敏捷，灵活，降低成本&lt;/td&gt;
          &lt;td&gt;兼容性问题&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;应用&#34;&gt;应用&lt;/h2&gt;
&lt;p&gt;存储云、医疗云、教育云、企业云、金融云、游戏云、桌面云&lt;/p&gt;
&lt;h1 id=&#34;关键技术&#34;&gt;关键技术&lt;/h1&gt;
&lt;h2 id=&#34;虚拟化&#34;&gt;虚拟化&lt;/h2&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/10-28-09-8bfe3f3fcd50903c6f72132d36c19f3e-20230531102804-d53750.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-28-09-8bfe3f3fcd50903c6f72132d36c19f3e-20230531102804-d53750.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;h2 id=&#34;分布式存储&#34;&gt;分布式存储&lt;/h2&gt;
&lt;p&gt;将数据存储在不同的物理设备中。这种模式不仅摆脱了硬件设备的限制，同时&lt;strong&gt;扩展性更好&lt;/strong&gt;，能够快速响应用户需求的变化（整合存储资源提供动态可伸缩资源池的分布式存储技术)&lt;/p&gt;
&lt;h2 id=&#34;数据中心联网&#34;&gt;数据中心联网&lt;/h2&gt;
&lt;p&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/10-38-24-9e7878e87965a767076d4071dd0e354f-20230531103820-7087aa.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-38-24-9e7878e87965a767076d4071dd0e354f-20230531103820-7087aa.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;h2 id=&#34;并行编程&#34;&gt;并行编程&lt;/h2&gt;
&lt;p&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/16-17-02-45f21028b6e199dccd616053e8e43db8-20230602161656-9db753.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-17-02-45f21028b6e199dccd616053e8e43db8-20230602161656-9db753.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;h2 id=&#34;体系结构&#34;&gt;体系结构&lt;/h2&gt;
&lt;p&gt;云计算平台体系结构由用户界面、服务目录、管理系统、部署工具、监控和服务器集群组成。&lt;/p&gt;
&lt;h2 id=&#34;自动化部署&#34;&gt;自动化部署&lt;/h2&gt;
&lt;p&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/08-46-31-bc2856698d6f9b760f7cad0b1bff7ef1-20230605084626-c49970.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-46-31-bc2856698d6f9b760f7cad0b1bff7ef1-20230605084626-c49970.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;h1 id=&#34;云服务提供商&#34;&gt;云服务提供商&lt;/h1&gt;
&lt;p&gt;亚马逊云、腾讯云、阿里云、百度云、华为云&lt;/p&gt;
&lt;p&gt;技术架构：开源（Xen,KVM），Vmware，微软 hyper-v，阿里飞天 Apsara&lt;/p&gt;
&lt;p&gt;开源云管理平台：OpenStack&lt;/p&gt;
&lt;h1 id=&#34;虚拟化简介&#34;&gt;虚拟化简介&lt;/h1&gt;
&lt;p&gt;虚拟化：一种计算机资源管理技术，将各种 T 实体资源抽象、转换成另一种形式的技术都是虚拟化。
作用：通过该技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机，每个逻辑计算机可运行不同的操作系统，并且应用程序都可以在相互独立的空间内运行而互不影响，从而&lt;strong&gt;显著提高计算机的工作效率&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;从行业数据互相关联的角度来说，云计算是极度依赖虚拟化的。但虚拟化并非云计算，云计算也并非虚拟化。&lt;strong&gt;虚拟化只是云计算的核心技术，但并非云计算的核心关注点&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;云计算是一种服务。虚拟化是云计算的技术基础。&lt;/p&gt;
&lt;h2 id=&#34;虚拟化相关的几个概念&#34;&gt;虚拟化相关的几个概念&lt;/h2&gt;
&lt;p&gt;Guest OS:运行在虚拟机之上的 OS
Guest Machine:虚拟出来的虚拟机
VMM:虚拟机监控器，即虚拟化层 (Virtual Machine Monitor,VMM)
Host OS:运行在物理机之上的 OS
Host Machine:物理机&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/12-08-35-2a578c6b5ecfe26034e87a65aafd21da-20230531120830-e26a24.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/12-08-35-2a578c6b5ecfe26034e87a65aafd21da-20230531120830-e26a24.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;h2 id=&#34;虚拟化类型&#34;&gt;虚拟化类型&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;虚拟化类型&lt;/th&gt;
          &lt;th&gt;描述&lt;/th&gt;
          &lt;th&gt;特点&lt;/th&gt;
          &lt;th&gt;案例&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;寄居虚拟化（Type2）&lt;/td&gt;
          &lt;td&gt;在主机（宿主）操作系统上安装和运行虚拟化程序&lt;/td&gt;
          &lt;td&gt;- 简单、易于实现。&lt;br /&gt;- 安装和运行应用程序依赖于主机操作系统对设备的支持。&lt;br /&gt;- 有两层 OS，管理开销较大，性能损耗大。&lt;br /&gt;- 虚拟机对各种物理设备 (cpu、内存、硬盘等) 的调用，都通过虚拟化层和宿主机的 OS 一起协调才能完成。&lt;/td&gt;
          &lt;td&gt;- Vmware&lt;br /&gt;- VirturalBox&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;裸金属虚拟化 (Type1)&lt;/td&gt;
          &lt;td&gt;直接将 VMM 安装在硬件设备上，VMM 在这种模式下又叫做 Hypervisor，虚拟机有指令要执行时，Hypervisors 会接管该指令，模拟相应的操作。&lt;/td&gt;
          &lt;td&gt;- 不依赖于操作系统。&lt;br /&gt;- 支持多种操作系统，多种应用。&lt;br /&gt;- 依赖虚拟层内核和服务器控制台进行管理。&lt;br /&gt;- 需要对虚拟层的内核进行开发（难度大）。&lt;/td&gt;
          &lt;td&gt;- VMware ESX&lt;br /&gt;- Xen&lt;br /&gt;- 华为 FusionSphere&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;混合虚拟化&lt;/td&gt;
          &lt;td&gt;在一个现有的正常操作系统下&lt;strong&gt;安装一个内核模块&lt;/strong&gt;，内核拥有虚拟化能力。(相当于寄居与裸金属的混合)&lt;/td&gt;
          &lt;td&gt;- 相对于寄居虚拟化架构，性能高。&lt;br /&gt;- 相对于裸金属虚拟化架构，不需要开发内核。&lt;br /&gt;- 可支持多种操作系统。&lt;br /&gt;- 需底层硬件支持虚拟化扩展功能。&lt;/td&gt;
          &lt;td&gt;- KVM&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;寄居虚拟化（Type2）&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/14-24-43-35c85e077a5dc39f5f17e5a2ba879e16-20230602142439-ca0fee.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-24-43-35c85e077a5dc39f5f17e5a2ba879e16-20230602142439-ca0fee.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;裸金属虚拟化 (Type1)&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/14-24-29-4e00e51ef3d07ece5d29fa6a297b2f5b-20230602142424-f05f39.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-24-29-4e00e51ef3d07ece5d29fa6a297b2f5b-20230602142424-f05f39.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;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/14-24-07-e0079492f1d08c44e5c5bf88944f6e42-20230602142402-80ad49.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-24-07-e0079492f1d08c44e5c5bf88944f6e42-20230602142402-80ad49.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;h2 id=&#34;虚拟化层架构&#34;&gt;虚拟化层架构&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;架构&lt;/th&gt;
          &lt;th&gt;描述&lt;/th&gt;
          &lt;th&gt;特点&lt;/th&gt;
          &lt;th&gt;典型&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;全虚拟化&lt;/td&gt;
          &lt;td&gt;即所抽象的 VM 具有完全的物理特性，虚拟化层负责捕获 CPU 指令，为指令访问硬件充当媒介。&lt;/td&gt;
          &lt;td&gt;- OS 无需修改。&lt;br /&gt;- 速度和功能都非常不错，使用非常简单。&lt;br /&gt;- 移植性好。&lt;/td&gt;
          &lt;td&gt;- VMware&lt;br /&gt;- KVM&lt;br /&gt;- Virtualbox&lt;br /&gt;- Virtual PC&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;半虚拟化&lt;/td&gt;
          &lt;td&gt;起初是为了解决全虚拟化效率不高的困难，它需要修改 OS，工作效率相对全虚拟化要高很多。Hypervisor 直接安装在物理机上，多个虚拟机在 Hypervisor 上运行。Hypervisor 实现方式一般是一个特殊定制的 Linux 系统。&lt;/td&gt;
          &lt;td&gt;- 架构更精简。&lt;br /&gt;- 在整体速度上有一定的优势。&lt;br /&gt;- 需要对 OS 进行修改，在用户体验方面比较麻烦。&lt;/td&gt;
          &lt;td&gt;- Xen&lt;br /&gt;- VMWare ESXi&lt;br /&gt;- 微软 Hyper-V&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;硬件辅助虚拟化&lt;/td&gt;
          &lt;td&gt;硬件辅助虚拟化是随着虚拟化技术的应用越来越广泛 lntl、AMD 等硬件厂商通过对硬件的改造来支持虚拟化技术。&lt;br /&gt;&lt;br /&gt;常用于优化全虚拟化和半虚拟化产品，像 VMware Workstation，它虽然属于全虚拟化，但它在 6.0 版本中引入了硬件辅助虚拟化技术，比如 Intel 的 VT-x 和 AMD 的 AMD-V。主流全虚拟化和半虚拟化产品都支持硬件辅助虚拟化。(VirtualBox,KVM,Xen 等)&lt;br /&gt;&lt;/td&gt;
          &lt;td&gt;辅助产品&lt;/td&gt;
          &lt;td&gt;- VT-x&lt;br /&gt;- AMD-V&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&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/12-21-14-f022c910d48931ce11223d247c888351-20230531122109-78f6d6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/12-21-14-f022c910d48931ce11223d247c888351-20230531122109-78f6d6.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;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/13-51-35-895d9c73dccc874815424d87298ef8bd-20230531135130-c79d43.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/13-51-35-895d9c73dccc874815424d87298ef8bd-20230531135130-c79d43.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;全虚拟化代表-kvm-和半虚拟化代表-xen-架构对比&#34;&gt;全虚拟化代表 KVM 和半虚拟化代表 Xen 架构对比&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;架构&lt;/th&gt;
          &lt;th&gt;描述&lt;/th&gt;
          &lt;th&gt;对比&lt;/th&gt;
          &lt;th&gt;示意图&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;全虚拟化：KVM&lt;/td&gt;
          &lt;td&gt;KVM(Kernel&amp;ndash;Based Virtual Machines) 是一个基于 Linux 内核的虚拟化技术，可以直接将 Linux 内核转换为 Hypervisor。.从而使得 Linuxp 内核能够直接管理虚拟机，直接调用 Linux 内核中的内存管理、进程管理子系统来管理虚拟机。&lt;/td&gt;
          &lt;td&gt;- 支持全虚拟化&lt;br /&gt;- 内置在内核中&lt;br /&gt;- 便于版本安装、升级、维护，性能高&lt;br /&gt;总结：KVM 平台架构侧重性能&lt;/td&gt;
          &lt;td&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/13-50-44-295215a975864579d54f5ea07685e119-20230531135039-2dafa3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/13-50-44-295215a975864579d54f5ea07685e119-20230531135039-2dafa3.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;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;半虚拟化：Xen&lt;/td&gt;
          &lt;td&gt;Xen:直接把操作系统内核改了，把 OS 改成一个轻量级 Hypervisor1 在里面运行了一个管理所有资源作资源调度的 Domain0。&lt;br /&gt;组成：由 Xen Hypervisor(虚拟化层)、Domin0(管理主机)、Domin U(用户虚&lt;br /&gt;拟机)&lt;br /&gt;&lt;/td&gt;
          &lt;td&gt;- 支持全虚拟化、半虚拟化&lt;br /&gt;- 需要对内核修改&lt;br /&gt;- 更新版本，Xen 需要重新编译整个内核隔离性好&lt;br /&gt;总结：Xen 平台架构侧重安全性&lt;/td&gt;
          &lt;td&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/14-18-51-ec8fcb01ac5cda397c51a883587850ea-20230531141846-6f42cf.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-18-51-ec8fcb01ac5cda397c51a883587850ea-20230531141846-6f42cf.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;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id=&#34;容器&#34;&gt;容器&lt;/h1&gt;
&lt;p&gt;容器：包装或装载物品的贮存器，利用一个开源的应用容器引擎，让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中，然后发布到任一 Liux 或 Windows 机器上，也可以实现虚拟化。相互之间不会有任何接口，实现 App 与操作系统的解耦。&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/14-49-30-c10e37d44495429910370b47eac70018-20230531144926-f7f9e5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-49-30-c10e37d44495429910370b47eac70018-20230531144926-f7f9e5.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;h2 id=&#34;主流容器技术&#34;&gt;主流容器技术&lt;/h2&gt;
&lt;p&gt;定义：Docker) 属于 Liux 容器的一种封装，提供简单易用的容器使用接口，他是目前最流行的 Linux 容器解决方案。
作用：将应用程序与该程序的依赖，&lt;strong&gt;打包在一个文件里&lt;/strong&gt;。运行这个文件，就会生成一个虚拟容器。程序在这个虚拟容器里运行，就好像在真实的物理机上运行一样。有了 Docker，就不用担心环境问题。&lt;/p&gt;
&lt;p&gt;组成：客户端 (Docker Client)、守护进程 (Docker Daemon)、镜像（Docker Image)、容器 (DockerContainer)、仓库（Docker Registry)&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/14-53-26-70a710bfca6ebb2d0197a8fb28a89b13-20230531145321-ce6e27.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-53-26-70a710bfca6ebb2d0197a8fb28a89b13-20230531145321-ce6e27.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;h2 id=&#34;容器和虚拟化的区别&#34;&gt;容器和虚拟化的区别&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;虚拟化&lt;/th&gt;
          &lt;th&gt;容器&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;隔离性强，有独立的 GUEST OS&lt;/td&gt;
          &lt;td&gt;共享内核和 OS，隔离性弱&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;虚拟化性能差 (&amp;gt;15%)&lt;/td&gt;
          &lt;td&gt;计算/存储无损耗，无 Guest0S 内存开销（200M)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;虚拟机镜像庞大（十几 G 几十 G),且实例化时不能共享&lt;/td&gt;
          &lt;td&gt;Docker 容器镜象 200300M，且公共基础镜象实例化时&lt;br /&gt;可以共享&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;虚拟机镜象缺乏统一标准&lt;/td&gt;
          &lt;td&gt;Docker 提供了容器应用镜象事实标准，OCI 推动进一步标&lt;br /&gt;准化&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;虚拟机创建慢 (&amp;gt;2 分钟)&lt;/td&gt;
          &lt;td&gt;秒级创建 (&amp;lt;10s)&lt;br /&gt;相当于建立索引&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;虚拟机启动慢 (&amp;gt;30s) 读文件逐个加载&lt;/td&gt;
          &lt;td&gt;秒级 (&amp;lt;1s，不含应用本身启动)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;资源虚拟化粒度低，单机 10~100 虚拟机&lt;/td&gt;
          &lt;td&gt;单机支持 1000+ 容器&lt;br /&gt;密度很高，适合大规模的部署&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id=&#34;计算虚拟化&#34;&gt;计算虚拟化&lt;/h1&gt;
&lt;p&gt;从服务器组建角度来看，计算虚拟化可分为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU 虚拟化：保障 CPU 资源的合理调度以及 VM 上的指令能够正常高效的执行。&lt;/li&gt;
&lt;li&gt;内存虚拟化：保障内存空间的合理分配、管理，隔离，以及高效可靠地使用。&lt;/li&gt;
&lt;li&gt;I/O 虚拟化：保障 VM 的 1O 隔离与正常高效的执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常见的计算服务架构有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenStack Nova&lt;/li&gt;
&lt;li&gt;阿里云 ECS&lt;/li&gt;
&lt;li&gt;腾讯云 CVM&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;nova&#34;&gt;Nova&lt;/h2&gt;
&lt;p&gt;OpenStack 是开源的云平台，通过不同的组件提供计算、存储、网络、数据库等多种云服务。其中计算服务由 Nova 组件提供，通过 nova-API 与其他组件通信，通过 nova-computex 对接不同的虚拟层提供计算虚拟化服务。&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/15-20-39-626c5b8808d188dd741630c2f871ac95-20230531152034-53e4f2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-20-39-626c5b8808d188dd741630c2f871ac95-20230531152034-53e4f2.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;创建实例流程：创建实例请求 nova-api，会唤醒 nova-database，请求刷新数据库。将请求给队列组件，nova-scheduler 从队列中取出请求，请求运行相对应的虚拟机。要运行不同的虚拟机，需要不同的平台支持（KVM，Xen,VMware）。虚拟机不能直接与数据库直接交互，需要通过 nova-conductor 转发。&lt;/p&gt;
&lt;h2 id=&#34;ecs&#34;&gt;ECS&lt;/h2&gt;
&lt;p&gt;云服务器 ECS(Elastic Compute Service) 是阿里云提供的基于 KVM 虚拟化的弹性计算服务，建立在阿里云飞天 (Apsara) 分布式操作系统上。
请求的主要调用流程为：OpenAPI、.业务层、控制系统、宿主机服务。&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/15-27-39-3057bca8eb97414d6d91c2201a895311-20230531152734-7f53e0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-27-39-3057bca8eb97414d6d91c2201a895311-20230531152734-7f53e0.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;h2 id=&#34;cvm&#34;&gt;CVM&lt;/h2&gt;
&lt;p&gt;云服务器 CVM(Cloud Virtual Machine)) 是腾讯提供的基于 KVM 虚拟化的弹性计算服务，建立在腾讯云分布式资源管理调度系统 VStation.上。
请求的主要调用流程为：API Server、.VStation、服务器集群。&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/15-28-22-2ee550326d62c2552c94cc2ef677e5a3-20230531152818-41688d.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-28-22-2ee550326d62c2552c94cc2ef677e5a3-20230531152818-41688d.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;h1 id=&#34;cpu-虚拟化&#34;&gt;CPU 虚拟化&lt;/h1&gt;
&lt;p&gt;在物理机（宿主机）中通过线程或进程这种纯软件方式模拟出假的 CPU，通过 CPU 虚拟化就可以将一个物理 CPU 发给不同的虚拟机使用。&lt;/p&gt;
&lt;p&gt;虚拟出来的每颗 CPU 实际上就是一个线程或者进程，因此物理 CPU 核数要大于虚拟 CPU 总核数。&lt;/p&gt;
&lt;h2 id=&#34;cpu-qosquality-of-service-服务质量&#34;&gt;CPU QoS(Quality of Service) 服务质量&lt;/h2&gt;
&lt;p&gt;QoS 用来控制虚拟机使用 CPU 资源量的大小。
CPU 资源限额：控制虚拟机占用物理资源使用的上限。
CPU 资源份额：定义了多台虚拟机在竞争物理 CPU 资源时，需按比例分配计算资源。
CPU 预留资源：定义了多台虚拟机在竞争物理 CPU 时，每台虚拟机最低分配的计算资源。&lt;/p&gt;
&lt;h2 id=&#34;numa&#34;&gt;NUMA&lt;/h2&gt;
&lt;p&gt;NUMA(Non Uniform Memory Access Architecture) 非统一内存访问体系结构，提高物理服务器性能的一种技术。&lt;/p&gt;
&lt;p&gt;将物理服务器的 CPU 和内存资源分到多个 node 上，node 内的内存访问效率最高。&lt;/p&gt;
&lt;p&gt;NUMA 保证了一个 VM 上的 VCPU 尽量分配到同一个 node 中的物理 CPU 上，如果一台 VM 的 VCPU 跨 node 访问内存的话，访问的延时肯定增加。&lt;/p&gt;
&lt;h1 id=&#34;内存虚拟化&#34;&gt;内存虚拟化&lt;/h1&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/15-53-33-a6d733a49ff6057c8e528cad5867f015-20230531155328-769d51.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-53-33-a6d733a49ff6057c8e528cad5867f015-20230531155328-769d51.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;h2 id=&#34;虚拟化类型-1&#34;&gt;虚拟化类型&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;全虚拟化&lt;/th&gt;
          &lt;th&gt;半虚拟化&lt;/th&gt;
          &lt;th&gt;硬件辅助虚拟化&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;为每个 VM 维护一个影子页表记录虚拟化内存与物理内存的映射关系，VMM 将影子页表提交给 CPU 的内存管理单元 MMU 进行地址转换，VM 的页表无需改动。&lt;/td&gt;
          &lt;td&gt;采用页表写入法，为每个 VM 创建&lt;br /&gt;一个页表并向虚拟化层注册。VM&lt;br /&gt;运行过程中 VMM 不断管理和维护&lt;br /&gt;该页表，确保 VM 能直接访问到合&lt;br /&gt;适的地址。&lt;/td&gt;
          &lt;td&gt;EPT/NPT 是内存管理单元 MMU 的&lt;br /&gt;扩展，CPU 硬件一个特性，通过&lt;br /&gt;硬件方式实现 GuestOS 物理内存地&lt;br /&gt;址到主机物理内存地址的转换，系&lt;br /&gt;统开销更低，性能更高。&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;内存复用技术&#34;&gt;内存复用技术&lt;/h2&gt;
&lt;p&gt;内存复用是指在服务器物理内存一定的情况下，通过综合运用内存复用技术对内存进行分时复用。
内存复用技术有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内存气泡：虚拟化层将较空闲 VM 内存，分配给内存使用较高的虚拟机。内存的回收和分配由虚拟化层实现，虚拟机上的应用无感知，提高物理内存利用率。（虚拟机分配的内存不超过物理机总内存）&lt;/li&gt;
&lt;li&gt;内存交换：将外部存储虚拟成内存给 VM 使用，将 VM 上长时间未访问的数据存放到外部存储上，建立映射关系。VM 再次访问这些数据是通过映射在与内存上的数据进行交换。&lt;/li&gt;
&lt;li&gt;内存共享：VM 只对共用的内存（共享数据内容为零的内存页）做只读操作，有写操作时运用写时复制 (VM 有写操作时，开辟另一空间，并修改映射)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;io-虚拟化&#34;&gt;IO 虚拟化&lt;/h1&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;全虚拟化&lt;/th&gt;
          &lt;th&gt;半虚拟化&lt;/th&gt;
          &lt;th&gt;Pass-Thorugh（直通）&lt;/th&gt;
          &lt;th&gt;硬件辅助虚拟化&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;通过软件模拟的形式模拟 O 设备，&lt;strong&gt;不需要硬件支持&lt;/strong&gt;，对虚拟机的操作系统也不需要修改（因为模拟的都是一个常见的硬件网卡，如 IntelE1000，主流操作系统一般都自带这些驱动，因此默认情下虚拟机不需要再安装驱动。缺点就是&lt;strong&gt;性能差&lt;/strong&gt;。&lt;/td&gt;
          &lt;td&gt;由 Hypervisor 提供资源调用接口。VM 通过特定的调用接口与 Hypervisor 通信，完成获取完整/O 资源控制操作。(需修改内核及驱动程序，存在移植性和适用性问题，导致其使用受限。)&lt;/td&gt;
          &lt;td&gt;Hypervisor] 直接把硬件 PCI 设备分配给虚拟独占使用，性能挡当然好啦。但是&lt;strong&gt;浪遗硬件设备&lt;/strong&gt;，且&lt;strong&gt;配置复杂&lt;/strong&gt;，首先需要在 hypervisor 指定通过 PClid 方式分配给指定的虚拟机，然后虚拟机再识别到设备再安装驱动来使用。&lt;/td&gt;
          &lt;td&gt;通过硬件的辅助可以让虚拟机直接访问物理设备，而不需要通过 VMM。最常用的就是 SR-lOV(Single Root I/OVirtualizmion)单根 I/O 虚拟化标准，该技术可以直接虚拟出 128-512 网卡，可以让虚拟机都拿到一块独立的网卡，直接使用/O 资源。&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id=&#34;常见集群策略&#34;&gt;常见集群策略&lt;/h1&gt;
&lt;h2 id=&#34;集群简介&#34;&gt;集群简介&lt;/h2&gt;
&lt;p&gt;集群是一种计算机系统，通过一组计算机或服务器的软硬件连接起来高度紧密地协作完成计算工作。在客户端看来为其提供服务的只有一台设备，实际上它是一群设备的集合，只不过这些设备提供的服务一样。&lt;/p&gt;
&lt;p&gt;集群系统中单个计算机通常称为节点，通过局域网连接，利用多个计算机进行并行计算获得很高计算速度，也可以用多个计算机做备份提高可靠性。（并行计算技术）&lt;/p&gt;
&lt;h2 id=&#34;ha-策略&#34;&gt;HA 策略&lt;/h2&gt;
&lt;p&gt;HA(High Availability) 高可用性，一种让服务中断尽可能少的技术。将多台主机组建成一个故障转移集群，运行在集群上的服务（或 VM) 不会因为单台主机的故障而停止。&lt;/p&gt;
&lt;p&gt;提升故障恢复速度，降低业务中断时间、保障业务连续性、实现一定的系统自维护。&lt;/p&gt;
&lt;h2 id=&#34;drs-策略&#34;&gt;DRS 策略&lt;/h2&gt;
&lt;p&gt;DRS(Dynamic resource scheduling) 动态资源调度，根据对资源池资源负载的动态监控，合理触发均匀分配规侧，实现资源池中的物理服务器之间重新分配资源，达到&lt;strong&gt;负载均衡、消峰填谷&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;当物理服务器上负载过大时，通过 DRS 将虚拟机迁移到其他负载较轻的物理服务器上。当虚拟机遇到负载增大时，DRS 将为资源池中的物理服务器重新分配虚拟机可使用资源，在多个虚拟机之间智能地分配可用资源。&lt;/p&gt;
&lt;h2 id=&#34;dpm-策略&#34;&gt;DPM 策略&lt;/h2&gt;
&lt;p&gt;DPM（Distributed power management) 分布式电源管理，用于业务较轻时，把虚拟机动态“集中”到集群中的少部分主机上，将其他主机待机，节省电力消耗，等业务量较大时，再重新唤醒之前待机的主机。&lt;/p&gt;
&lt;p&gt;执行 DPM 策略的前提是开启 DRS 策略，即集群必须先设置好 DRS 策略，才能设置 DPM 策略。&lt;/p&gt;
&lt;h1 id=&#34;存储虚拟化&#34;&gt;存储虚拟化&lt;/h1&gt;
&lt;h2 id=&#34;存储类型&#34;&gt;存储类型&lt;/h2&gt;
&lt;p&gt;常用的存储类型有：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;本地磁盘&lt;/th&gt;
          &lt;th&gt;DAS&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;简介&lt;/td&gt;
          &lt;td&gt;云计算虚拟化场景下的本地磁盘是指使用服务器本地的磁盘资源，经过 RAD(磁盘阵列) 化后提供给虚拟化平台进行使用。&lt;/td&gt;
          &lt;td&gt;DAS(Direct-Attached Storage) 直连式存储：一个存储设备与使用存储空间的服务器&lt;br /&gt;直接相连的架构。DAS 为服务器提供块级的存储服务。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;优点&lt;/td&gt;
          &lt;td&gt;- 使用方便&lt;br /&gt;- 无共享框架&lt;br /&gt;&lt;/td&gt;
          &lt;td&gt;- 多个磁盘合并成一个逻辑磁盘，满足海量存储的需求&lt;br /&gt;- 可实现应用数据和操作系统的分离&lt;br /&gt;- 能提高存取性能&lt;br /&gt;- 实施简单&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;缺点&lt;/td&gt;
          &lt;td&gt;- 对跨服务器来说没有备份、冗余机制&lt;/td&gt;
          &lt;td&gt;- 服务器发生故障，数据不可访问&lt;br /&gt;- 传输距离短&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;NAS&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;SAN&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;简介&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;NAS(Network Attached Storage) 网络附加存储：将分布、独立的数据进行整合，集&lt;br /&gt;中化管理，以便对不同主机和应用服务器进行访问的技术。&lt;br /&gt;NAS 将存储设备连接到现有的网络上来提供数据和文件服务。&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;SAN(Storage Area Networks) 存储区域网络：是一种高速的、专门用于存储操作的网&lt;br /&gt;络，通常独立于计算机局域网。&lt;br /&gt;提供在主机和存储系统之间数据传输，网络内部数据传输的速率快。&lt;br /&gt;常见架构有 FC SAN、IP SAN。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;优点&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;- 支持快照等高级特性&lt;br /&gt;- 集中存储&lt;br /&gt;- 提供安全集成环境（用户认证和授权)&lt;br /&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;- 存储容量利用率高&lt;br /&gt;- 兼容性高&lt;br /&gt;- 传输距离远&lt;br /&gt;- 高带宽&lt;br /&gt;- 主机、存储设备可以独立扩展&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;缺点&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;- 传输速率低&lt;br /&gt;- 前期安装和设备成本高&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;- 成本高、复杂&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;云存储基本概念&#34;&gt;云存储基本概念&lt;/h2&gt;
&lt;p&gt;存储资源：表示实际的物理存储设备，例如 DAS(直连存储)、NAS(网络附加存储)、SA(存储区域网络) 等。&lt;/p&gt;
&lt;p&gt;存储设备：表示存储资源中的管理单元，例如本地磁盘、LUN(逻辑单元号)、Storage 存储池、NAS 共享目录等。&lt;/p&gt;
&lt;p&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/18-55-45-5599a99b10ac1c9c3ad6cc69bc1c9c67-20230531185540-d75052.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-55-45-5599a99b10ac1c9c3ad6cc69bc1c9c67-20230531185540-d75052.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;h2 id=&#34;创建虚拟存储的流程&#34;&gt;创建虚拟存储的流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在主机软件界面添加存储资源 (SAN、DAS 等)，对主机的启动进行配置。&lt;/li&gt;
&lt;li&gt;主机关联存储资源后，进行扫描存储设备（本地磁盘、LUN 等），将具体的设备扫描到主机上。&lt;/li&gt;
&lt;li&gt;主机在选择存储设备，进行数据存储的添加，并进行虚拟化。&lt;/li&gt;
&lt;li&gt;最后对虚拟化好的数据存储进行创建卷等操作。&lt;/li&gt;
&lt;/ol&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/18-58-26-0207d3cfacb381f75c3d8f52b3251639-20230531185822-ac2437.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-58-26-0207d3cfacb381f75c3d8f52b3251639-20230531185822-ac2437.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;h2 id=&#34;存储模式&#34;&gt;存储模式&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;非虚拟化存储&lt;/th&gt;
          &lt;th&gt;虚拟化存储&lt;/th&gt;
          &lt;th&gt;裸设备映射&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;传统的存储模式，就是把磁盘进行分区，分割成不同的逻辑卷，每一个逻辑卷可以给到虚拟机进行使用。&lt;/td&gt;
          &lt;td&gt;将不同的存储设备、磁盘进行格式化，格式化的目的是屏蔽底层存储设备的能力、接口&lt;br /&gt;协议等差异性，将各种存储资源转化为统一管理的数据存储资源。&lt;/td&gt;
          &lt;td&gt;将磁盘直接给到虚拟机使用，让虚拟机直接处理调用存储的命令（直接访问磁盘）中间虚拟化层不再对其进行任何干预（卷都不需要创建）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;特点&lt;/td&gt;
          &lt;td&gt;- 性能好 (不再有中间的虚拟化层，VM 读写直接在磁盘上)、速度快、效率高。&lt;br /&gt;&lt;br /&gt;- 支持的存储功能少（不支持快照、精简配置等）&lt;br /&gt;&lt;/td&gt;
          &lt;td&gt;- 支持多种存储功能（快照、精简磁盘、磁盘扩容、存储热迁移等)。&lt;br /&gt;- 性能不高（没有非虚拟化存储好）&lt;br /&gt;&lt;/td&gt;
          &lt;td&gt;速度快（三种模式中最快）、性能好。&lt;br /&gt;支持的存储功能少（不支持快照、精简配置等），仅支持部分操作系统的虚拟机使用、数据存储只能整块当做裸设备映射的磁盘使用，不可分割。&lt;br /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&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/19-05-13-74eb9473a84f7cc8d591b18d5b0ec7ee-20230531190509-4a3908.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-05-13-74eb9473a84f7cc8d591b18d5b0ec7ee-20230531190509-4a3908.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;/td&gt;
          &lt;td&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/19-08-34-98b4e748d8c582b1e74f109fe228fb9c-20230531190829-8006f5.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-08-34-98b4e748d8c582b1e74f109fe228fb9c-20230531190829-8006f5.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;/td&gt;
          &lt;td&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/19-12-22-a0c2ce10d5ab79474d217028ed20056b-20230531191217-563c1b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-12-22-a0c2ce10d5ab79474d217028ed20056b-20230531191217-563c1b.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;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;存储虚拟化方法&#34;&gt;存储虚拟化方法&lt;/h2&gt;
&lt;h3 id=&#34;基于主机的存储虚拟化&#34;&gt;基于主机的存储虚拟化&lt;/h3&gt;
&lt;p&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/08-36-54-e70134a6efd0d031785dc81eeb922c38-20230601083649-a5e4db.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-36-54-e70134a6efd0d031785dc81eeb922c38-20230601083649-a5e4db.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;若多个主机服务器需要访问同一个磁盘阵列时，可采用基于存储设备虚拟化。虚拟化的工作在阵列控制器上完成，将一个阵列上的存储容量划分多个存储空间 (LUN),供不同的主机系统访问。主要用在同一存储设备内部，进行数据保护和数据迁移。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优点是与主机无关，不占用主机资源，数据管理功能丰富。&lt;/li&gt;
&lt;li&gt;软件运行于存储设备中专门的嵌入式系统上。&lt;/li&gt;
&lt;li&gt;从与 SAN 连接的存储上创建虚拟卷。&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/08-41-22-f6e0c1760cda24ae776bfcf3c57c2af5-20230601084118-73285e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-41-22-f6e0c1760cda24ae776bfcf3c57c2af5-20230601084118-73285e.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;通过在存储区域网 (SAN) 中添加虚似化引擎实现的，主要用于异构存储系统的整合和统数据管理。&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/08-42-36-66183a6a63e0ab9ed777c2ff96834109-20230601084232-7fdd98.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-42-36-66183a6a63e0ab9ed777c2ff96834109-20230601084232-7fdd98.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;h2 id=&#34;存储虚拟化的功能&#34;&gt;存储虚拟化的功能&lt;/h2&gt;
&lt;p&gt;存储虚拟化可以提高硬件资源的使用效率，简化系统管理的复杂度，增强云存储平台的可靠性。可以通过以下几种技术实现：&lt;/p&gt;
&lt;h3 id=&#34;精简磁盘和空间回收&#34;&gt;精简磁盘和空间回收&lt;/h3&gt;
&lt;p&gt;精简磁盘和空间回收用于提高存储资源的使用效率、减小虚拟机未使用空间在主机上占用率过大的问题。&lt;/p&gt;
&lt;p&gt;用户用多少分配多少空间（自动分配）&lt;br /&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/09-07-37-a97805053794b23776d5085409611896-20230601090732-4ff3a3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-07-37-a97805053794b23776d5085409611896-20230601090732-4ff3a3.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;ul&gt;
&lt;li&gt;指定数据集合的一个完全可用拷贝，该拷贝包括相应数据在某个时间点（拷贝开始的时间点) 的映像。&lt;/li&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;记录了虚拟机在某一时间点的内容和状。&lt;/li&gt;
&lt;li&gt;恢复虚拟机快照可以使虚拟机多次快速恢复到某一时间点。&lt;/li&gt;
&lt;li&gt;快照包含磁盘内容、虚拟机配置信息、内存数据。&lt;/li&gt;
&lt;li&gt;多次快照之间&lt;strong&gt;保存差量数据&lt;/strong&gt;，节约存储空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;快照方式介绍&#34;&gt;快照方式介绍&lt;/h4&gt;
&lt;p&gt;创建快照时会生成一个新的差分卷，虚拟机会挂载这个差分卷作为磁盘文件。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ROW 写时重定向&lt;/li&gt;
&lt;li&gt;COW 写时拷贝&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/09-13-17-381617aa029cc392aa3ae573efdfb8aa-20230601091313-a0828c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-13-17-381617aa029cc392aa3ae573efdfb8aa-20230601091313-a0828c.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;h4 id=&#34;快照链介绍&#34;&gt;快照链介绍&lt;/h4&gt;
&lt;p&gt;对虚拟机进行多次的快照操作，这些多次的快照操作形成快照链。&lt;/p&gt;
&lt;p&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/09-35-59-7cc5ae355ea7c376e94b60b952e0284e-20230601093555-863fc8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-35-59-7cc5ae355ea7c376e94b60b952e0284e-20230601093555-863fc8.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;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/09-19-47-654bac5b7dfa915bea5c572df6f58bbd-20230601091942-a6a3d7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-19-47-654bac5b7dfa915bea5c572df6f58bbd-20230601091942-a6a3d7.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;功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将虚拟机的磁盘从一个数据存储迁移到另一个数据存储。可以将虚拟机的所有磁盘整体迁移，也可以单个磁盘分别迁移。&lt;/li&gt;
&lt;li&gt;虚拟机的快照可以一起迁移，虚拟机开启或者关闭时都可以迁移。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&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/09-32-38-a14bb4f8ed8bac86585c62877564bb65-20230601093233-54d851.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-32-38-a14bb4f8ed8bac86585c62877564bb65-20230601093233-54d851.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;h1 id=&#34;网络虚拟化&#34;&gt;网络虚拟化&lt;/h1&gt;
&lt;p&gt;虚拟化是对所有 T 资源的虚拟化，提高物理硬件的灵活性及利用效率。云计算中的计算和存储资源分别由计算虚拟化和存储虚拟化提供，而网络作为 T 的重要资源也有相应的虚拟化技术，网络资源由网络虚拟化提供。&lt;/p&gt;
&lt;p&gt;网络是由各种设备组成，有传统的物理网络，还有运行在服务器上看不到的虚拟网络。如何呈现和管理它们将是网络虚拟化的首要目标。&lt;/p&gt;
&lt;p&gt;将物理网络虚拟出多个相互隔离的虚拟网络（逻辑网络），从而使得不同用户之间使用独立的网络资源，从而提高网络资源利用率，实现弹性的网络。&lt;/p&gt;
&lt;p&gt;VLAN 就是一种网络虚拟化，在原有网络基础上通过 VLAN Tag:划分出多个广播域。&lt;/p&gt;
&lt;p&gt;网络虚拟化保障我们创建出来的虚拟机可以正常  通信、访问网络。&lt;/p&gt;
&lt;p&gt;节省物理主机的网卡设备资源，并且可以提供应用的虚拟网络所需的 L2 一 L7 层网络服务。&lt;/p&gt;
&lt;p&gt;网络虚拟化软件提供逻辑上的交换机和路由器 (L2-L3),逻辑负载均衡器，逻辑防火墙 (L4-L7) 等，且可以以任何形式进行组装，从而为虚拟机提供一个完整的 L2-L7 层的虚拟网络拓扑。&lt;/p&gt;
&lt;h2 id=&#34;物理网络包含的设备&#34;&gt;物理网络包含的设备&lt;/h2&gt;
&lt;p&gt;路由器：工作在网络层，连接两个不同的网络。&lt;/p&gt;
&lt;p&gt;二层交换机：工作在数据链路层，转发数据。&lt;/p&gt;
&lt;p&gt;三层交换机：工作在网络层，结合了部分路由和交换机的功能。&lt;/p&gt;
&lt;p&gt;服务器网卡：提供通信服务。&lt;/p&gt;
&lt;h1 id=&#34;虚拟化中的网络架构&#34;&gt;虚拟化中的网络架构&lt;/h1&gt;
&lt;p&gt;网卡虚拟化方法有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;软件网卡虚拟化&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主要通过软件控制各个虚拟机共享同一块物理网卡实现。软件虚拟出来的网卡可以有单独的 MAC 地址、IP 地址。&lt;/li&gt;
&lt;li&gt;所有虚拟机的虚拟网卡通过虚拟交换机以及物理网卡连接至物理交换机。虚拟交换机负责将虚拟机上的数据报文从物理网口转发出去。&lt;/li&gt;
&lt;/ul&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/14-16-53-540de968dd6e4e7cc2e45e8b1b1df1a1-20230601141648-d238fb.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-16-53-540de968dd6e4e7cc2e45e8b1b1df1a1-20230601141648-d238fb.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;ul&gt;
&lt;li&gt;
&lt;p&gt;硬件网卡虚拟化&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主要用到的技术是单根 I/O 虚拟化 (Single Root/O Virtulization,SR-lOV),就是 I/O 直通技术，通过硬件的辅助可以让虚拟机直接访问物理设备，而不需要通过 VMM。该技术可以直接虚拟出 128-512 网卡，可以让虚拟机都拿到一块独立的网卡，直接使用/O 资源。SR-OV 能够让网络传输绕过软件模拟层，直接分配到虚拟机，这样就降低了软件模拟层中的/○ 开销。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;交换机虚拟化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OVS(Open vSwitch) 开放虚拟化软件交换机，是一款基于软件实现的开源虚拟以太网交换机，使用开源 Apache2.0 许可协议，主要用于虚拟机 VM 环境。与众多开源的虚拟化平台相整合（支持 Xen、KVM 及 VirtualBox 多种虚拟化技术），主要有两个作用：传递虚拟机之间的流量，实现虚拟机和外界网络的通信。&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/14-30-39-aaaaed14c501175ffa51df2735b09d2a-20230601143034-d4f7f6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-30-39-aaaaed14c501175ffa51df2735b09d2a-20230601143034-d4f7f6.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;h2 id=&#34;虚拟化中数据的转发路径&#34;&gt;虚拟化中数据的转发路径&lt;/h2&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/18-59-54-9abbabcce9a6864d7c990eba310e171f-20230601185949-5239e7.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-59-54-9abbabcce9a6864d7c990eba310e171f-20230601185949-5239e7.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;h2 id=&#34;链路虚拟化&#34;&gt;链路虚拟化&lt;/h2&gt;
&lt;p&gt;VPC(Virtual Port Channel) 虚链路聚合，是最常见的二层虚拟化技术。&lt;/p&gt;
&lt;p&gt;链路聚合将多个物理端口捆绑在一起，虚拟成为一个逻辑端口。但传统链路聚合不能跨设备，VPC 很好解决了这个问题，既可以跨设备，又可以增加链路带宽、实现链路层的高可用性。&lt;/p&gt;
&lt;p&gt;隧道协议 (Tunneling Protocol)：指通过隧道协议使多个&lt;strong&gt;不同协议的网络实现互联&lt;/strong&gt;。使用隧道传递的数据可以是不同协议的数据帧或包。隧道可以将数据流强制送到特定的地址，并隐藏中间节点的网络地址，还可根据需要，提供对数据加密的功能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GRE(Generic Routing Encapsulation) 通用路由封装。&lt;/li&gt;
&lt;li&gt;IPsec(Internet Protocol Security)Internett 协议安全。&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/19-34-04-8dc54676f16ac517fa503ded64966d24-20230601193359-139761.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-34-04-8dc54676f16ac517fa503ded64966d24-20230601193359-139761.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;h2 id=&#34;虚拟网络&#34;&gt;虚拟网络&lt;/h2&gt;
&lt;p&gt;虚拟网络 (Virtual Network):是由虚拟链路组成的网络。&lt;/p&gt;
&lt;p&gt;虚拟网络节点之间的连接并不使用物理线缆连接，而是依靠特定的虚拟化链路相连。&lt;/p&gt;
&lt;p&gt;典型的虚拟网络包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;层叠网络（虚拟二层延伸网络）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;层叠网络 (Overlay Network)：在现有网络的基础上搭建另外一种网络&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;层叠网络允许对没有引 P 地址标识的目的主机路由信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;层叠网络可以充分利用现有资源，在不增加成本的前提下，提供更多的服务。（比如 ADSL Internet 接入线路就是基于已经存在的 PSTN 网络实现)&lt;/p&gt;
&lt;/li&gt;
&lt;li&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/19-36-27-c84d66208097477a09c6ce6b162bfacd-20230601193622-21ef48.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-36-27-c84d66208097477a09c6ce6b162bfacd-20230601193622-21ef48.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;/li&gt;
&lt;li&gt;
&lt;p&gt;典型技术：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VXLAN(Virtual eXtensible Local Area Network) 虚拟扩展局域网：很好地解决了现有 VLAN 技术无法满足大二层网络需求的问题。&lt;/li&gt;
&lt;li&gt;VXLAN 技术是一种大二层的虚拟网络技术。&lt;/li&gt;
&lt;li&gt;原理是引入一个 UDP 格式的外层隧道作为数据链路层，而原有数据报文内容作为隧道净荷加以传输。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;VPN 网络&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;VPN(Virtual Private Network) 虚拟专用网：是一种常用于连接中、大型企业或团体与团体间的私人网络的通信方法。&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;li&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/19-39-40-4bc357c1f760435fb8d1a980f230527c-20230601193935-06e11c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-39-40-4bc357c1f760435fb8d1a980f230527c-20230601193935-06e11c.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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h1 id="云计算服务类型">云计算服务类型</h1>
<p>传统架构=&gt;Iaas=&gt;Paas=&gt;Saas</p>
<p>自己烧饭=&gt; 叮咚买菜=&gt; 美团外卖=&gt; 餐厅吃饭</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/09-36-45-b25eafa700db3dce390dab2aeb3af4f9-20230531093640-f17dbe.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-36-45-b25eafa700db3dce390dab2aeb3af4f9-20230531093640-f17dbe.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>
<h1 id="云计算部署形式以及应用">云计算部署形式以及应用</h1>
<table>
  <thead>
      <tr>
          <th>类型</th>
          <th>描述</th>
          <th>优点</th>
          <th>缺点</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>私有云</td>
          <td>利用已有设备自我构建，云端资源只给内部人员使用。</td>
          <td>安全性高</td>
          <td>维护成本高</td>
      </tr>
      <tr>
          <td>社区云、行业云</td>
          <td>为特定行业构建共享基础设施的云。</td>
          <td>有一套用户体系</td>
          <td>维护成本高</td>
      </tr>
      <tr>
          <td>公有云</td>
          <td>构建大型基础设施云出租给公众。</td>
          <td>用户来说成本低，服务多</td>
          <td>安全性低</td>
      </tr>
      <tr>
          <td>混合云</td>
          <td>两种或者两种以上的云组成的云服务</td>
          <td>敏捷，灵活，降低成本</td>
          <td>兼容性问题</td>
      </tr>
  </tbody>
</table>
<h2 id="应用">应用</h2>
<p>存储云、医疗云、教育云、企业云、金融云、游戏云、桌面云</p>
<h1 id="关键技术">关键技术</h1>
<h2 id="虚拟化">虚拟化</h2>
<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/10-28-09-8bfe3f3fcd50903c6f72132d36c19f3e-20230531102804-d53750.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-28-09-8bfe3f3fcd50903c6f72132d36c19f3e-20230531102804-d53750.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>
<h2 id="分布式存储">分布式存储</h2>
<p>将数据存储在不同的物理设备中。这种模式不仅摆脱了硬件设备的限制，同时<strong>扩展性更好</strong>，能够快速响应用户需求的变化（整合存储资源提供动态可伸缩资源池的分布式存储技术)</p>
<h2 id="数据中心联网">数据中心联网</h2>
<p>虚拟机之间需要实时同步大量的数据，产生大量东西流量。</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/10-38-24-9e7878e87965a767076d4071dd0e354f-20230531103820-7087aa.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-38-24-9e7878e87965a767076d4071dd0e354f-20230531103820-7087aa.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>
<h2 id="并行编程">并行编程</h2>
<p>在并行编程模式下，并发处理、容错、数据分布、负载均衡等细节都被抽象到一个函数库中，通过统一接口，用户大尺度的计算任务被自动并发和分布执行，即将一个任务自动分成多个子任务，并行地处理海量数据。</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/16-17-02-45f21028b6e199dccd616053e8e43db8-20230602161656-9db753.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-17-02-45f21028b6e199dccd616053e8e43db8-20230602161656-9db753.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>
<h2 id="体系结构">体系结构</h2>
<p>云计算平台体系结构由用户界面、服务目录、管理系统、部署工具、监控和服务器集群组成。</p>
<h2 id="自动化部署">自动化部署</h2>
<p>对云资源进行自动化部署指的是基于脚本调节的基础上实现不同厂商对于设备工具的自动配置，用以减少人机交互比例、提高应变效率，避免超负荷人工操作等现象的发生，最终推进智能部署进程。</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/08-46-31-bc2856698d6f9b760f7cad0b1bff7ef1-20230605084626-c49970.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-46-31-bc2856698d6f9b760f7cad0b1bff7ef1-20230605084626-c49970.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>
<h1 id="云服务提供商">云服务提供商</h1>
<p>亚马逊云、腾讯云、阿里云、百度云、华为云</p>
<p>技术架构：开源（Xen,KVM），Vmware，微软 hyper-v，阿里飞天 Apsara</p>
<p>开源云管理平台：OpenStack</p>
<h1 id="虚拟化简介">虚拟化简介</h1>
<p>虚拟化：一种计算机资源管理技术，将各种 T 实体资源抽象、转换成另一种形式的技术都是虚拟化。
作用：通过该技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机，每个逻辑计算机可运行不同的操作系统，并且应用程序都可以在相互独立的空间内运行而互不影响，从而<strong>显著提高计算机的工作效率</strong>。</p>
<p>从行业数据互相关联的角度来说，云计算是极度依赖虚拟化的。但虚拟化并非云计算，云计算也并非虚拟化。<strong>虚拟化只是云计算的核心技术，但并非云计算的核心关注点</strong>。</p>
<p>云计算是一种服务。虚拟化是云计算的技术基础。</p>
<h2 id="虚拟化相关的几个概念">虚拟化相关的几个概念</h2>
<p>Guest OS:运行在虚拟机之上的 OS
Guest Machine:虚拟出来的虚拟机
VMM:虚拟机监控器，即虚拟化层 (Virtual Machine Monitor,VMM)
Host OS:运行在物理机之上的 OS
Host Machine:物理机</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/12-08-35-2a578c6b5ecfe26034e87a65aafd21da-20230531120830-e26a24.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/12-08-35-2a578c6b5ecfe26034e87a65aafd21da-20230531120830-e26a24.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>
<h2 id="虚拟化类型">虚拟化类型</h2>
<table>
  <thead>
      <tr>
          <th>虚拟化类型</th>
          <th>描述</th>
          <th>特点</th>
          <th>案例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>寄居虚拟化（Type2）</td>
          <td>在主机（宿主）操作系统上安装和运行虚拟化程序</td>
          <td>- 简单、易于实现。<br />- 安装和运行应用程序依赖于主机操作系统对设备的支持。<br />- 有两层 OS，管理开销较大，性能损耗大。<br />- 虚拟机对各种物理设备 (cpu、内存、硬盘等) 的调用，都通过虚拟化层和宿主机的 OS 一起协调才能完成。</td>
          <td>- Vmware<br />- VirturalBox<br /></td>
      </tr>
      <tr>
          <td>裸金属虚拟化 (Type1)</td>
          <td>直接将 VMM 安装在硬件设备上，VMM 在这种模式下又叫做 Hypervisor，虚拟机有指令要执行时，Hypervisors 会接管该指令，模拟相应的操作。</td>
          <td>- 不依赖于操作系统。<br />- 支持多种操作系统，多种应用。<br />- 依赖虚拟层内核和服务器控制台进行管理。<br />- 需要对虚拟层的内核进行开发（难度大）。</td>
          <td>- VMware ESX<br />- Xen<br />- 华为 FusionSphere<br /></td>
      </tr>
      <tr>
          <td>混合虚拟化</td>
          <td>在一个现有的正常操作系统下<strong>安装一个内核模块</strong>，内核拥有虚拟化能力。(相当于寄居与裸金属的混合)</td>
          <td>- 相对于寄居虚拟化架构，性能高。<br />- 相对于裸金属虚拟化架构，不需要开发内核。<br />- 可支持多种操作系统。<br />- 需底层硬件支持虚拟化扩展功能。</td>
          <td>- KVM</td>
      </tr>
  </tbody>
</table>
<p>寄居虚拟化（Type2）</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/14-24-43-35c85e077a5dc39f5f17e5a2ba879e16-20230602142439-ca0fee.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-24-43-35c85e077a5dc39f5f17e5a2ba879e16-20230602142439-ca0fee.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>裸金属虚拟化 (Type1)</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/14-24-29-4e00e51ef3d07ece5d29fa6a297b2f5b-20230602142424-f05f39.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-24-29-4e00e51ef3d07ece5d29fa6a297b2f5b-20230602142424-f05f39.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>
<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/14-24-07-e0079492f1d08c44e5c5bf88944f6e42-20230602142402-80ad49.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-24-07-e0079492f1d08c44e5c5bf88944f6e42-20230602142402-80ad49.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>
<h2 id="虚拟化层架构">虚拟化层架构</h2>
<table>
  <thead>
      <tr>
          <th>架构</th>
          <th>描述</th>
          <th>特点</th>
          <th>典型</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>全虚拟化</td>
          <td>即所抽象的 VM 具有完全的物理特性，虚拟化层负责捕获 CPU 指令，为指令访问硬件充当媒介。</td>
          <td>- OS 无需修改。<br />- 速度和功能都非常不错，使用非常简单。<br />- 移植性好。</td>
          <td>- VMware<br />- KVM<br />- Virtualbox<br />- Virtual PC<br /></td>
      </tr>
      <tr>
          <td>半虚拟化</td>
          <td>起初是为了解决全虚拟化效率不高的困难，它需要修改 OS，工作效率相对全虚拟化要高很多。Hypervisor 直接安装在物理机上，多个虚拟机在 Hypervisor 上运行。Hypervisor 实现方式一般是一个特殊定制的 Linux 系统。</td>
          <td>- 架构更精简。<br />- 在整体速度上有一定的优势。<br />- 需要对 OS 进行修改，在用户体验方面比较麻烦。</td>
          <td>- Xen<br />- VMWare ESXi<br />- 微软 Hyper-V<br /></td>
      </tr>
      <tr>
          <td>硬件辅助虚拟化</td>
          <td>硬件辅助虚拟化是随着虚拟化技术的应用越来越广泛 lntl、AMD 等硬件厂商通过对硬件的改造来支持虚拟化技术。<br /><br />常用于优化全虚拟化和半虚拟化产品，像 VMware Workstation，它虽然属于全虚拟化，但它在 6.0 版本中引入了硬件辅助虚拟化技术，比如 Intel 的 VT-x 和 AMD 的 AMD-V。主流全虚拟化和半虚拟化产品都支持硬件辅助虚拟化。(VirtualBox,KVM,Xen 等)<br /></td>
          <td>辅助产品</td>
          <td>- VT-x<br />- AMD-V<br /></td>
      </tr>
  </tbody>
</table>
<p>全虚拟化</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/12-21-14-f022c910d48931ce11223d247c888351-20230531122109-78f6d6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/12-21-14-f022c910d48931ce11223d247c888351-20230531122109-78f6d6.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>
<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/13-51-35-895d9c73dccc874815424d87298ef8bd-20230531135130-c79d43.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/13-51-35-895d9c73dccc874815424d87298ef8bd-20230531135130-c79d43.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="全虚拟化代表-kvm-和半虚拟化代表-xen-架构对比">全虚拟化代表 KVM 和半虚拟化代表 Xen 架构对比</h3>
<table>
  <thead>
      <tr>
          <th>架构</th>
          <th>描述</th>
          <th>对比</th>
          <th>示意图</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>全虚拟化：KVM</td>
          <td>KVM(Kernel&ndash;Based Virtual Machines) 是一个基于 Linux 内核的虚拟化技术，可以直接将 Linux 内核转换为 Hypervisor。.从而使得 Linuxp 内核能够直接管理虚拟机，直接调用 Linux 内核中的内存管理、进程管理子系统来管理虚拟机。</td>
          <td>- 支持全虚拟化<br />- 内置在内核中<br />- 便于版本安装、升级、维护，性能高<br />总结：KVM 平台架构侧重性能</td>
          <td>

<!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/13-50-44-295215a975864579d54f5ea07685e119-20230531135039-2dafa3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/13-50-44-295215a975864579d54f5ea07685e119-20230531135039-2dafa3.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></td>
      </tr>
      <tr>
          <td>半虚拟化：Xen</td>
          <td>Xen:直接把操作系统内核改了，把 OS 改成一个轻量级 Hypervisor1 在里面运行了一个管理所有资源作资源调度的 Domain0。<br />组成：由 Xen Hypervisor(虚拟化层)、Domin0(管理主机)、Domin U(用户虚<br />拟机)<br /></td>
          <td>- 支持全虚拟化、半虚拟化<br />- 需要对内核修改<br />- 更新版本，Xen 需要重新编译整个内核隔离性好<br />总结：Xen 平台架构侧重安全性</td>
          <td>

<!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/14-18-51-ec8fcb01ac5cda397c51a883587850ea-20230531141846-6f42cf.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-18-51-ec8fcb01ac5cda397c51a883587850ea-20230531141846-6f42cf.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></td>
      </tr>
  </tbody>
</table>
<h1 id="容器">容器</h1>
<p>容器：包装或装载物品的贮存器，利用一个开源的应用容器引擎，让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中，然后发布到任一 Liux 或 Windows 机器上，也可以实现虚拟化。相互之间不会有任何接口，实现 App 与操作系统的解耦。</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/14-49-30-c10e37d44495429910370b47eac70018-20230531144926-f7f9e5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-49-30-c10e37d44495429910370b47eac70018-20230531144926-f7f9e5.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>
<h2 id="主流容器技术">主流容器技术</h2>
<p>定义：Docker) 属于 Liux 容器的一种封装，提供简单易用的容器使用接口，他是目前最流行的 Linux 容器解决方案。
作用：将应用程序与该程序的依赖，<strong>打包在一个文件里</strong>。运行这个文件，就会生成一个虚拟容器。程序在这个虚拟容器里运行，就好像在真实的物理机上运行一样。有了 Docker，就不用担心环境问题。</p>
<p>组成：客户端 (Docker Client)、守护进程 (Docker Daemon)、镜像（Docker Image)、容器 (DockerContainer)、仓库（Docker Registry)</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/14-53-26-70a710bfca6ebb2d0197a8fb28a89b13-20230531145321-ce6e27.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-53-26-70a710bfca6ebb2d0197a8fb28a89b13-20230531145321-ce6e27.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>
<h2 id="容器和虚拟化的区别">容器和虚拟化的区别</h2>
<table>
  <thead>
      <tr>
          <th>虚拟化</th>
          <th>容器</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>隔离性强，有独立的 GUEST OS</td>
          <td>共享内核和 OS，隔离性弱</td>
      </tr>
      <tr>
          <td>虚拟化性能差 (&gt;15%)</td>
          <td>计算/存储无损耗，无 Guest0S 内存开销（200M)</td>
      </tr>
      <tr>
          <td>虚拟机镜像庞大（十几 G 几十 G),且实例化时不能共享</td>
          <td>Docker 容器镜象 200300M，且公共基础镜象实例化时<br />可以共享</td>
      </tr>
      <tr>
          <td>虚拟机镜象缺乏统一标准</td>
          <td>Docker 提供了容器应用镜象事实标准，OCI 推动进一步标<br />准化</td>
      </tr>
      <tr>
          <td>虚拟机创建慢 (&gt;2 分钟)</td>
          <td>秒级创建 (&lt;10s)<br />相当于建立索引</td>
      </tr>
      <tr>
          <td>虚拟机启动慢 (&gt;30s) 读文件逐个加载</td>
          <td>秒级 (&lt;1s，不含应用本身启动)</td>
      </tr>
      <tr>
          <td>资源虚拟化粒度低，单机 10~100 虚拟机</td>
          <td>单机支持 1000+ 容器<br />密度很高，适合大规模的部署</td>
      </tr>
  </tbody>
</table>
<h1 id="计算虚拟化">计算虚拟化</h1>
<p>从服务器组建角度来看，计算虚拟化可分为：</p>
<ul>
<li>CPU 虚拟化：保障 CPU 资源的合理调度以及 VM 上的指令能够正常高效的执行。</li>
<li>内存虚拟化：保障内存空间的合理分配、管理，隔离，以及高效可靠地使用。</li>
<li>I/O 虚拟化：保障 VM 的 1O 隔离与正常高效的执行。</li>
</ul>
<p>常见的计算服务架构有：</p>
<ul>
<li>OpenStack Nova</li>
<li>阿里云 ECS</li>
<li>腾讯云 CVM</li>
</ul>
<h2 id="nova">Nova</h2>
<p>OpenStack 是开源的云平台，通过不同的组件提供计算、存储、网络、数据库等多种云服务。其中计算服务由 Nova 组件提供，通过 nova-API 与其他组件通信，通过 nova-computex 对接不同的虚拟层提供计算虚拟化服务。</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/15-20-39-626c5b8808d188dd741630c2f871ac95-20230531152034-53e4f2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-20-39-626c5b8808d188dd741630c2f871ac95-20230531152034-53e4f2.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>创建实例流程：创建实例请求 nova-api，会唤醒 nova-database，请求刷新数据库。将请求给队列组件，nova-scheduler 从队列中取出请求，请求运行相对应的虚拟机。要运行不同的虚拟机，需要不同的平台支持（KVM，Xen,VMware）。虚拟机不能直接与数据库直接交互，需要通过 nova-conductor 转发。</p>
<h2 id="ecs">ECS</h2>
<p>云服务器 ECS(Elastic Compute Service) 是阿里云提供的基于 KVM 虚拟化的弹性计算服务，建立在阿里云飞天 (Apsara) 分布式操作系统上。
请求的主要调用流程为：OpenAPI、.业务层、控制系统、宿主机服务。</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/15-27-39-3057bca8eb97414d6d91c2201a895311-20230531152734-7f53e0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-27-39-3057bca8eb97414d6d91c2201a895311-20230531152734-7f53e0.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>
<h2 id="cvm">CVM</h2>
<p>云服务器 CVM(Cloud Virtual Machine)) 是腾讯提供的基于 KVM 虚拟化的弹性计算服务，建立在腾讯云分布式资源管理调度系统 VStation.上。
请求的主要调用流程为：API Server、.VStation、服务器集群。</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/15-28-22-2ee550326d62c2552c94cc2ef677e5a3-20230531152818-41688d.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-28-22-2ee550326d62c2552c94cc2ef677e5a3-20230531152818-41688d.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>
<h1 id="cpu-虚拟化">CPU 虚拟化</h1>
<p>在物理机（宿主机）中通过线程或进程这种纯软件方式模拟出假的 CPU，通过 CPU 虚拟化就可以将一个物理 CPU 发给不同的虚拟机使用。</p>
<p>虚拟出来的每颗 CPU 实际上就是一个线程或者进程，因此物理 CPU 核数要大于虚拟 CPU 总核数。</p>
<h2 id="cpu-qosquality-of-service-服务质量">CPU QoS(Quality of Service) 服务质量</h2>
<p>QoS 用来控制虚拟机使用 CPU 资源量的大小。
CPU 资源限额：控制虚拟机占用物理资源使用的上限。
CPU 资源份额：定义了多台虚拟机在竞争物理 CPU 资源时，需按比例分配计算资源。
CPU 预留资源：定义了多台虚拟机在竞争物理 CPU 时，每台虚拟机最低分配的计算资源。</p>
<h2 id="numa">NUMA</h2>
<p>NUMA(Non Uniform Memory Access Architecture) 非统一内存访问体系结构，提高物理服务器性能的一种技术。</p>
<p>将物理服务器的 CPU 和内存资源分到多个 node 上，node 内的内存访问效率最高。</p>
<p>NUMA 保证了一个 VM 上的 VCPU 尽量分配到同一个 node 中的物理 CPU 上，如果一台 VM 的 VCPU 跨 node 访问内存的话，访问的延时肯定增加。</p>
<h1 id="内存虚拟化">内存虚拟化</h1>
<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/15-53-33-a6d733a49ff6057c8e528cad5867f015-20230531155328-769d51.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-53-33-a6d733a49ff6057c8e528cad5867f015-20230531155328-769d51.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>
<h2 id="虚拟化类型-1">虚拟化类型</h2>
<table>
  <thead>
      <tr>
          <th>全虚拟化</th>
          <th>半虚拟化</th>
          <th>硬件辅助虚拟化</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>为每个 VM 维护一个影子页表记录虚拟化内存与物理内存的映射关系，VMM 将影子页表提交给 CPU 的内存管理单元 MMU 进行地址转换，VM 的页表无需改动。</td>
          <td>采用页表写入法，为每个 VM 创建<br />一个页表并向虚拟化层注册。VM<br />运行过程中 VMM 不断管理和维护<br />该页表，确保 VM 能直接访问到合<br />适的地址。</td>
          <td>EPT/NPT 是内存管理单元 MMU 的<br />扩展，CPU 硬件一个特性，通过<br />硬件方式实现 GuestOS 物理内存地<br />址到主机物理内存地址的转换，系<br />统开销更低，性能更高。</td>
      </tr>
  </tbody>
</table>
<h2 id="内存复用技术">内存复用技术</h2>
<p>内存复用是指在服务器物理内存一定的情况下，通过综合运用内存复用技术对内存进行分时复用。
内存复用技术有：</p>
<ul>
<li>内存气泡：虚拟化层将较空闲 VM 内存，分配给内存使用较高的虚拟机。内存的回收和分配由虚拟化层实现，虚拟机上的应用无感知，提高物理内存利用率。（虚拟机分配的内存不超过物理机总内存）</li>
<li>内存交换：将外部存储虚拟成内存给 VM 使用，将 VM 上长时间未访问的数据存放到外部存储上，建立映射关系。VM 再次访问这些数据是通过映射在与内存上的数据进行交换。</li>
<li>内存共享：VM 只对共用的内存（共享数据内容为零的内存页）做只读操作，有写操作时运用写时复制 (VM 有写操作时，开辟另一空间，并修改映射)</li>
</ul>
<h1 id="io-虚拟化">IO 虚拟化</h1>
<table>
  <thead>
      <tr>
          <th>全虚拟化</th>
          <th>半虚拟化</th>
          <th>Pass-Thorugh（直通）</th>
          <th>硬件辅助虚拟化</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>通过软件模拟的形式模拟 O 设备，<strong>不需要硬件支持</strong>，对虚拟机的操作系统也不需要修改（因为模拟的都是一个常见的硬件网卡，如 IntelE1000，主流操作系统一般都自带这些驱动，因此默认情下虚拟机不需要再安装驱动。缺点就是<strong>性能差</strong>。</td>
          <td>由 Hypervisor 提供资源调用接口。VM 通过特定的调用接口与 Hypervisor 通信，完成获取完整/O 资源控制操作。(需修改内核及驱动程序，存在移植性和适用性问题，导致其使用受限。)</td>
          <td>Hypervisor] 直接把硬件 PCI 设备分配给虚拟独占使用，性能挡当然好啦。但是<strong>浪遗硬件设备</strong>，且<strong>配置复杂</strong>，首先需要在 hypervisor 指定通过 PClid 方式分配给指定的虚拟机，然后虚拟机再识别到设备再安装驱动来使用。</td>
          <td>通过硬件的辅助可以让虚拟机直接访问物理设备，而不需要通过 VMM。最常用的就是 SR-lOV(Single Root I/OVirtualizmion)单根 I/O 虚拟化标准，该技术可以直接虚拟出 128-512 网卡，可以让虚拟机都拿到一块独立的网卡，直接使用/O 资源。</td>
      </tr>
  </tbody>
</table>
<h1 id="常见集群策略">常见集群策略</h1>
<h2 id="集群简介">集群简介</h2>
<p>集群是一种计算机系统，通过一组计算机或服务器的软硬件连接起来高度紧密地协作完成计算工作。在客户端看来为其提供服务的只有一台设备，实际上它是一群设备的集合，只不过这些设备提供的服务一样。</p>
<p>集群系统中单个计算机通常称为节点，通过局域网连接，利用多个计算机进行并行计算获得很高计算速度，也可以用多个计算机做备份提高可靠性。（并行计算技术）</p>
<h2 id="ha-策略">HA 策略</h2>
<p>HA(High Availability) 高可用性，一种让服务中断尽可能少的技术。将多台主机组建成一个故障转移集群，运行在集群上的服务（或 VM) 不会因为单台主机的故障而停止。</p>
<p>提升故障恢复速度，降低业务中断时间、保障业务连续性、实现一定的系统自维护。</p>
<h2 id="drs-策略">DRS 策略</h2>
<p>DRS(Dynamic resource scheduling) 动态资源调度，根据对资源池资源负载的动态监控，合理触发均匀分配规侧，实现资源池中的物理服务器之间重新分配资源，达到<strong>负载均衡、消峰填谷</strong>。</p>
<p>当物理服务器上负载过大时，通过 DRS 将虚拟机迁移到其他负载较轻的物理服务器上。当虚拟机遇到负载增大时，DRS 将为资源池中的物理服务器重新分配虚拟机可使用资源，在多个虚拟机之间智能地分配可用资源。</p>
<h2 id="dpm-策略">DPM 策略</h2>
<p>DPM（Distributed power management) 分布式电源管理，用于业务较轻时，把虚拟机动态“集中”到集群中的少部分主机上，将其他主机待机，节省电力消耗，等业务量较大时，再重新唤醒之前待机的主机。</p>
<p>执行 DPM 策略的前提是开启 DRS 策略，即集群必须先设置好 DRS 策略，才能设置 DPM 策略。</p>
<h1 id="存储虚拟化">存储虚拟化</h1>
<h2 id="存储类型">存储类型</h2>
<p>常用的存储类型有：</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>本地磁盘</th>
          <th>DAS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>简介</td>
          <td>云计算虚拟化场景下的本地磁盘是指使用服务器本地的磁盘资源，经过 RAD(磁盘阵列) 化后提供给虚拟化平台进行使用。</td>
          <td>DAS(Direct-Attached Storage) 直连式存储：一个存储设备与使用存储空间的服务器<br />直接相连的架构。DAS 为服务器提供块级的存储服务。</td>
      </tr>
      <tr>
          <td>优点</td>
          <td>- 使用方便<br />- 无共享框架<br /></td>
          <td>- 多个磁盘合并成一个逻辑磁盘，满足海量存储的需求<br />- 可实现应用数据和操作系统的分离<br />- 能提高存取性能<br />- 实施简单<br /></td>
      </tr>
      <tr>
          <td>缺点</td>
          <td>- 对跨服务器来说没有备份、冗余机制</td>
          <td>- 服务器发生故障，数据不可访问<br />- 传输距离短</td>
      </tr>
  </tbody>
</table>
<table>
  <thead>
      <tr>
          <th></th>
          <th style="text-align: left">NAS</th>
          <th style="text-align: left">SAN</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>简介</td>
          <td style="text-align: left">NAS(Network Attached Storage) 网络附加存储：将分布、独立的数据进行整合，集<br />中化管理，以便对不同主机和应用服务器进行访问的技术。<br />NAS 将存储设备连接到现有的网络上来提供数据和文件服务。</td>
          <td style="text-align: left">SAN(Storage Area Networks) 存储区域网络：是一种高速的、专门用于存储操作的网<br />络，通常独立于计算机局域网。<br />提供在主机和存储系统之间数据传输，网络内部数据传输的速率快。<br />常见架构有 FC SAN、IP SAN。</td>
      </tr>
      <tr>
          <td>优点</td>
          <td style="text-align: left">- 支持快照等高级特性<br />- 集中存储<br />- 提供安全集成环境（用户认证和授权)<br /></td>
          <td style="text-align: left">- 存储容量利用率高<br />- 兼容性高<br />- 传输距离远<br />- 高带宽<br />- 主机、存储设备可以独立扩展<br /></td>
      </tr>
      <tr>
          <td>缺点</td>
          <td style="text-align: left">- 传输速率低<br />- 前期安装和设备成本高</td>
          <td style="text-align: left">- 成本高、复杂</td>
      </tr>
  </tbody>
</table>
<h2 id="云存储基本概念">云存储基本概念</h2>
<p>存储资源：表示实际的物理存储设备，例如 DAS(直连存储)、NAS(网络附加存储)、SA(存储区域网络) 等。</p>
<p>存储设备：表示存储资源中的管理单元，例如本地磁盘、LUN(逻辑单元号)、Storage 存储池、NAS 共享目录等。</p>
<p>数据存储：表示虚拟化平台中可管理的存储逻辑单元，承载了虚拟机业务，创建磁盘。</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/18-55-45-5599a99b10ac1c9c3ad6cc69bc1c9c67-20230531185540-d75052.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-55-45-5599a99b10ac1c9c3ad6cc69bc1c9c67-20230531185540-d75052.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>
<h2 id="创建虚拟存储的流程">创建虚拟存储的流程</h2>
<ol>
<li>在主机软件界面添加存储资源 (SAN、DAS 等)，对主机的启动进行配置。</li>
<li>主机关联存储资源后，进行扫描存储设备（本地磁盘、LUN 等），将具体的设备扫描到主机上。</li>
<li>主机在选择存储设备，进行数据存储的添加，并进行虚拟化。</li>
<li>最后对虚拟化好的数据存储进行创建卷等操作。</li>
</ol>
<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/18-58-26-0207d3cfacb381f75c3d8f52b3251639-20230531185822-ac2437.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-58-26-0207d3cfacb381f75c3d8f52b3251639-20230531185822-ac2437.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>
<h2 id="存储模式">存储模式</h2>
<table>
  <thead>
      <tr>
          <th></th>
          <th>非虚拟化存储</th>
          <th>虚拟化存储</th>
          <th>裸设备映射</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td></td>
          <td>传统的存储模式，就是把磁盘进行分区，分割成不同的逻辑卷，每一个逻辑卷可以给到虚拟机进行使用。</td>
          <td>将不同的存储设备、磁盘进行格式化，格式化的目的是屏蔽底层存储设备的能力、接口<br />协议等差异性，将各种存储资源转化为统一管理的数据存储资源。</td>
          <td>将磁盘直接给到虚拟机使用，让虚拟机直接处理调用存储的命令（直接访问磁盘）中间虚拟化层不再对其进行任何干预（卷都不需要创建）</td>
      </tr>
      <tr>
          <td>特点</td>
          <td>- 性能好 (不再有中间的虚拟化层，VM 读写直接在磁盘上)、速度快、效率高。<br /><br />- 支持的存储功能少（不支持快照、精简配置等）<br /></td>
          <td>- 支持多种存储功能（快照、精简磁盘、磁盘扩容、存储热迁移等)。<br />- 性能不高（没有非虚拟化存储好）<br /></td>
          <td>速度快（三种模式中最快）、性能好。<br />支持的存储功能少（不支持快照、精简配置等），仅支持部分操作系统的虚拟机使用、数据存储只能整块当做裸设备映射的磁盘使用，不可分割。<br /></td>
      </tr>
      <tr>
          <td></td>
          <td>

<!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/19-05-13-74eb9473a84f7cc8d591b18d5b0ec7ee-20230531190509-4a3908.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-05-13-74eb9473a84f7cc8d591b18d5b0ec7ee-20230531190509-4a3908.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></td>
          <td>

<!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/19-08-34-98b4e748d8c582b1e74f109fe228fb9c-20230531190829-8006f5.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-08-34-98b4e748d8c582b1e74f109fe228fb9c-20230531190829-8006f5.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></td>
          <td>

<!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/19-12-22-a0c2ce10d5ab79474d217028ed20056b-20230531191217-563c1b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-12-22-a0c2ce10d5ab79474d217028ed20056b-20230531191217-563c1b.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></td>
      </tr>
  </tbody>
</table>
<h2 id="存储虚拟化方法">存储虚拟化方法</h2>
<h3 id="基于主机的存储虚拟化">基于主机的存储虚拟化</h3>
<p>若仅是单个主机服务器（或单个集群）访问多个磁盘阵列，可采用基于主机的存储虚拟化：虚拟化的工作通过特定的软件在主机服务器上完成，经过虚拟化的存储空间可以跨越多个异构的磁盘阵列。</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/08-36-54-e70134a6efd0d031785dc81eeb922c38-20230601083649-a5e4db.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-36-54-e70134a6efd0d031785dc81eeb922c38-20230601083649-a5e4db.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>若多个主机服务器需要访问同一个磁盘阵列时，可采用基于存储设备虚拟化。虚拟化的工作在阵列控制器上完成，将一个阵列上的存储容量划分多个存储空间 (LUN),供不同的主机系统访问。主要用在同一存储设备内部，进行数据保护和数据迁移。</p>
<ul>
<li>优点是与主机无关，不占用主机资源，数据管理功能丰富。</li>
<li>软件运行于存储设备中专门的嵌入式系统上。</li>
<li>从与 SAN 连接的存储上创建虚拟卷。</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/08-41-22-f6e0c1760cda24ae776bfcf3c57c2af5-20230601084118-73285e.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-41-22-f6e0c1760cda24ae776bfcf3c57c2af5-20230601084118-73285e.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>通过在存储区域网 (SAN) 中添加虚似化引擎实现的，主要用于异构存储系统的整合和统数据管理。</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/08-42-36-66183a6a63e0ab9ed777c2ff96834109-20230601084232-7fdd98.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/08-42-36-66183a6a63e0ab9ed777c2ff96834109-20230601084232-7fdd98.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>
<h2 id="存储虚拟化的功能">存储虚拟化的功能</h2>
<p>存储虚拟化可以提高硬件资源的使用效率，简化系统管理的复杂度，增强云存储平台的可靠性。可以通过以下几种技术实现：</p>
<h3 id="精简磁盘和空间回收">精简磁盘和空间回收</h3>
<p>精简磁盘和空间回收用于提高存储资源的使用效率、减小虚拟机未使用空间在主机上占用率过大的问题。</p>
<p>用户用多少分配多少空间（自动分配）<br />空间回收可以将用户删除的数据空间释放到数据存储。</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/09-07-37-a97805053794b23776d5085409611896-20230601090732-4ff3a3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-07-37-a97805053794b23776d5085409611896-20230601090732-4ff3a3.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>
<ul>
<li>指定数据集合的一个完全可用拷贝，该拷贝包括相应数据在某个时间点（拷贝开始的时间点) 的映像。</li>
<li>快照可以是其所表示的数据的一个副本，可以是数据的一个复制品。</li>
<li>快照的作用主要是能够进行在线数据备份恢复。</li>
<li>为用户提供了数据访问通道</li>
</ul>
<p>特点</p>
<ul>
<li>记录了虚拟机在某一时间点的内容和状。</li>
<li>恢复虚拟机快照可以使虚拟机多次快速恢复到某一时间点。</li>
<li>快照包含磁盘内容、虚拟机配置信息、内存数据。</li>
<li>多次快照之间<strong>保存差量数据</strong>，节约存储空间。</li>
</ul>
<h4 id="快照方式介绍">快照方式介绍</h4>
<p>创建快照时会生成一个新的差分卷，虚拟机会挂载这个差分卷作为磁盘文件。</p>
<ul>
<li>ROW 写时重定向</li>
<li>COW 写时拷贝</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/09-13-17-381617aa029cc392aa3ae573efdfb8aa-20230601091313-a0828c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-13-17-381617aa029cc392aa3ae573efdfb8aa-20230601091313-a0828c.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>
<h4 id="快照链介绍">快照链介绍</h4>
<p>对虚拟机进行多次的快照操作，这些多次的快照操作形成快照链。</p>
<p>虚拟机卷始终挂载在快照链的最末端。</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/09-35-59-7cc5ae355ea7c376e94b60b952e0284e-20230601093555-863fc8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-35-59-7cc5ae355ea7c376e94b60b952e0284e-20230601093555-863fc8.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>
<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/09-19-47-654bac5b7dfa915bea5c572df6f58bbd-20230601091942-a6a3d7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-19-47-654bac5b7dfa915bea5c572df6f58bbd-20230601091942-a6a3d7.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>功能：</p>
<ul>
<li>将虚拟机的磁盘从一个数据存储迁移到另一个数据存储。可以将虚拟机的所有磁盘整体迁移，也可以单个磁盘分别迁移。</li>
<li>虚拟机的快照可以一起迁移，虚拟机开启或者关闭时都可以迁移。</li>
</ul>
<p>特点：</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/09-32-38-a14bb4f8ed8bac86585c62877564bb65-20230601093233-54d851.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/09-32-38-a14bb4f8ed8bac86585c62877564bb65-20230601093233-54d851.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>
<h1 id="网络虚拟化">网络虚拟化</h1>
<p>虚拟化是对所有 T 资源的虚拟化，提高物理硬件的灵活性及利用效率。云计算中的计算和存储资源分别由计算虚拟化和存储虚拟化提供，而网络作为 T 的重要资源也有相应的虚拟化技术，网络资源由网络虚拟化提供。</p>
<p>网络是由各种设备组成，有传统的物理网络，还有运行在服务器上看不到的虚拟网络。如何呈现和管理它们将是网络虚拟化的首要目标。</p>
<p>将物理网络虚拟出多个相互隔离的虚拟网络（逻辑网络），从而使得不同用户之间使用独立的网络资源，从而提高网络资源利用率，实现弹性的网络。</p>
<p>VLAN 就是一种网络虚拟化，在原有网络基础上通过 VLAN Tag:划分出多个广播域。</p>
<p>网络虚拟化保障我们创建出来的虚拟机可以正常  通信、访问网络。</p>
<p>节省物理主机的网卡设备资源，并且可以提供应用的虚拟网络所需的 L2 一 L7 层网络服务。</p>
<p>网络虚拟化软件提供逻辑上的交换机和路由器 (L2-L3),逻辑负载均衡器，逻辑防火墙 (L4-L7) 等，且可以以任何形式进行组装，从而为虚拟机提供一个完整的 L2-L7 层的虚拟网络拓扑。</p>
<h2 id="物理网络包含的设备">物理网络包含的设备</h2>
<p>路由器：工作在网络层，连接两个不同的网络。</p>
<p>二层交换机：工作在数据链路层，转发数据。</p>
<p>三层交换机：工作在网络层，结合了部分路由和交换机的功能。</p>
<p>服务器网卡：提供通信服务。</p>
<h1 id="虚拟化中的网络架构">虚拟化中的网络架构</h1>
<p>网卡虚拟化方法有：</p>
<ul>
<li>
<p>软件网卡虚拟化</p>
<ul>
<li>主要通过软件控制各个虚拟机共享同一块物理网卡实现。软件虚拟出来的网卡可以有单独的 MAC 地址、IP 地址。</li>
<li>所有虚拟机的虚拟网卡通过虚拟交换机以及物理网卡连接至物理交换机。虚拟交换机负责将虚拟机上的数据报文从物理网口转发出去。</li>
</ul>
</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/14-16-53-540de968dd6e4e7cc2e45e8b1b1df1a1-20230601141648-d238fb.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-16-53-540de968dd6e4e7cc2e45e8b1b1df1a1-20230601141648-d238fb.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>
<ul>
<li>
<p>硬件网卡虚拟化</p>
<ul>
<li>主要用到的技术是单根 I/O 虚拟化 (Single Root/O Virtulization,SR-lOV),就是 I/O 直通技术，通过硬件的辅助可以让虚拟机直接访问物理设备，而不需要通过 VMM。该技术可以直接虚拟出 128-512 网卡，可以让虚拟机都拿到一块独立的网卡，直接使用/O 资源。SR-OV 能够让网络传输绕过软件模拟层，直接分配到虚拟机，这样就降低了软件模拟层中的/○ 开销。</li>
</ul>
</li>
</ul>
<p>交换机虚拟化：</p>
<ul>
<li>OVS(Open vSwitch) 开放虚拟化软件交换机，是一款基于软件实现的开源虚拟以太网交换机，使用开源 Apache2.0 许可协议，主要用于虚拟机 VM 环境。与众多开源的虚拟化平台相整合（支持 Xen、KVM 及 VirtualBox 多种虚拟化技术），主要有两个作用：传递虚拟机之间的流量，实现虚拟机和外界网络的通信。</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/14-30-39-aaaaed14c501175ffa51df2735b09d2a-20230601143034-d4f7f6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-30-39-aaaaed14c501175ffa51df2735b09d2a-20230601143034-d4f7f6.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>
<h2 id="虚拟化中数据的转发路径">虚拟化中数据的转发路径</h2>
<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/18-59-54-9abbabcce9a6864d7c990eba310e171f-20230601185949-5239e7.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-59-54-9abbabcce9a6864d7c990eba310e171f-20230601185949-5239e7.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>
<h2 id="链路虚拟化">链路虚拟化</h2>
<p>VPC(Virtual Port Channel) 虚链路聚合，是最常见的二层虚拟化技术。</p>
<p>链路聚合将多个物理端口捆绑在一起，虚拟成为一个逻辑端口。但传统链路聚合不能跨设备，VPC 很好解决了这个问题，既可以跨设备，又可以增加链路带宽、实现链路层的高可用性。</p>
<p>隧道协议 (Tunneling Protocol)：指通过隧道协议使多个<strong>不同协议的网络实现互联</strong>。使用隧道传递的数据可以是不同协议的数据帧或包。隧道可以将数据流强制送到特定的地址，并隐藏中间节点的网络地址，还可根据需要，提供对数据加密的功能。</p>
<ul>
<li>GRE(Generic Routing Encapsulation) 通用路由封装。</li>
<li>IPsec(Internet Protocol Security)Internett 协议安全。</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/19-34-04-8dc54676f16ac517fa503ded64966d24-20230601193359-139761.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-34-04-8dc54676f16ac517fa503ded64966d24-20230601193359-139761.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>
<h2 id="虚拟网络">虚拟网络</h2>
<p>虚拟网络 (Virtual Network):是由虚拟链路组成的网络。</p>
<p>虚拟网络节点之间的连接并不使用物理线缆连接，而是依靠特定的虚拟化链路相连。</p>
<p>典型的虚拟网络包括：</p>
<ul>
<li>
<p>层叠网络（虚拟二层延伸网络）</p>
<ul>
<li>
<p>层叠网络 (Overlay Network)：在现有网络的基础上搭建另外一种网络</p>
</li>
<li>
<p>层叠网络允许对没有引 P 地址标识的目的主机路由信息。</p>
</li>
<li>
<p>层叠网络可以充分利用现有资源，在不增加成本的前提下，提供更多的服务。（比如 ADSL Internet 接入线路就是基于已经存在的 PSTN 网络实现)</p>
</li>
<li>
<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/19-36-27-c84d66208097477a09c6ce6b162bfacd-20230601193622-21ef48.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-36-27-c84d66208097477a09c6ce6b162bfacd-20230601193622-21ef48.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>
</li>
<li>
<p>典型技术：</p>
<ul>
<li>VXLAN(Virtual eXtensible Local Area Network) 虚拟扩展局域网：很好地解决了现有 VLAN 技术无法满足大二层网络需求的问题。</li>
<li>VXLAN 技术是一种大二层的虚拟网络技术。</li>
<li>原理是引入一个 UDP 格式的外层隧道作为数据链路层，而原有数据报文内容作为隧道净荷加以传输。</li>
</ul>
</li>
</ul>
</li>
<li>
<p>VPN 网络</p>
<ul>
<li>
<p>VPN(Virtual Private Network) 虚拟专用网：是一种常用于连接中、大型企业或团体与团体间的私人网络的通信方法。</p>
<ul>
<li>通过公用的网络架构（比如互联网）来传送内联网的信息。</li>
<li>利用已加密的隧道协议来达到保密、终端认证、信息准确性等安全效果。这种技术可以</li>
<li>在不安全的网络上传送可靠的、安全的信息。</li>
<li>

<!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/19-39-40-4bc357c1f760435fb8d1a980f230527c-20230601193935-06e11c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-39-40-4bc357c1f760435fb8d1a980f230527c-20230601193935-06e11c.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></li>
</ul>
</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Devstack 部署 OpenStack</title>
      <link>https://lifeislife.cn/posts/devstack%E9%83%A8%E7%BD%B2openstack/</link>
      <pubDate>Fri, 09 Jun 2023 21:38:34 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/devstack%E9%83%A8%E7%BD%B2openstack/</guid>
      <description>&lt;h1 id=&#34;devstack-部署-openstack&#34;&gt;Devstack 部署 OpenStack&lt;/h1&gt;
&lt;p&gt;试验发现在 Host 为 Ubuntu20.04 和 22.04 上无法顺利安装 VirtualBox，请在 Ubuntu18.04 上安装 VirtualBox。虚拟机镜像版本为 Ubuntu20.04，以下步骤可以稳定复现，OpenStack master（c424a7a299e37004d318107648bb18e157344985）版本。&lt;/p&gt;
&lt;p&gt;总而言之，在 18.04 版本上安装 VirtualBox，在 20.04 版本上安装 OpenStack。&lt;/p&gt;
&lt;p&gt;因为安装 OpenStack 容易破话系统包依赖，如果为了学习建议在虚拟机中安装。&lt;/p&gt;
&lt;p&gt;安装过程中需要下载镜像，请确认机器可以访问外网。&lt;/p&gt;
&lt;h2 id=&#34;安装-virtualbox&#34;&gt;安装 VirtualBox&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install virtualbox virtualbox-ext-pack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;确认-virtualbox-配置&#34;&gt;确认 VirtualBox 配置&lt;/h2&gt;
&lt;p&gt;请确认 VirtualBox 配置如下，VirtualBox 默认配置硬盘为 10G，远远不够用，为了避免后续的麻烦，请确认如下配置：&lt;/p&gt;
&lt;p&gt;磁盘大于 100G&lt;/p&gt;
&lt;p&gt;内存大于 16G&lt;/p&gt;
&lt;p&gt;CPU 大于 4 个&lt;/p&gt;
&lt;h2 id=&#34;下载镜像并安装&#34;&gt;下载镜像并安装&lt;/h2&gt;
&lt;p&gt;镜像可以去&lt;a href=&#34;https://mirrors.tuna.tsinghua.edu.cn/&#34;&gt;清华大学开源软件镜像站 | Tsinghua Open Source Mirror&lt;/a&gt;下载。&lt;/p&gt;
&lt;h2 id=&#34;更新源&#34;&gt;更新源&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restrcdicted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restrcd &amp;amp;&amp;amp; mkdir .pip &amp;amp;&amp;amp; cd .pipicted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;EOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;备用源-以备不时之需&#34;&gt;备用源 (以备不时之需)&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restrcdicted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restrcd &amp;amp;&amp;amp; mkdir .pip &amp;amp;&amp;amp; cd .pipicted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;EOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb https://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src https://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb https://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src https://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb https://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src https://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb https://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src https://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;EOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;EOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;安装基础包&#34;&gt;安装基础包&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install aptitude
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;aptitude 用于解决包依赖冲突。&lt;/p&gt;
&lt;h2 id=&#34;添加用户&#34;&gt;添加用户&lt;/h2&gt;
&lt;p&gt;添加 stack 用户&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo useradd -s /bin/bash -d /opt/stack -m stack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;授予 sudo 权限&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;stack ALL=(ALL) NOPASSWD: ALL&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee /etc/sudoers.d/stack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;切换到 stack 用户&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo su -  stack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;pip-配置&#34;&gt;PIP 配置&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cd &amp;amp;&amp;amp; mkdir -p  .pip &amp;amp;&amp;amp; cd .pip &amp;amp;&amp;amp; bash -c &amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; ~/.pip/pip.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[global]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;timeout = 6000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;index-url = http://mirrors.aliyun.com/pypi/simple/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;trusted-host = mirrors.aliyun.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;EOF&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;仓库下载&#34;&gt;仓库下载&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/openstack/devstack --depth &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;配置-localconf&#34;&gt;配置 local.conf&lt;/h3&gt;
&lt;p&gt;只需要修改 HOST_IP，其他的可以不用修改，HOST_IP 为本机 IP 地址，可以使用 ifconfig 查看。如果是虚拟机就是虚拟机的 IP，virtualbox 创建的虚拟机默认为 10.0.2.15。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; devstack &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; touch local.conf &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; /opt/stack/devstack/local.conf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;[[local|localrc]]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;HOST_IP=10.0.2.15
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;GIT_BASE=http://git.trystack.cn
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;ADMIN_PASSWORD=user
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;DATABASE_PASSWORD=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$ADMIN_PASSWORD&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;RABBIT_PASSWORD=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$ADMIN_PASSWORD&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;SERVICE_PASSWORD=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$ADMIN_PASSWORD&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;EOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;FORCE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;yes ./stack.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;bug-解决&#34;&gt;BUG 解决&lt;/h1&gt;
&lt;h2 id=&#34;systemd-包依赖冲突&#34;&gt;systemd 包依赖冲突&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo aptitude install systemd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;选择N
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;再选择Y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;modulenotfounderror-no-module-named-distutilscmd&#34;&gt;ModuleNotFoundError: No module named &amp;lsquo;distutils.cmd&amp;rsquo;&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; sudo apt-get install python3.10-distutils
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; # 根据自己的 Python 版本决定，可以 python3.7-distutils python3.8-distutils   ....都试一遍
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;apparmor-invalid-capability-bpf&#34;&gt;apparmor invalid capability bpf&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install apparmor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;no-tenant-network-is-available-for-allocation&#34;&gt;No tenant network is available for allocation&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim /etc/neutron/plugins/ml2/ml2_conf.ini
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;ml2&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;type_drivers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; flat,vlan,vxlan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;tenant_network_types&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; vxlan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;ml2_type_vxlan&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;vni_ranges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 1:1000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;unixvarrunopenvswitchdbsock-database-connection-failed-connection-refused&#34;&gt;unix:/var/run/openvswitch/db.sock: database connection failed (Connection refused)&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;  /opt/stack/devstack/lib/neutron_plugin 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vi ovn_agent 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;116G  跳转到116行
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;OVS_RUNDIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$OVS_PREFIX&lt;/span&gt;/var/run/openvswitch &lt;span class=&#34;nv&#34;&gt;修改为OVS_RUNDIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$OVS_PREFIX&lt;/span&gt;/var/run/ovn 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo rm -rf /var/run/ovn
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;virtualbox-启动报错--failed-to-send-host-log-message&#34;&gt;VirtualBox 启动报错 : Failed to send host log message&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Ctrl+F2进入另一个终端
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;输入：startx进入桌面
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;attributeerror-module-collections-has-no-attribute-mutablemapping&#34;&gt;AttributeError: module ‘collections‘ has no attribute ‘MutableMapping‘&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;collections.MutableMapping
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;collections.abc.MutableMapping
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;modulenotfounderror-no-module-named-distutilscore&#34;&gt;ModuleNotFoundError: No module named &amp;lsquo;distutils.core&amp;rsquo;&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install python3-pip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;error-cannot-uninstall-simplejson-it-is-a-distutils-installed-project-and-thus-we-cannot&#34;&gt;ERROR: Cannot uninstall &amp;lsquo;simplejson&amp;rsquo;. It is a distutils installed project and thus we cannot&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo pip install --ignore-installed wrapt enum34 simplejson netaddr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;server-certificate-verification-failed-cafile-none-crlfile-none&#34;&gt;server certificate verification failed. CAfile: none CRLfile: none&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global http.sslverify &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global https.sslverify &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ubuntu登录页面验证出错&#34;&gt;Ubuntu:登录页面验证出错&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo loginctl unlock-sessions
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ubuntu重装桌面&#34;&gt;Ubuntu:重装桌面&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install ubuntu-desktop
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ubuntu-登录界面-authentication-error&#34;&gt;Ubuntu 登录界面 Authentication Error&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo loginctl unlock-sessions
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;fs.inotify.max_user_watches=524288&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ubuntu-无法进入桌面系统但是可以-ssh-链接&#34;&gt;Ubuntu 无法进入桌面系统，但是可以 SSH 链接&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo rm -rf /var/lib/apt/lists/*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install --reinstall appstream
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;gsettings-desktop-schemas--破坏mutter--3314-但是-3284-0ubuntu18042-正要被安装解决方案&#34;&gt;gsettings-desktop-schemas : 破坏：mutter (＜ 3.31.4) 但是 3.28.4-0ubuntu18.04.2 正要被安装解决方案&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sudo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;apt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;install&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gsettings&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;desktop&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;schemas&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sudo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;apt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;install&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;essential&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="devstack-部署-openstack">Devstack 部署 OpenStack</h1>
<p>试验发现在 Host 为 Ubuntu20.04 和 22.04 上无法顺利安装 VirtualBox，请在 Ubuntu18.04 上安装 VirtualBox。虚拟机镜像版本为 Ubuntu20.04，以下步骤可以稳定复现，OpenStack master（c424a7a299e37004d318107648bb18e157344985）版本。</p>
<p>总而言之，在 18.04 版本上安装 VirtualBox，在 20.04 版本上安装 OpenStack。</p>
<p>因为安装 OpenStack 容易破话系统包依赖，如果为了学习建议在虚拟机中安装。</p>
<p>安装过程中需要下载镜像，请确认机器可以访问外网。</p>
<h2 id="安装-virtualbox">安装 VirtualBox</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install virtualbox virtualbox-ext-pack
</span></span></code></pre></div><h2 id="确认-virtualbox-配置">确认 VirtualBox 配置</h2>
<p>请确认 VirtualBox 配置如下，VirtualBox 默认配置硬盘为 10G，远远不够用，为了避免后续的麻烦，请确认如下配置：</p>
<p>磁盘大于 100G</p>
<p>内存大于 16G</p>
<p>CPU 大于 4 个</p>
<h2 id="下载镜像并安装">下载镜像并安装</h2>
<p>镜像可以去<a href="https://mirrors.tuna.tsinghua.edu.cn/">清华大学开源软件镜像站 | Tsinghua Open Source Mirror</a>下载。</p>
<h2 id="更新源">更新源</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk <span class="o">&amp;&amp;</span> sudo bash -c <span class="s2">&#34;cat &lt;&lt; EOF &gt; /etc/apt/sources.list
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restrcdicted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restrcd &amp;&amp; mkdir .pip &amp;&amp; cd .pipicted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF&#34;</span>
</span></span></code></pre></div><h3 id="备用源-以备不时之需">备用源 (以备不时之需)</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk <span class="o">&amp;&amp;</span> sudo bash -c <span class="s2">&#34;cat &lt;&lt; EOF &gt; /etc/apt/sources.list
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restrcdicted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restrcd &amp;&amp; mkdir .pip &amp;&amp; cd .pipicted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk <span class="o">&amp;&amp;</span> sudo bash -c <span class="s2">&#34;cat &lt;&lt; EOF &gt; /etc/apt/sources.list
</span></span></span><span class="line"><span class="cl"><span class="s2">deb https://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src https://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb https://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src https://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">deb https://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src https://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">deb https://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src https://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo mv /etc/apt/sources.list /etc/apt/sources.list.bk <span class="o">&amp;&amp;</span> sudo bash -c <span class="s2">&#34;cat &lt;&lt; EOF &gt; /etc/apt/sources.list
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF&#34;</span>
</span></span></code></pre></div><h2 id="安装基础包">安装基础包</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install git
</span></span><span class="line"><span class="cl">sudo apt install aptitude
</span></span></code></pre></div><p>aptitude 用于解决包依赖冲突。</p>
<h2 id="添加用户">添加用户</h2>
<p>添加 stack 用户</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo useradd -s /bin/bash -d /opt/stack -m stack
</span></span></code></pre></div><p>授予 sudo 权限</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;stack ALL=(ALL) NOPASSWD: ALL&#34;</span> <span class="p">|</span> sudo tee /etc/sudoers.d/stack
</span></span></code></pre></div><p>切换到 stack 用户</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo su -  stack
</span></span></code></pre></div><h2 id="pip-配置">PIP 配置</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">cd &amp;&amp; mkdir -p  .pip &amp;&amp; cd .pip &amp;&amp; bash -c &#34;cat &lt;&lt; EOF &gt; ~/.pip/pip.conf
</span></span><span class="line"><span class="cl">[global]
</span></span><span class="line"><span class="cl">timeout = 6000
</span></span><span class="line"><span class="cl">index-url = http://mirrors.aliyun.com/pypi/simple/
</span></span><span class="line"><span class="cl">trusted-host = mirrors.aliyun.com
</span></span><span class="line"><span class="cl">EOF&#34;
</span></span></code></pre></div><h2 id="仓库下载">仓库下载</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">git clone https://github.com/openstack/devstack --depth <span class="m">1</span>
</span></span></code></pre></div><h3 id="配置-localconf">配置 local.conf</h3>
<p>只需要修改 HOST_IP，其他的可以不用修改，HOST_IP 为本机 IP 地址，可以使用 ifconfig 查看。如果是虚拟机就是虚拟机的 IP，virtualbox 创建的虚拟机默认为 10.0.2.15。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> devstack <span class="o">&amp;&amp;</span> touch local.conf <span class="o">&amp;&amp;</span> bash -c <span class="s2">&#34;cat &lt;&lt; EOF &gt; /opt/stack/devstack/local.conf
</span></span></span><span class="line"><span class="cl"><span class="s2">[[local|localrc]]
</span></span></span><span class="line"><span class="cl"><span class="s2">HOST_IP=10.0.2.15
</span></span></span><span class="line"><span class="cl"><span class="s2">GIT_BASE=http://git.trystack.cn
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">ADMIN_PASSWORD=user
</span></span></span><span class="line"><span class="cl"><span class="s2">DATABASE_PASSWORD=</span><span class="nv">$ADMIN_PASSWORD</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">RABBIT_PASSWORD=</span><span class="nv">$ADMIN_PASSWORD</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">SERVICE_PASSWORD=</span><span class="nv">$ADMIN_PASSWORD</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF&#34;</span>
</span></span></code></pre></div><h2 id="安装">安装</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nv">FORCE</span><span class="o">=</span>yes ./stack.sh
</span></span></code></pre></div><h1 id="bug-解决">BUG 解决</h1>
<h2 id="systemd-包依赖冲突">systemd 包依赖冲突</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo aptitude install systemd
</span></span><span class="line"><span class="cl">选择N
</span></span><span class="line"><span class="cl">再选择Y
</span></span></code></pre></div><h2 id="modulenotfounderror-no-module-named-distutilscmd">ModuleNotFoundError: No module named &lsquo;distutils.cmd&rsquo;</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> sudo apt-get install python3.10-distutils
</span></span><span class="line"><span class="cl"> # 根据自己的 Python 版本决定，可以 python3.7-distutils python3.8-distutils   ....都试一遍
</span></span></code></pre></div><h2 id="apparmor-invalid-capability-bpf">apparmor invalid capability bpf</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt install apparmor
</span></span></code></pre></div><h2 id="no-tenant-network-is-available-for-allocation">No tenant network is available for allocation</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">vim /etc/neutron/plugins/ml2/ml2_conf.ini
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>ml2<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">type_drivers</span> <span class="o">=</span> flat,vlan,vxlan
</span></span><span class="line"><span class="cl"><span class="nv">tenant_network_types</span> <span class="o">=</span> vxlan
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>ml2_type_vxlan<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">vni_ranges</span> <span class="o">=</span> 1:1000
</span></span></code></pre></div><h2 id="unixvarrunopenvswitchdbsock-database-connection-failed-connection-refused">unix:/var/run/openvswitch/db.sock: database connection failed (Connection refused)</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span>  /opt/stack/devstack/lib/neutron_plugin 
</span></span><span class="line"><span class="cl">vi ovn_agent 
</span></span><span class="line"><span class="cl">116G  跳转到116行
</span></span><span class="line"><span class="cl"><span class="nv">OVS_RUNDIR</span><span class="o">=</span><span class="nv">$OVS_PREFIX</span>/var/run/openvswitch <span class="nv">修改为OVS_RUNDIR</span><span class="o">=</span><span class="nv">$OVS_PREFIX</span>/var/run/ovn 
</span></span><span class="line"><span class="cl">sudo rm -rf /var/run/ovn
</span></span></code></pre></div><h2 id="virtualbox-启动报错--failed-to-send-host-log-message">VirtualBox 启动报错 : Failed to send host log message</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">Ctrl+F2进入另一个终端
</span></span><span class="line"><span class="cl">输入：startx进入桌面
</span></span></code></pre></div><h2 id="attributeerror-module-collections-has-no-attribute-mutablemapping">AttributeError: module ‘collections‘ has no attribute ‘MutableMapping‘</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">collections.MutableMapping
</span></span><span class="line"><span class="cl">collections.abc.MutableMapping
</span></span></code></pre></div><h2 id="modulenotfounderror-no-module-named-distutilscore">ModuleNotFoundError: No module named &lsquo;distutils.core&rsquo;</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sudo apt install python3-pip
</span></span></code></pre></div><h2 id="error-cannot-uninstall-simplejson-it-is-a-distutils-installed-project-and-thus-we-cannot">ERROR: Cannot uninstall &lsquo;simplejson&rsquo;. It is a distutils installed project and thus we cannot</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sudo pip install --ignore-installed wrapt enum34 simplejson netaddr
</span></span></code></pre></div><h2 id="server-certificate-verification-failed-cafile-none-crlfile-none">server certificate verification failed. CAfile: none CRLfile: none</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">git config --global http.sslverify <span class="nb">false</span>
</span></span><span class="line"><span class="cl">git config --global https.sslverify <span class="nb">false</span>
</span></span></code></pre></div><h2 id="ubuntu登录页面验证出错">Ubuntu:登录页面验证出错</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo loginctl unlock-sessions
</span></span></code></pre></div><h2 id="ubuntu重装桌面">Ubuntu:重装桌面</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install ubuntu-desktop
</span></span></code></pre></div><h2 id="ubuntu-登录界面-authentication-error">Ubuntu 登录界面 Authentication Error</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo loginctl unlock-sessions
</span></span><span class="line"><span class="cl">sudo <span class="nb">echo</span> <span class="s2">&#34;fs.inotify.max_user_watches=524288&#34;</span> &gt;&gt; /etc/sysctl.conf
</span></span></code></pre></div><h2 id="ubuntu-无法进入桌面系统但是可以-ssh-链接">Ubuntu 无法进入桌面系统，但是可以 SSH 链接</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo rm -rf /var/lib/apt/lists/*
</span></span><span class="line"><span class="cl">sudo apt-get clean
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get install --reinstall appstream
</span></span></code></pre></div><h2 id="gsettings-desktop-schemas--破坏mutter--3314-但是-3284-0ubuntu18042-正要被安装解决方案">gsettings-desktop-schemas : 破坏：mutter (＜ 3.31.4) 但是 3.28.4-0ubuntu18.04.2 正要被安装解决方案</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">sudo</span><span class="w"> </span><span class="n">apt</span><span class="w"> </span><span class="n">install</span><span class="w"> </span><span class="n">gsettings</span><span class="o">-</span><span class="n">desktop</span><span class="o">-</span><span class="n">schemas</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">sudo</span><span class="w"> </span><span class="n">apt</span><span class="o">-</span><span class="n">get</span><span class="w"> </span><span class="n">install</span><span class="w"> </span><span class="n">build</span><span class="o">-</span><span class="n">essential</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>计算机网络 - 物理层</title>
      <link>https://lifeislife.cn/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-%E7%89%A9%E7%90%86%E5%B1%82/</link>
      <pubDate>Mon, 10 Apr 2023 21:14:41 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-%E7%89%A9%E7%90%86%E5%B1%82/</guid>
      <description>&lt;h2 id=&#34;物理层&#34;&gt;物理层&lt;/h2&gt;
&lt;h3 id=&#34;基本概念&#34;&gt;基本概念&lt;/h3&gt;
&lt;p&gt;物理层解决如何在连接各种计算机的传输媒体上&lt;code&gt;传输数据比特流&lt;/code&gt;，而不是指具体的传输媒体。
物理层主要任务：确定与传输媒体&lt;code&gt;接口&lt;/code&gt;有关的一些特性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;机械特性：定义物理连接的特性，规定物理连接时所采用的规格、接口形状、引线数目、&lt;code&gt;引脚数量&lt;/code&gt;和排列情况。&lt;/li&gt;
&lt;li&gt;电气特性：规定传输二进制位时，线路上信号的&lt;code&gt;电压范围&lt;/code&gt;、阻抗匹配、传输&lt;code&gt;速率&lt;/code&gt;和&lt;code&gt;距离&lt;/code&gt;限制等。&lt;/li&gt;
&lt;li&gt;功能特性：指明某条线上出现的某一&lt;code&gt;电平表示何种意义&lt;/code&gt;，接口部件的信号线的用途。&lt;/li&gt;
&lt;li&gt;规程特性（过程特性）：定义各条物理线路的工作&lt;code&gt;规程和时序&lt;/code&gt;关系&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;数据通信基础概念&#34;&gt;数据通信基础概念&lt;/h3&gt;
&lt;h4 id=&#34;典型的数据通信模型&#34;&gt;典型的数据通信模型&lt;/h4&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//2023/06/10/dcfc2ba8a7d5d1e222e8be2ea6e6b78b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/dcfc2ba8a7d5d1e222e8be2ea6e6b78b.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;/ul&gt;
&lt;p&gt;信源：产生和发送数据的源头。
信道：信号的传输媒介。一般用来表示向某一个方向传送信息的介质，因此一条通信线路往往包含一条发送信道和一条接收信道。&lt;/p&gt;
&lt;h4 id=&#34;通信方式&#34;&gt;通信方式&lt;/h4&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;h4 id=&#34;传输方式&#34;&gt;传输方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;串行传输
&lt;ul&gt;
&lt;li&gt;速度慢，费用低，适合远距离&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;并行传输
&lt;ul&gt;
&lt;li&gt;速度快，费用高，适合近距离&lt;/li&gt;
&lt;/ul&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//2023/06/10/c45684f0736a31c6a9515c2f546187b3.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/c45684f0736a31c6a9515c2f546187b3.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;h4 id=&#34;码元&#34;&gt;码元&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;码元&lt;/code&gt;是指用一个&lt;code&gt;固定时长&lt;/code&gt;的&lt;code&gt;信号波形&lt;/code&gt;（数字脉冲），代表不同离散数值的基本波形，是数字通信中数字信号的&lt;code&gt;计量单位&lt;/code&gt;，这个时长内的信号称为&lt;code&gt;k进制码元&lt;/code&gt;，而该时长称为&lt;code&gt;码元宽度&lt;/code&gt;。当码元的离散状态有 M 个时（M 大于 2），此时码元为 M 进制码元。
&lt;code&gt;1码元可以携带多个比特的信息量&lt;/code&gt;。例如，在使用二进制编码时，只有两种不同的码元，一种代表 0 状态，另 - 一种代表 1 状态。在四进制码元中，一个码元就由 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//2023/06/10/4a5eda03b690c8ebe8bd258be92a0d97.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/4a5eda03b690c8ebe8bd258be92a0d97.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;h4 id=&#34;速率波特带宽&#34;&gt;速率，波特，带宽&lt;/h4&gt;
&lt;p&gt;速率也叫数据率，是指数据的&lt;code&gt;传输速率&lt;/code&gt;，表示单位时间内传输的数据量。可以用码元传输速率和信息传输速率表示。
&lt;code&gt;传输速率&lt;/code&gt;是主机上发出的速率，而&lt;code&gt;传播速率&lt;/code&gt;是在信道上的传播速率。两者是不同的概念&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;码元传输速率&lt;/strong&gt;&lt;code&gt;1s传输多少个码元&lt;/code&gt;：别名&lt;code&gt;码元速率&lt;/code&gt;、波形速率、调制速率、符号速率等，它表示单位时间内数字通信系统所传输的码元个数（也可称为&lt;code&gt;脉冲个数&lt;/code&gt;或&lt;code&gt;信号变化的次数&lt;/code&gt;），单位是&lt;code&gt;波特&lt;/code&gt;（Baud）。1 波特表示数字通信系统每秒传输一个码元。这里的码元可以是多进制的，也可以是二进制的，但码元速率与进制数无关。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信息传输速率&lt;/strong&gt;&lt;code&gt;1s传输多少个比特&lt;/code&gt;：别名&lt;code&gt;信息速率&lt;/code&gt;、比特率等，表示单位时间内数字通信系统传输的二进制码元个数（即比特数），单位是比特/秒（b/s）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;关系：若一个码元携带&lt;code&gt;n bit&lt;/code&gt;的信息量，则&lt;code&gt;M Baud&lt;/code&gt;的码元传输速率所对应的信息传输速率为&lt;code&gt;M * n bit/s&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;带宽 (是个理想值)：表示在单位时间内从网络中的某一点到另一 点所能通过的“&lt;code&gt;最高数据率&lt;/code&gt;”，常用来表示网络的通信线路所能传输数据的能力。单位是 b/s。&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//2023/06/10/aa1b08e1a99ea7fd84c0375d303b3afa.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/aa1b08e1a99ea7fd84c0375d303b3afa.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;4 进制表示码元有 4 种波形，只需要 2 位就可以表示 4 种波形，同理 16 进制需要 4 位表示。&lt;/p&gt;
&lt;h3 id=&#34;奈氏准则&#34;&gt;奈氏准则&lt;/h3&gt;
&lt;h4 id=&#34;失真&#34;&gt;失真&lt;/h4&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//2023/06/10/8c49c7fd87d8971741228463c0bb1639.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/8c49c7fd87d8971741228463c0bb1639.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;li&gt;传输媒体质量&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;码间串扰&#34;&gt;码间串扰&lt;/h4&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//2023/06/10/74aab08e792d5be066e1fb82ad251ab6.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/74aab08e792d5be066e1fb82ad251ab6.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;p&gt;&lt;strong&gt;码间串扰&lt;/strong&gt;：接收端收到的信号波形&lt;code&gt;失去了码元之间清晰界限&lt;/code&gt;的现象。&lt;/p&gt;
&lt;h4 id=&#34;奈氏准则-1&#34;&gt;奈氏准则&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;奈氏准则&lt;/strong&gt;：在理想低通（无噪声，带宽受限）条件下，为了避免码间串扰，极限码元传输速率为&lt;code&gt;2W Baud&lt;/code&gt;，W 是信道带宽，单位是&lt;code&gt;Hz&lt;/code&gt;。(&lt;code&gt;只有在奈氏准则和香农定理中的带宽采用Hz&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;理想低通信道下的极限数据传输率 =$2Wlog_2V$(b/s)
W:带宽，V：码元的离散电平数目（几种码元）&lt;/p&gt;
&lt;p&gt;1.在任何信道中，&lt;code&gt;码元传输的速率是有上限的&lt;/code&gt;。若传输速率超过此上限，就会出现严重的码间串扰问题，使接收端对码元的完全正确识别成为不可能。
2.信道的&lt;code&gt;频带越宽&lt;/code&gt;（即能通过的信号高频分量越多），就可以用更高的速率进行码元的有效传输。
3.&lt;strong&gt;奈氏准则给出了码元传输速率的限制，但并没有对信息传输速率给出限制&lt;/strong&gt;。
4.由于码元的传输速率受奈氏准则的制约，所以要提高数据的传输速率，就必须设法使每个码元能携带更多个比特的信息量，这就需要采用&lt;code&gt;多元制的调制&lt;/code&gt;方法。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;练习：在无噪声的情况下，若某通信链路的带宽为3kHz，采用4个相位，每个相位具有4种振幅的QAM调制技术，则该通信链路的最大数据传输率是多少？
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;答：信号有4 x 4=16种变化
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;最大数据传输率=2 x 3k x4=24kb/s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;香农定理&#34;&gt;香农定理&lt;/h3&gt;
&lt;p&gt;噪声存在于所有的电子设备和通信信道中。由于噪声随机产生，它的瞬时值有时会很大，因此噪声会使接收端对码元的判决产生错误。但是噪声的影响是相对的，若信号较强，那么噪声影响相对较小。因此，&lt;code&gt;信噪比&lt;/code&gt;就很重要。&lt;/p&gt;
&lt;p&gt;信噪比 = &lt;code&gt;信号&lt;/code&gt;的平均功率 / &lt;code&gt;噪声&lt;/code&gt;的平均功率，常记为 S/N，并用分贝（dB）作为度量单位，即：
$$dB = 10log_{10}(S/N)$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;香农定理&lt;/strong&gt;：在&lt;code&gt;带宽受限&lt;/code&gt;且&lt;code&gt;有噪声&lt;/code&gt;的信道中，为了不产生误差，信息的数据传输速率有上限值。
信道的极限传输速率 = $Wlog_2(1+S/N)$(b/s)&lt;/p&gt;
&lt;p&gt;1.信道的&lt;code&gt;带宽&lt;/code&gt;或信道中的&lt;code&gt;信噪比越大&lt;/code&gt;，则信息的&lt;code&gt;极限传输速率就越高&lt;/code&gt;。
2.对一定的传输带宽和一 - 定的信噪比，信息传输速率的上限就确定了。
3.只要信息的传输速率&lt;code&gt;低于信道的极限传输速率&lt;/code&gt;，就一定能找到某种方法来实现&lt;code&gt;无差错的传输&lt;/code&gt;。
4.香农定理得出的为极限信息传输速率，实际信道能达到的传输速率要比它低不少。
5.从香农定理可以看出，若信道带宽 W 或信噪比 S/N 没有上限（不可能），那么信道的极限信息传输速率也就没有上限。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;练习：电话系统的典型参数是信道带宽为3000Hz，信噪比为30dB，则该系统最大数据传输速率是多少？
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;答：30dB=10log1o（S/N）则S/N=1000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;信道的极限数据传输速率=Wlog2（1+S/N）=3000 x log2（1+1000）&amp;gt;30kb/s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;当题目中既给出了码元信息，又给了信噪比信息，就需要用两个公式都算一下，取最小值&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;题目：二进制信号在信噪比为127：1的4kHz信道上传输，最大的数据速率可达到多少？
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;答：
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Nice：2 X 4000X log2=8000b/s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;香浓：4000 log2（1+127）=28000b/s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;编码与调制&#34;&gt;编码与调制&lt;/h3&gt;
&lt;h4 id=&#34;基带信号与宽带信号&#34;&gt;基带信号与宽带信号&lt;/h4&gt;
&lt;p&gt;信道：信号的传输媒介。一般用来表示向某一个方向传送信息的介质，因此一条通信线路往往包含一条发送信道和一条接收信道。&lt;/p&gt;
&lt;p&gt;信道上传输的信号&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基带信号
将数字信号 1 和 0 直接用两种不同的电压表示，再送到&lt;code&gt;数字信道&lt;/code&gt;上去传输（基带传输）来&lt;code&gt;自信源的信号&lt;/code&gt;，像计算机输出的代表各种文字或图像文件的数据信号都属于基带信号。基带信号就是发出的&lt;code&gt;直接表达了要传输的信息的信号&lt;/code&gt;，比如我们说话的声波就是基带信号。&lt;/li&gt;
&lt;li&gt;宽带信号
将基带信号进行调制后形成的频分复用模拟信号，再传送到模拟信道上去传输（宽带传输）。
把基带信号经过&lt;code&gt;载波调制&lt;/code&gt;后，把信号的&lt;code&gt;频率范围搬移&lt;/code&gt;到&lt;code&gt;较高的频段&lt;/code&gt;以便在信道中传输（即仅在一段频率范围内能够通过信道）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在&lt;strong&gt;传输距离较近时&lt;/strong&gt;，计算机网络采用&lt;strong&gt;基带传输方式&lt;/strong&gt;（近距离衰减小，从而信号内容不易发生变化）
在&lt;strong&gt;传输距离较远时&lt;/strong&gt;，计算机网络采用&lt;strong&gt;宽带传输方式&lt;/strong&gt;（远距离衰减大，即使信号变化大也能最后过滤出来基带信号）&lt;/p&gt;
&lt;h4 id=&#34;编码与调制-1&#34;&gt;编码与调制&lt;/h4&gt;
&lt;p&gt;编码：数字数据-》数字信号（数字发送器）
调制：数字数据-》模拟信号（调制器）&lt;/p&gt;
&lt;p&gt;编码：模拟数据-》数字信号（PCM 编码器）
调制：模拟数据-》模拟信号（放大器调制器）&lt;/p&gt;
&lt;h5 id=&#34;数字数据-数字信号&#34;&gt;数字数据-》数字信号&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;非归零编码（NRZ）（&lt;code&gt;高1低0&lt;/code&gt;）
编码容易实现，但没有检错功能，且&lt;code&gt;无法判断一-个码元的开始和结束&lt;/code&gt;，以至于收发双方难以保持同步。一个码元内电平不会跳变。&lt;/li&gt;
&lt;li&gt;归零编码（RZ）
信号电平在一一个码元之内都要恢复到零的这种编码成编码方式。&lt;/li&gt;
&lt;li&gt;反向不归零编码（NRZI）
信号电平翻转表示 0，信号电平不。变表示 1。一个码元内电平不会跳变。&lt;/li&gt;
&lt;li&gt;曼彻斯特编码（&lt;code&gt;前高后低1，前低后高为0&lt;/code&gt;）
将一个码元分成两个相等的间隔，前一个间隔为低电平后 - 一个间隔为高电平表示码元 1；码元 0 则正好相反。也可以采用相反的规定。该编码的特点是在每&amp;ndash;个码元的中间出现电平跳变，位中间的跳变既作时钟信号（可用于同步），又作数据信号，但它所占的频带宽度是原始的基带宽度的两倍。每一个码元都被调成两个电平，所以&lt;code&gt;数据传输速率只有调制速率的1/2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;差分曼彻斯特编码（&lt;code&gt;同1异0&lt;/code&gt;）
常用于局域网传输，其规则是：若码元为 1，则前半个码元的电平与上一个码元的后半个码元的电平相同，若为 0，则相反。该编码的特点是，在每个码元的中间，都有一次电平的跳转，可以实现&lt;code&gt;自同步&lt;/code&gt;，且&lt;code&gt;抗干扰性强于曼彻斯特编码&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;4B/5B编码
比特流中插入额外的比特以打破连串的 0 或 1，就是用 5 个比特来编码 4 个比特的数据，之后再传给接收方，因此称为 4B/5B。编码效率为 80%。&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//2023/06/10/2f59ff009651b57f47c33471a365e382.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/2f59ff009651b57f47c33471a365e382.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//2023/06/10/8badc86efb3d58ed7a7e9a2ccde5afa9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/8badc86efb3d58ed7a7e9a2ccde5afa9.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;h5 id=&#34;数字数据-模拟信号&#34;&gt;数字数据-》模拟信号&lt;/h5&gt;
&lt;p&gt;数字数据调制技术在发送端将&lt;code&gt;数字信号转换为模拟信号&lt;/code&gt;，而在接收端将&lt;code&gt;模拟信号还原为数字信号&lt;/code&gt;，分别对应于调制解调器的&lt;code&gt;调制&lt;/code&gt;和&lt;code&gt;解调&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//2023/06/10/374733a7484818df12c798c997ecab87.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/374733a7484818df12c798c997ecab87.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 class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;例：某通信链路的波特率是1200Baud，采用4个相位，每个相位有4种振幅的QAM调制技术，则该链路的信息传输速率是多少？
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;答：4个相位4种振幅也就是说有16个码元。16个码元也就是16个状态，需要4位来表示。也就是1码元对应4bit。题目中波特率是1200，也就说明1200*4=4800bit/s。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h5 id=&#34;模拟数据-数字信号&#34;&gt;模拟数据-》数字信号&lt;/h5&gt;
&lt;p&gt;计算机内部处理的是二进制数据，处理的都是&lt;code&gt;数字音频&lt;/code&gt;，所以需要将模拟音频通过采样、量化转换成有限个数字表示的离散序列（即实现&lt;code&gt;音频数字化&lt;/code&gt;）。
最典型的例子就是对音频信号进行编码的&lt;code&gt;脉码调制&lt;/code&gt;（PCM），在计算机应用中，能够达到&lt;code&gt;最高保真水平&lt;/code&gt;的就是 PCM 编码，被广泛用于素材保存及音乐欣赏，CD、DVD 以及我们常见的&lt;code&gt;WAV文件&lt;/code&gt;中均有应用。它主要包括三步：抽样、量化、编码。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;抽样
对模拟信号&lt;code&gt;周期性扫描&lt;/code&gt;，把时间上连续的信号变成时间上离散的信号。为了使所得的离散信号能无失真地代表被抽样的模拟数据，要使用采样定理采样：&lt;code&gt;采样频率 &amp;gt;= 2*信号最高频率&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;量化
把抽样取得的电平幅值按照一定的分级标度转化为对应的数字值，并&lt;code&gt;取整数&lt;/code&gt;，这就把连续的电平幅值转换为离散的数字量。&lt;/li&gt;
&lt;li&gt;编码
把量化的结果转换为与之对应的二进制编码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;模拟数据-模拟信号&#34;&gt;模拟数据-》模拟信号&lt;/h5&gt;
&lt;p&gt;为了实现&lt;code&gt;传输的有效性&lt;/code&gt;，可能需要较高的频率。这种调制方式还可以使用频分复用技术，充分利用带宽资源。在电话机和本地交换机所传输的信号是采用模拟信号传输模拟数据的方式；模拟的声音数据是加载到模拟的载波信号中传输的。&lt;/p&gt;
&lt;h4 id=&#34;传输介质及分类&#34;&gt;传输介质及分类&lt;/h4&gt;
&lt;p&gt;传输介质也称传输媒体/传输媒介，它就是数据传输系统中在发送设备和接收设备之间的&lt;code&gt;物理通路&lt;/code&gt;。传输媒体并不是物理层。
传输媒体在物理层的下面，因为物理层是体系结构的第一层，因此有时称传输媒体为 0 层。在传输媒体中传输的是信号，但传输媒体并不知道所传输的信号代表什么意思。但物理层规定了&lt;code&gt;电气特性&lt;/code&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;/ul&gt;
&lt;h5 id=&#34;双绞线&#34;&gt;双绞线&lt;/h5&gt;
&lt;p&gt;双绞线是古老、又最常用的传输介质，它由两根采用一定规则并排绞合的、相互绝缘的铜导线组成。&lt;code&gt;绞合可以减少对相邻导线的电磁干扰&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//2023/06/10/f2e78b6a0ef702d0b739df620de5315f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/f2e78b6a0ef702d0b739df620de5315f.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;code&gt;模拟传输&lt;/code&gt;，要用&lt;code&gt;放大器&lt;/code&gt;放大衰减的信号；对于&lt;code&gt;数字传输&lt;/code&gt;，要用&lt;code&gt;中继器&lt;/code&gt;将失真的信号整形。&lt;/p&gt;
&lt;h5 id=&#34;同轴电缆&#34;&gt;同轴电缆&lt;/h5&gt;
&lt;p&gt;同轴电缆由&lt;code&gt;导体铜质芯线&lt;/code&gt;、&lt;code&gt;绝缘层&lt;/code&gt;、&lt;code&gt;网状编织屏蔽层&lt;/code&gt;和&lt;code&gt;塑料外层构成&lt;/code&gt;。按特性阻抗数值的不同，通常将同轴电缆分为两类：50$\Omega$同轴 电缆和 75$\Omega$同轴电缆。其中，50$\Omega$同轴电缆主要用于传送基带数字信号，又称为&lt;code&gt;基带同轴电缆&lt;/code&gt;，它在局域网中得到广泛应用；75$\Omega$同轴电缆主要用于传送宽带信号，又称为&lt;code&gt;宽带同轴电缆&lt;/code&gt;，它主要用于有线电视系统。&lt;/p&gt;
&lt;p&gt;由于外导体屏蔽层的作用，同轴电缆&lt;code&gt;抗干扰特性比双绞线好&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//2023/06/10/26ee48fc0439a698939d57379bd37f75.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/26ee48fc0439a698939d57379bd37f75.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;h5 id=&#34;光纤&#34;&gt;光纤&lt;/h5&gt;
&lt;p&gt;光纤通信就是利用光导纤维（简称光纤）传递&lt;code&gt;光脉冲&lt;/code&gt;来进行通信。有光脉冲表示 1，无光脉冲表示 0。而可见光的频率大约是 108MHz，因此光纤通信系统的&lt;code&gt;带宽远远大于&lt;/code&gt;目前其他各种传输媒体的带宽。&lt;/p&gt;
&lt;p&gt;光纤主要由&lt;code&gt;纤芯&lt;/code&gt;（实心的！）和包层构成，光波&lt;code&gt;通过纤芯进行传导&lt;/code&gt;，包层较纤芯有较低的折射率。当光线从高折射率的介质射向低折射率的介质时，其折射角将大于入射角。因此，如果入射角足够大，就会出现&lt;code&gt;全反射&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//2023/06/10/17d42048d440c46bfca2dd72b5647f52.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/17d42048d440c46bfca2dd72b5647f52.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;code&gt;远距离&lt;/code&gt;传输特别经济。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;抗&lt;/code&gt;雷电和电磁&lt;code&gt;干扰性&lt;/code&gt;能好。&lt;/li&gt;
&lt;li&gt;无串音干扰，&lt;code&gt;保密性好&lt;/code&gt;，也不易被窃听或截取数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;无线电波&#34;&gt;无线电波&lt;/h5&gt;
&lt;p&gt;信号&lt;code&gt;所有方向&lt;/code&gt;都能传播&lt;/p&gt;
&lt;p&gt;较强&lt;code&gt;穿透能力&lt;/code&gt;，可传远距离，广泛用于通信领域（如手机通信）。&lt;/p&gt;
&lt;h5 id=&#34;微波&#34;&gt;微波&lt;/h5&gt;
&lt;p&gt;信号&lt;code&gt;固定&lt;/code&gt;方向传播&lt;/p&gt;
&lt;p&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;li&gt;广播通信和多址通信&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;缺点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传播时延长（250-270ms）&lt;/li&gt;
&lt;li&gt;受气候影响大（eg：强风太阳黑子爆发、日凌）X 信&lt;/li&gt;
&lt;li&gt;误码率较高&lt;/li&gt;
&lt;li&gt;成本高&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;红外线激光&#34;&gt;红外线，激光&lt;/h5&gt;
&lt;p&gt;信号&lt;code&gt;固定&lt;/code&gt;方向传播&lt;/p&gt;
&lt;p&gt;要把要传输的信号分别&lt;code&gt;转换为各自的信号格式&lt;/code&gt;，即红外光信号和激光信号，再在空间中传播。&lt;/p&gt;
&lt;h4 id=&#34;物理层设备&#34;&gt;物理层设备&lt;/h4&gt;
&lt;h5 id=&#34;中继器&#34;&gt;中继器&lt;/h5&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//2023/06/10/4d060bedc81f82a7fac50aa8453e55c1.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/4d060bedc81f82a7fac50aa8453e55c1.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;strong&gt;诞生原因&lt;/strong&gt;：由于存在损耗，在线路上传输的信号功率会逐渐衰减，衰减到&amp;ndash;定程度时将造成信号失真，因此会导致接收错误。
&lt;strong&gt;中继器的功能&lt;/strong&gt;(&lt;code&gt;再生数字信号&lt;/code&gt;)：对信号进行&lt;code&gt;再生&lt;/code&gt;和&lt;code&gt;还原&lt;/code&gt;，对衰减的信号进行放大，保持与原数据相同，以增加信号传输的距离，延长网络的长度。
&lt;strong&gt;中继器两端&lt;/strong&gt;：适用于完全相同的两类网络的互连，速率要相同，只做转发不做检测，可以连接不同设备，两端是同一个协议
&lt;strong&gt;5-4-3&lt;/strong&gt;规则：网络标准中都对信号的延迟范围作了具体的规定，因而中继器只能在规定的范围内进行，否则会网络故障。
&lt;code&gt;5个网段-4个中继器-3个设备&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//2023/06/10/2881413b433a3331f4f45bbe9859d1b9.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/2881413b433a3331f4f45bbe9859d1b9.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;h5 id=&#34;集线器多口中继器&#34;&gt;集线器（多口中继器）&lt;/h5&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//2023/06/10/1c56eea785903e1e349f8dfc242a80e2.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/1c56eea785903e1e349f8dfc242a80e2.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;
</description>
      <content:encoded><![CDATA[<h2 id="物理层">物理层</h2>
<h3 id="基本概念">基本概念</h3>
<p>物理层解决如何在连接各种计算机的传输媒体上<code>传输数据比特流</code>，而不是指具体的传输媒体。
物理层主要任务：确定与传输媒体<code>接口</code>有关的一些特性</p>
<ul>
<li>机械特性：定义物理连接的特性，规定物理连接时所采用的规格、接口形状、引线数目、<code>引脚数量</code>和排列情况。</li>
<li>电气特性：规定传输二进制位时，线路上信号的<code>电压范围</code>、阻抗匹配、传输<code>速率</code>和<code>距离</code>限制等。</li>
<li>功能特性：指明某条线上出现的某一<code>电平表示何种意义</code>，接口部件的信号线的用途。</li>
<li>规程特性（过程特性）：定义各条物理线路的工作<code>规程和时序</code>关系</li>
</ul>
<h3 id="数据通信基础概念">数据通信基础概念</h3>
<h4 id="典型的数据通信模型">典型的数据通信模型</h4>
<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//2023/06/10/dcfc2ba8a7d5d1e222e8be2ea6e6b78b.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/dcfc2ba8a7d5d1e222e8be2ea6e6b78b.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>
</ul>
<p>信源：产生和发送数据的源头。
信道：信号的传输媒介。一般用来表示向某一个方向传送信息的介质，因此一条通信线路往往包含一条发送信道和一条接收信道。</p>
<h4 id="通信方式">通信方式</h4>
<p>从通信双方信息的交互方式看，可以有三种基本方式：</p>
<ul>
<li>单工通信只有一个方向的通信而没有反方向的交互，仅需要一一条信道。</li>
<li>半双工通信通信的双方都可以发送或接收信息，但任何一方都不能同时发送和接收，需要两条信道。</li>
<li>全双工通信通信双方可以同时发送和接受信息，也需要两条信道。</li>
</ul>
<h4 id="传输方式">传输方式</h4>
<ul>
<li>串行传输
<ul>
<li>速度慢，费用低，适合远距离</li>
</ul>
</li>
<li>并行传输
<ul>
<li>速度快，费用高，适合近距离</li>
</ul>
</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//2023/06/10/c45684f0736a31c6a9515c2f546187b3.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/c45684f0736a31c6a9515c2f546187b3.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>
<h4 id="码元">码元</h4>
<p><code>码元</code>是指用一个<code>固定时长</code>的<code>信号波形</code>（数字脉冲），代表不同离散数值的基本波形，是数字通信中数字信号的<code>计量单位</code>，这个时长内的信号称为<code>k进制码元</code>，而该时长称为<code>码元宽度</code>。当码元的离散状态有 M 个时（M 大于 2），此时码元为 M 进制码元。
<code>1码元可以携带多个比特的信息量</code>。例如，在使用二进制编码时，只有两种不同的码元，一种代表 0 状态，另 - 一种代表 1 状态。在四进制码元中，一个码元就由 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//2023/06/10/4a5eda03b690c8ebe8bd258be92a0d97.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/4a5eda03b690c8ebe8bd258be92a0d97.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>
<h4 id="速率波特带宽">速率，波特，带宽</h4>
<p>速率也叫数据率，是指数据的<code>传输速率</code>，表示单位时间内传输的数据量。可以用码元传输速率和信息传输速率表示。
<code>传输速率</code>是主机上发出的速率，而<code>传播速率</code>是在信道上的传播速率。两者是不同的概念</p>
<ul>
<li><strong>码元传输速率</strong><code>1s传输多少个码元</code>：别名<code>码元速率</code>、波形速率、调制速率、符号速率等，它表示单位时间内数字通信系统所传输的码元个数（也可称为<code>脉冲个数</code>或<code>信号变化的次数</code>），单位是<code>波特</code>（Baud）。1 波特表示数字通信系统每秒传输一个码元。这里的码元可以是多进制的，也可以是二进制的，但码元速率与进制数无关。</li>
<li><strong>信息传输速率</strong><code>1s传输多少个比特</code>：别名<code>信息速率</code>、比特率等，表示单位时间内数字通信系统传输的二进制码元个数（即比特数），单位是比特/秒（b/s）。</li>
</ul>
<p><strong>关系：若一个码元携带<code>n bit</code>的信息量，则<code>M Baud</code>的码元传输速率所对应的信息传输速率为<code>M * n bit/s</code>。</strong></p>
<p>带宽 (是个理想值)：表示在单位时间内从网络中的某一点到另一 点所能通过的“<code>最高数据率</code>”，常用来表示网络的通信线路所能传输数据的能力。单位是 b/s。</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//2023/06/10/aa1b08e1a99ea7fd84c0375d303b3afa.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/aa1b08e1a99ea7fd84c0375d303b3afa.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>4 进制表示码元有 4 种波形，只需要 2 位就可以表示 4 种波形，同理 16 进制需要 4 位表示。</p>
<h3 id="奈氏准则">奈氏准则</h3>
<h4 id="失真">失真</h4>
<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//2023/06/10/8c49c7fd87d8971741228463c0bb1639.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/8c49c7fd87d8971741228463c0bb1639.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>
<li>传输媒体质量</li>
</ul>
<h4 id="码间串扰">码间串扰</h4>
<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//2023/06/10/74aab08e792d5be066e1fb82ad251ab6.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/74aab08e792d5be066e1fb82ad251ab6.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>
<p><strong>码间串扰</strong>：接收端收到的信号波形<code>失去了码元之间清晰界限</code>的现象。</p>
<h4 id="奈氏准则-1">奈氏准则</h4>
<p><strong>奈氏准则</strong>：在理想低通（无噪声，带宽受限）条件下，为了避免码间串扰，极限码元传输速率为<code>2W Baud</code>，W 是信道带宽，单位是<code>Hz</code>。(<code>只有在奈氏准则和香农定理中的带宽采用Hz</code>)</p>
<p>理想低通信道下的极限数据传输率 =$2Wlog_2V$(b/s)
W:带宽，V：码元的离散电平数目（几种码元）</p>
<p>1.在任何信道中，<code>码元传输的速率是有上限的</code>。若传输速率超过此上限，就会出现严重的码间串扰问题，使接收端对码元的完全正确识别成为不可能。
2.信道的<code>频带越宽</code>（即能通过的信号高频分量越多），就可以用更高的速率进行码元的有效传输。
3.<strong>奈氏准则给出了码元传输速率的限制，但并没有对信息传输速率给出限制</strong>。
4.由于码元的传输速率受奈氏准则的制约，所以要提高数据的传输速率，就必须设法使每个码元能携带更多个比特的信息量，这就需要采用<code>多元制的调制</code>方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">练习：在无噪声的情况下，若某通信链路的带宽为3kHz，采用4个相位，每个相位具有4种振幅的QAM调制技术，则该通信链路的最大数据传输率是多少？
</span></span><span class="line"><span class="cl">答：信号有4 x 4=16种变化
</span></span><span class="line"><span class="cl">最大数据传输率=2 x 3k x4=24kb/s
</span></span></code></pre></div><h3 id="香农定理">香农定理</h3>
<p>噪声存在于所有的电子设备和通信信道中。由于噪声随机产生，它的瞬时值有时会很大，因此噪声会使接收端对码元的判决产生错误。但是噪声的影响是相对的，若信号较强，那么噪声影响相对较小。因此，<code>信噪比</code>就很重要。</p>
<p>信噪比 = <code>信号</code>的平均功率 / <code>噪声</code>的平均功率，常记为 S/N，并用分贝（dB）作为度量单位，即：
$$dB = 10log_{10}(S/N)$$</p>
<p><strong>香农定理</strong>：在<code>带宽受限</code>且<code>有噪声</code>的信道中，为了不产生误差，信息的数据传输速率有上限值。
信道的极限传输速率 = $Wlog_2(1+S/N)$(b/s)</p>
<p>1.信道的<code>带宽</code>或信道中的<code>信噪比越大</code>，则信息的<code>极限传输速率就越高</code>。
2.对一定的传输带宽和一 - 定的信噪比，信息传输速率的上限就确定了。
3.只要信息的传输速率<code>低于信道的极限传输速率</code>，就一定能找到某种方法来实现<code>无差错的传输</code>。
4.香农定理得出的为极限信息传输速率，实际信道能达到的传输速率要比它低不少。
5.从香农定理可以看出，若信道带宽 W 或信噪比 S/N 没有上限（不可能），那么信道的极限信息传输速率也就没有上限。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">练习：电话系统的典型参数是信道带宽为3000Hz，信噪比为30dB，则该系统最大数据传输速率是多少？
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">答：30dB=10log1o（S/N）则S/N=1000
</span></span><span class="line"><span class="cl">信道的极限数据传输速率=Wlog2（1+S/N）=3000 x log2（1+1000）&gt;30kb/s
</span></span></code></pre></div><p><strong>当题目中既给出了码元信息，又给了信噪比信息，就需要用两个公式都算一下，取最小值</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">题目：二进制信号在信噪比为127：1的4kHz信道上传输，最大的数据速率可达到多少？
</span></span><span class="line"><span class="cl">答：
</span></span><span class="line"><span class="cl">Nice：2 X 4000X log2=8000b/s
</span></span><span class="line"><span class="cl">香浓：4000 log2（1+127）=28000b/s
</span></span></code></pre></div><h3 id="编码与调制">编码与调制</h3>
<h4 id="基带信号与宽带信号">基带信号与宽带信号</h4>
<p>信道：信号的传输媒介。一般用来表示向某一个方向传送信息的介质，因此一条通信线路往往包含一条发送信道和一条接收信道。</p>
<p>信道上传输的信号</p>
<ul>
<li>基带信号
将数字信号 1 和 0 直接用两种不同的电压表示，再送到<code>数字信道</code>上去传输（基带传输）来<code>自信源的信号</code>，像计算机输出的代表各种文字或图像文件的数据信号都属于基带信号。基带信号就是发出的<code>直接表达了要传输的信息的信号</code>，比如我们说话的声波就是基带信号。</li>
<li>宽带信号
将基带信号进行调制后形成的频分复用模拟信号，再传送到模拟信道上去传输（宽带传输）。
把基带信号经过<code>载波调制</code>后，把信号的<code>频率范围搬移</code>到<code>较高的频段</code>以便在信道中传输（即仅在一段频率范围内能够通过信道）</li>
</ul>
<p>在<strong>传输距离较近时</strong>，计算机网络采用<strong>基带传输方式</strong>（近距离衰减小，从而信号内容不易发生变化）
在<strong>传输距离较远时</strong>，计算机网络采用<strong>宽带传输方式</strong>（远距离衰减大，即使信号变化大也能最后过滤出来基带信号）</p>
<h4 id="编码与调制-1">编码与调制</h4>
<p>编码：数字数据-》数字信号（数字发送器）
调制：数字数据-》模拟信号（调制器）</p>
<p>编码：模拟数据-》数字信号（PCM 编码器）
调制：模拟数据-》模拟信号（放大器调制器）</p>
<h5 id="数字数据-数字信号">数字数据-》数字信号</h5>
<ul>
<li>非归零编码（NRZ）（<code>高1低0</code>）
编码容易实现，但没有检错功能，且<code>无法判断一-个码元的开始和结束</code>，以至于收发双方难以保持同步。一个码元内电平不会跳变。</li>
<li>归零编码（RZ）
信号电平在一一个码元之内都要恢复到零的这种编码成编码方式。</li>
<li>反向不归零编码（NRZI）
信号电平翻转表示 0，信号电平不。变表示 1。一个码元内电平不会跳变。</li>
<li>曼彻斯特编码（<code>前高后低1，前低后高为0</code>）
将一个码元分成两个相等的间隔，前一个间隔为低电平后 - 一个间隔为高电平表示码元 1；码元 0 则正好相反。也可以采用相反的规定。该编码的特点是在每&ndash;个码元的中间出现电平跳变，位中间的跳变既作时钟信号（可用于同步），又作数据信号，但它所占的频带宽度是原始的基带宽度的两倍。每一个码元都被调成两个电平，所以<code>数据传输速率只有调制速率的1/2</code>。</li>
<li>差分曼彻斯特编码（<code>同1异0</code>）
常用于局域网传输，其规则是：若码元为 1，则前半个码元的电平与上一个码元的后半个码元的电平相同，若为 0，则相反。该编码的特点是，在每个码元的中间，都有一次电平的跳转，可以实现<code>自同步</code>，且<code>抗干扰性强于曼彻斯特编码</code>。</li>
<li>4B/5B编码
比特流中插入额外的比特以打破连串的 0 或 1，就是用 5 个比特来编码 4 个比特的数据，之后再传给接收方，因此称为 4B/5B。编码效率为 80%。</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//2023/06/10/2f59ff009651b57f47c33471a365e382.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/2f59ff009651b57f47c33471a365e382.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//2023/06/10/8badc86efb3d58ed7a7e9a2ccde5afa9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/8badc86efb3d58ed7a7e9a2ccde5afa9.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>
<h5 id="数字数据-模拟信号">数字数据-》模拟信号</h5>
<p>数字数据调制技术在发送端将<code>数字信号转换为模拟信号</code>，而在接收端将<code>模拟信号还原为数字信号</code>，分别对应于调制解调器的<code>调制</code>和<code>解调</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//2023/06/10/374733a7484818df12c798c997ecab87.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/374733a7484818df12c798c997ecab87.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 class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">例：某通信链路的波特率是1200Baud，采用4个相位，每个相位有4种振幅的QAM调制技术，则该链路的信息传输速率是多少？
</span></span><span class="line"><span class="cl">答：4个相位4种振幅也就是说有16个码元。16个码元也就是16个状态，需要4位来表示。也就是1码元对应4bit。题目中波特率是1200，也就说明1200*4=4800bit/s。
</span></span></code></pre></div><h5 id="模拟数据-数字信号">模拟数据-》数字信号</h5>
<p>计算机内部处理的是二进制数据，处理的都是<code>数字音频</code>，所以需要将模拟音频通过采样、量化转换成有限个数字表示的离散序列（即实现<code>音频数字化</code>）。
最典型的例子就是对音频信号进行编码的<code>脉码调制</code>（PCM），在计算机应用中，能够达到<code>最高保真水平</code>的就是 PCM 编码，被广泛用于素材保存及音乐欣赏，CD、DVD 以及我们常见的<code>WAV文件</code>中均有应用。它主要包括三步：抽样、量化、编码。</p>
<ul>
<li>抽样
对模拟信号<code>周期性扫描</code>，把时间上连续的信号变成时间上离散的信号。为了使所得的离散信号能无失真地代表被抽样的模拟数据，要使用采样定理采样：<code>采样频率 &gt;= 2*信号最高频率</code></li>
<li>量化
把抽样取得的电平幅值按照一定的分级标度转化为对应的数字值，并<code>取整数</code>，这就把连续的电平幅值转换为离散的数字量。</li>
<li>编码
把量化的结果转换为与之对应的二进制编码。</li>
</ul>
<h5 id="模拟数据-模拟信号">模拟数据-》模拟信号</h5>
<p>为了实现<code>传输的有效性</code>，可能需要较高的频率。这种调制方式还可以使用频分复用技术，充分利用带宽资源。在电话机和本地交换机所传输的信号是采用模拟信号传输模拟数据的方式；模拟的声音数据是加载到模拟的载波信号中传输的。</p>
<h4 id="传输介质及分类">传输介质及分类</h4>
<p>传输介质也称传输媒体/传输媒介，它就是数据传输系统中在发送设备和接收设备之间的<code>物理通路</code>。传输媒体并不是物理层。
传输媒体在物理层的下面，因为物理层是体系结构的第一层，因此有时称传输媒体为 0 层。在传输媒体中传输的是信号，但传输媒体并不知道所传输的信号代表什么意思。但物理层规定了<code>电气特性</code>，因此能够识别所传送的比特流。</p>
<p>传输介质</p>
<ul>
<li>导向性：电磁波被导向沿着固体媒介传播</li>
<li>非导向性：空气，海水等</li>
</ul>
<h5 id="双绞线">双绞线</h5>
<p>双绞线是古老、又最常用的传输介质，它由两根采用一定规则并排绞合的、相互绝缘的铜导线组成。<code>绞合可以减少对相邻导线的电磁干扰</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//2023/06/10/f2e78b6a0ef702d0b739df620de5315f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/f2e78b6a0ef702d0b739df620de5315f.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>双绞线价格便宜，是最常用的传输介质之一，在局域网和传统电话网中普遍使用。模拟传输和数字传输都可以使用双绞线，其通信距离一般为几公里到数十公里。距离太远时，对于<code>模拟传输</code>，要用<code>放大器</code>放大衰减的信号；对于<code>数字传输</code>，要用<code>中继器</code>将失真的信号整形。</p>
<h5 id="同轴电缆">同轴电缆</h5>
<p>同轴电缆由<code>导体铜质芯线</code>、<code>绝缘层</code>、<code>网状编织屏蔽层</code>和<code>塑料外层构成</code>。按特性阻抗数值的不同，通常将同轴电缆分为两类：50$\Omega$同轴 电缆和 75$\Omega$同轴电缆。其中，50$\Omega$同轴电缆主要用于传送基带数字信号，又称为<code>基带同轴电缆</code>，它在局域网中得到广泛应用；75$\Omega$同轴电缆主要用于传送宽带信号，又称为<code>宽带同轴电缆</code>，它主要用于有线电视系统。</p>
<p>由于外导体屏蔽层的作用，同轴电缆<code>抗干扰特性比双绞线好</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//2023/06/10/26ee48fc0439a698939d57379bd37f75.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/26ee48fc0439a698939d57379bd37f75.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>
<h5 id="光纤">光纤</h5>
<p>光纤通信就是利用光导纤维（简称光纤）传递<code>光脉冲</code>来进行通信。有光脉冲表示 1，无光脉冲表示 0。而可见光的频率大约是 108MHz，因此光纤通信系统的<code>带宽远远大于</code>目前其他各种传输媒体的带宽。</p>
<p>光纤主要由<code>纤芯</code>（实心的！）和包层构成，光波<code>通过纤芯进行传导</code>，包层较纤芯有较低的折射率。当光线从高折射率的介质射向低折射率的介质时，其折射角将大于入射角。因此，如果入射角足够大，就会出现<code>全反射</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//2023/06/10/17d42048d440c46bfca2dd72b5647f52.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/17d42048d440c46bfca2dd72b5647f52.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>传输损耗小，中继距离长，对<code>远距离</code>传输特别经济。</li>
<li><code>抗</code>雷电和电磁<code>干扰性</code>能好。</li>
<li>无串音干扰，<code>保密性好</code>，也不易被窃听或截取数据。</li>
</ul>
<h5 id="无线电波">无线电波</h5>
<p>信号<code>所有方向</code>都能传播</p>
<p>较强<code>穿透能力</code>，可传远距离，广泛用于通信领域（如手机通信）。</p>
<h5 id="微波">微波</h5>
<p>信号<code>固定</code>方向传播</p>
<p>微波通信频率较高、频段范围宽，因此数据率很高</p>
<p>优点：</p>
<ul>
<li>通信容量大</li>
<li>距离远</li>
<li>覆盖广</li>
<li>广播通信和多址通信</li>
</ul>
<p>缺点：</p>
<ul>
<li>传播时延长（250-270ms）</li>
<li>受气候影响大（eg：强风太阳黑子爆发、日凌）X 信</li>
<li>误码率较高</li>
<li>成本高</li>
</ul>
<h5 id="红外线激光">红外线，激光</h5>
<p>信号<code>固定</code>方向传播</p>
<p>要把要传输的信号分别<code>转换为各自的信号格式</code>，即红外光信号和激光信号，再在空间中传播。</p>
<h4 id="物理层设备">物理层设备</h4>
<h5 id="中继器">中继器</h5>
<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//2023/06/10/4d060bedc81f82a7fac50aa8453e55c1.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/4d060bedc81f82a7fac50aa8453e55c1.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>
<strong>诞生原因</strong>：由于存在损耗，在线路上传输的信号功率会逐渐衰减，衰减到&ndash;定程度时将造成信号失真，因此会导致接收错误。
<strong>中继器的功能</strong>(<code>再生数字信号</code>)：对信号进行<code>再生</code>和<code>还原</code>，对衰减的信号进行放大，保持与原数据相同，以增加信号传输的距离，延长网络的长度。
<strong>中继器两端</strong>：适用于完全相同的两类网络的互连，速率要相同，只做转发不做检测，可以连接不同设备，两端是同一个协议
<strong>5-4-3</strong>规则：网络标准中都对信号的延迟范围作了具体的规定，因而中继器只能在规定的范围内进行，否则会网络故障。
<code>5个网段-4个中继器-3个设备</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//2023/06/10/2881413b433a3331f4f45bbe9859d1b9.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/2881413b433a3331f4f45bbe9859d1b9.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>
<h5 id="集线器多口中继器">集线器（多口中继器）</h5>
<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//2023/06/10/1c56eea785903e1e349f8dfc242a80e2.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/06/10/1c56eea785903e1e349f8dfc242a80e2.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>
]]></content:encoded>
    </item>
    <item>
      <title>VSCode 插件 REST Client 使用文档</title>
      <link>https://lifeislife.cn/posts/vscode-%E6%8F%92%E4%BB%B6-rest-client%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3/</link>
      <pubDate>Fri, 24 Mar 2023 19:59:35 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/vscode-%E6%8F%92%E4%BB%B6-rest-client%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3/</guid>
      <description>&lt;p&gt;REST Client 是 VSCode 中一款非常好用的插件，能够帮助开发人员快速、方便地发送 HTTP 请求并查看响应。在本文中，我们将会详细介绍 REST Client 的使用方法。&lt;/p&gt;
&lt;h2 id=&#34;安装-rest-client-插件&#34;&gt;安装 REST Client 插件&lt;/h2&gt;
&lt;p&gt;在 VSCode 中，你可以通过以下步骤安装 REST Client 插件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;打开 VSCode；&lt;/li&gt;
&lt;li&gt;点击左侧的插件图标（Ctrl+Shift+X）；&lt;/li&gt;
&lt;li&gt;搜索“REST Client”插件；&lt;/li&gt;
&lt;li&gt;点击“安装”按钮。&lt;/li&gt;
&lt;li&gt;发送 HTTP 请求&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用 REST Client 插件发送 HTTP 请求非常简单。你只需要创建一个新的&lt;code&gt;.rest&lt;/code&gt;文本文件，将请求信息放入其中，然后使用快捷键&lt;code&gt;Ctrl + Alt + R&lt;/code&gt; 或者右键菜单的 &lt;code&gt;Send Request&lt;/code&gt; 选项发送请求。&lt;/p&gt;
&lt;p&gt;下面是一个简单的 &lt;code&gt;GET&lt;/code&gt; 请求的例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GET https://jsonplaceholder.typicode.com/posts/1 HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个请求会获取 JSONPlaceholder API 中的一篇博客文章。&lt;/p&gt;
&lt;p&gt;如果你想添加请求头或请求体，可以使用以下语法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GET https://jsonplaceholder.typicode.com/posts/1 HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Content-Type: application/json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#34;title&amp;#34;: &amp;#34;foo&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#34;body&amp;#34;: &amp;#34;bar&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#34;userId&amp;#34;: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个请求会在请求头中添加 &lt;code&gt;Content-Type&lt;/code&gt; 头，请求体中包含 JSON 数据。&lt;/p&gt;
&lt;h2 id=&#34;查看响应&#34;&gt;查看响应&lt;/h2&gt;
&lt;p&gt;发送请求后，你可以在编辑器底部看到响应信息。如果你想查看响应头、响应体或状态码等详细信息，可以使用以下语法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;HTTP/1.1 200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Content-Type: application/json; charset=utf-8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#34;userId&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#34;id&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#34;title&amp;#34;: &amp;#34;sunt aut facere repellat provident occaecati excepturi optio reprehenderit&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &amp;#34;body&amp;#34;: &amp;#34;quia et suscipit\nsuscipit...&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，###用来分隔请求和响应，这样你就可以很方便地查看请求和响应的详细信息了。&lt;/p&gt;
&lt;h2 id=&#34;变量&#34;&gt;变量&lt;/h2&gt;
&lt;p&gt;REST Client 插件还支持变量的使用。你可以使用${variable}语法来定义变量，然后在请求中使用它们。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@host = https://jsonplaceholder.typicode.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GET ${host}/posts/1 HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们定义了一个名为 &lt;code&gt;host&lt;/code&gt; 的变量，并在请求中使用它来指定 API 的基础 URL。&lt;/p&gt;
&lt;h2 id=&#34;循环&#34;&gt;循环&lt;/h2&gt;
&lt;p&gt;如果你需要发送多个请求，REST Client 插件支持循环语法。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@host = https://jsonplaceholder.typicode.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@for(i,1,10){
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    GET ${host}/posts/${i} HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们使用@for 语法来发送 10 个 GET 请求，每个请求 URL 中的 i 变量从 1 到 10 依次递增。&lt;/p&gt;
&lt;h2 id=&#34;条件语句&#34;&gt;条件语句&lt;/h2&gt;
&lt;p&gt;如果你需要根据条件发送请求，REST Client 插件也支持条件语句。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@host = https://jsonplaceholder.typicode.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@if(isDebug){
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    GET ${host}/posts/1 HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}else{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    GET ${host}/posts/2 HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们使用@if 语法来判断是否为调试模式，如果是就发送一个请求，否则发送另一个请求。&lt;/p&gt;
&lt;h2 id=&#34;导入环境变量&#34;&gt;导入环境变量&lt;/h2&gt;
&lt;p&gt;REST Client 插件支持从外部文件中导入环境变量。例如，你可以在&lt;code&gt;.env&lt;/code&gt; 文件中定义变量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;host=https://jsonplaceholder.typicode.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;isDebug=true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在请求文件中使用&lt;code&gt;@environment&lt;/code&gt; 语法来导入这些变量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@environment .env
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@if(isDebug){
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    GET ${host}/posts/1 HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}else{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    GET ${host}/posts/2 HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们使用&lt;code&gt;@environment&lt;/code&gt; 语法从&lt;code&gt;.env&lt;/code&gt; 文件中导入环境变量，然后在请求文件中使用这些变量。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;
&lt;p&gt;REST Client 插件是一个非常好用的工具，能够帮助开发人员快速、方便地发送 HTTP 请求并查看响应。在本文中，我们介绍了 REST Client 的基本使用方法，包括发送请求、查看响应、使用变量、循环、条件语句和导入环境变量等。希望这篇文章能够帮助你更好地使用 REST Client 插件。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>REST Client 是 VSCode 中一款非常好用的插件，能够帮助开发人员快速、方便地发送 HTTP 请求并查看响应。在本文中，我们将会详细介绍 REST Client 的使用方法。</p>
<h2 id="安装-rest-client-插件">安装 REST Client 插件</h2>
<p>在 VSCode 中，你可以通过以下步骤安装 REST Client 插件：</p>
<ul>
<li>打开 VSCode；</li>
<li>点击左侧的插件图标（Ctrl+Shift+X）；</li>
<li>搜索“REST Client”插件；</li>
<li>点击“安装”按钮。</li>
<li>发送 HTTP 请求</li>
</ul>
<p>使用 REST Client 插件发送 HTTP 请求非常简单。你只需要创建一个新的<code>.rest</code>文本文件，将请求信息放入其中，然后使用快捷键<code>Ctrl + Alt + R</code> 或者右键菜单的 <code>Send Request</code> 选项发送请求。</p>
<p>下面是一个简单的 <code>GET</code> 请求的例子：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">GET https://jsonplaceholder.typicode.com/posts/1 HTTP/1.1
</span></span></code></pre></div><p>这个请求会获取 JSONPlaceholder API 中的一篇博客文章。</p>
<p>如果你想添加请求头或请求体，可以使用以下语法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">GET https://jsonplaceholder.typicode.com/posts/1 HTTP/1.1
</span></span><span class="line"><span class="cl">Content-Type: application/json
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  &#34;title&#34;: &#34;foo&#34;,
</span></span><span class="line"><span class="cl">  &#34;body&#34;: &#34;bar&#34;,
</span></span><span class="line"><span class="cl">  &#34;userId&#34;: 1
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>这个请求会在请求头中添加 <code>Content-Type</code> 头，请求体中包含 JSON 数据。</p>
<h2 id="查看响应">查看响应</h2>
<p>发送请求后，你可以在编辑器底部看到响应信息。如果你想查看响应头、响应体或状态码等详细信息，可以使用以下语法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">###
</span></span><span class="line"><span class="cl">HTTP/1.1 200 OK
</span></span><span class="line"><span class="cl">Content-Type: application/json; charset=utf-8
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  &#34;userId&#34;: 1,
</span></span><span class="line"><span class="cl">  &#34;id&#34;: 1,
</span></span><span class="line"><span class="cl">  &#34;title&#34;: &#34;sunt aut facere repellat provident occaecati excepturi optio reprehenderit&#34;,
</span></span><span class="line"><span class="cl">  &#34;body&#34;: &#34;quia et suscipit\nsuscipit...&#34;
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>在这个例子中，###用来分隔请求和响应，这样你就可以很方便地查看请求和响应的详细信息了。</p>
<h2 id="变量">变量</h2>
<p>REST Client 插件还支持变量的使用。你可以使用${variable}语法来定义变量，然后在请求中使用它们。例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">@host = https://jsonplaceholder.typicode.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">GET ${host}/posts/1 HTTP/1.1
</span></span></code></pre></div><p>在这个例子中，我们定义了一个名为 <code>host</code> 的变量，并在请求中使用它来指定 API 的基础 URL。</p>
<h2 id="循环">循环</h2>
<p>如果你需要发送多个请求，REST Client 插件支持循环语法。例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">@host = https://jsonplaceholder.typicode.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@for(i,1,10){
</span></span><span class="line"><span class="cl">    GET ${host}/posts/${i} HTTP/1.1
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>在这个例子中，我们使用@for 语法来发送 10 个 GET 请求，每个请求 URL 中的 i 变量从 1 到 10 依次递增。</p>
<h2 id="条件语句">条件语句</h2>
<p>如果你需要根据条件发送请求，REST Client 插件也支持条件语句。例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">@host = https://jsonplaceholder.typicode.com
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@if(isDebug){
</span></span><span class="line"><span class="cl">    GET ${host}/posts/1 HTTP/1.1
</span></span><span class="line"><span class="cl">}else{
</span></span><span class="line"><span class="cl">    GET ${host}/posts/2 HTTP/1.1
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>在这个例子中，我们使用@if 语法来判断是否为调试模式，如果是就发送一个请求，否则发送另一个请求。</p>
<h2 id="导入环境变量">导入环境变量</h2>
<p>REST Client 插件支持从外部文件中导入环境变量。例如，你可以在<code>.env</code> 文件中定义变量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">host=https://jsonplaceholder.typicode.com
</span></span><span class="line"><span class="cl">isDebug=true
</span></span></code></pre></div><p>然后在请求文件中使用<code>@environment</code> 语法来导入这些变量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">@environment .env
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@if(isDebug){
</span></span><span class="line"><span class="cl">    GET ${host}/posts/1 HTTP/1.1
</span></span><span class="line"><span class="cl">}else{
</span></span><span class="line"><span class="cl">    GET ${host}/posts/2 HTTP/1.1
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>在这个例子中，我们使用<code>@environment</code> 语法从<code>.env</code> 文件中导入环境变量，然后在请求文件中使用这些变量。</p>
<h2 id="结论">结论</h2>
<p>REST Client 插件是一个非常好用的工具，能够帮助开发人员快速、方便地发送 HTTP 请求并查看响应。在本文中，我们介绍了 REST Client 的基本使用方法，包括发送请求、查看响应、使用变量、循环、条件语句和导入环境变量等。希望这篇文章能够帮助你更好地使用 REST Client 插件。</p>
]]></content:encoded>
    </item>
    <item>
      <title>WSL2 安装 Docker</title>
      <link>https://lifeislife.cn/posts/wsl2%E5%AE%89%E8%A3%85docker/</link>
      <pubDate>Thu, 16 Mar 2023 22:19:00 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/wsl2%E5%AE%89%E8%A3%85docker/</guid>
      <description>&lt;p&gt;在 WSL2 中，你可能会遇到与 Docker 服务相关的问题，因为 WSL2 与传统 Linux 系统在某些方面有所不同。在这种情况下，你可以尝试以下步骤来解决问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先，确保你已经安装了 WSL2 的最新版本。你可以通过运行以下命令来更新 WSL2：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;确保 Docker Desktop for Windows 已安装并启用 WSL2 集成。你可以在 Docker Desktop 设置中找到这个选项。确保你的 WSL2 发行版已被添加到 Docker Desktop 的 WSL 集成列表中。点击链接下载安装&lt;a href=&#34;https://dockerdocs.cn/docker-for-windows/install/&#34;&gt;在 Windows 上安装 Docker 桌面&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;在 WSL2 中，尝试手动停止 Docker 服务：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo /etc/init.d/docker stop
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;如果这个命令无法停止 Docker 服务，请尝试以下命令：
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo killall dockerd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;卸载 Docker：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get purge docker-ce
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;删除 Docker 相关的文件和目录：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo rm -rf /var/lib/docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;重新启动 WSL2：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wsl --shutdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;然后重新打开 WSL2。&lt;/li&gt;
&lt;li&gt;在 WSL2 中，不要直接安装 Docker CE。而是使用 Docker Desktop for Windows 提供的 Docker 服务。这意味着你不需要在 WSL2 中安装 Docker CE，因为 Docker Desktop 已经提供了 Docker 服务。&lt;/li&gt;
&lt;li&gt;确保你的 WSL2 发行版可以访问 Docker Desktop 提供的 Docker 服务。你可以通过运行以下命令来检查：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker --version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker info
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>在 WSL2 中，你可能会遇到与 Docker 服务相关的问题，因为 WSL2 与传统 Linux 系统在某些方面有所不同。在这种情况下，你可以尝试以下步骤来解决问题：</p>
<ol>
<li>首先，确保你已经安装了 WSL2 的最新版本。你可以通过运行以下命令来更新 WSL2：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">wsl --update
</span></span></code></pre></div><ol start="2">
<li>确保 Docker Desktop for Windows 已安装并启用 WSL2 集成。你可以在 Docker Desktop 设置中找到这个选项。确保你的 WSL2 发行版已被添加到 Docker Desktop 的 WSL 集成列表中。点击链接下载安装<a href="https://dockerdocs.cn/docker-for-windows/install/">在 Windows 上安装 Docker 桌面</a>。</li>
<li>在 WSL2 中，尝试手动停止 Docker 服务：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo /etc/init.d/docker stop
</span></span></code></pre></div><pre><code>如果这个命令无法停止 Docker 服务，请尝试以下命令：
</code></pre>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo killall dockerd
</span></span></code></pre></div><ol start="4">
<li>卸载 Docker：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt-get purge docker-ce
</span></span></code></pre></div><ol start="5">
<li>删除 Docker 相关的文件和目录：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo rm -rf /var/lib/docker
</span></span></code></pre></div><ol start="6">
<li>重新启动 WSL2：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">wsl --shutdown
</span></span></code></pre></div><ol start="7">
<li>然后重新打开 WSL2。</li>
<li>在 WSL2 中，不要直接安装 Docker CE。而是使用 Docker Desktop for Windows 提供的 Docker 服务。这意味着你不需要在 WSL2 中安装 Docker CE，因为 Docker Desktop 已经提供了 Docker 服务。</li>
<li>确保你的 WSL2 发行版可以访问 Docker Desktop 提供的 Docker 服务。你可以通过运行以下命令来检查：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">docker --version
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">docker info
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>一生一芯笔记</title>
      <link>https://lifeislife.cn/posts/%E4%B8%80%E7%94%9F%E4%B8%80%E8%8A%AF%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Sun, 12 Mar 2023 12:58:15 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E4%B8%80%E7%94%9F%E4%B8%80%E8%8A%AF%E7%AC%94%E8%AE%B0/</guid>
      <description>&lt;h1 id=&#34;一生一芯概述&#34;&gt;一生一芯概述&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://www.bilibili.com/video/BV12e4y1Y76i/?spm_id_from=333.788&amp;amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44&#34;&gt;“一生一芯”概述 _哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;程序的执行和模拟器&#34;&gt;程序的执行和模拟器&lt;/h1&gt;
&lt;h2 id=&#34;freestanding-运行时环境&#34;&gt;freestanding 运行时环境&lt;/h2&gt;
&lt;h3 id=&#34;程序如何结束运行&#34;&gt;程序如何结束运行&lt;/h3&gt;
&lt;p&gt;在正常的环境中，写了一段代码&lt;code&gt;return&lt;/code&gt;之后，实际上调用了一个系统调用&lt;code&gt;exit&lt;/code&gt;。但是在 freestanding 环境中，没有操作系统支持，根据 C99 手册规定，在 freestanding 环境中结束运行是由用户实现决定的。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5.1.2.1 Freestanding environment
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2 The effect of program termination in a freestanding environment is
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;implementation-defined.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 qemu-system-riscv64 中的 virt 机器模型中，往一个特殊的地址写入一个特殊的“暗号”即可结束 QEMU&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;volatile&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0x10000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;volatile&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0x100000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x5555&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// magic number
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;         &lt;span class=&#34;c1&#34;&gt;// 递归调用，如果正常退出将不会再次打印A
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;在自制-freestanding-运行时环境上运行-hello-程序&#34;&gt;在自制 freestanding 运行时环境上运行 Hello 程序&lt;/h3&gt;
&lt;p&gt;QEMU 虽然是个开源项目，但还挺复杂，不利于我们理解细节。让我们来设计一个面向 RISC-V 程序的简单 freestanding 运行时环境，我做以下约定。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序从地址 0 开始执行&lt;/li&gt;
&lt;li&gt;只支持两条指令
&lt;ul&gt;
&lt;li&gt;addi 指令&lt;/li&gt;
&lt;li&gt;ebreak 指令
&lt;ul&gt;
&lt;li&gt;寄存器 a0=0 时，输出寄存器 a1 低 8 位的字符&lt;/li&gt;
&lt;li&gt;寄存器 a0=1 时，结束运行
&lt;ul&gt;
&lt;li&gt;ABI Mnemonic（RISC-V 官方为每个寄存器起个名字）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ebreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;asm&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;volatile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;addi a0, x0, %0;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               &lt;span class=&#34;s&#34;&gt;&amp;#34;addi a1, x0, %1;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               &lt;span class=&#34;s&#34;&gt;&amp;#34;ebreak&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;i&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arg0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;i&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arg1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;putch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ebreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;halt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ebreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;putch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;halt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/** 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 这段代码定义了三个函数：ebreak、putch 和 halt。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * ebreak 函数是一个内联汇编函数，它执行 ebreak 指令。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 该指令是 RISC-V 架构中的一条调试指令，可以在调试器的控制下执行。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 该函数接受两个参数 arg0 和 arg1，它们将被存储在寄存器 a0 和 a1 中。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * putch 函数调用了 ebreak 函数，并将第一个参数设为 0，
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 第二个参数设为函数参数 ch。这样做的目的可能是为了在调试器的控制下输出一个字符。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * halt 函数调用了 ebreak 函数，并将第一个参数设为 1，
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 第二个参数设为函数参数 code。这样做的目的可能是为了通知调试器程序已经结束，
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 并使用 code 作为结束状态。然后，halt 函数进入一个死循环，等待调试器的操作。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 最后，_start 函数调用了 putch 函数输出字符 &amp;#39;A&amp;#39;，然后调用 halt 函数结束程序 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;riscv64-linux-gnu-gcc -march&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rv64g -ffreestanding -nostdlib -static -Wl,-Ttext&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -O2 -o prog a.c
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;riscv64-linux-gnu-gcc: 这是 GCC 的可执行文件的名称，表示使用的是 GCC 编译器。riscv64-linux-gnu 是编译器的目标平台，表示生成的代码是针对 RISC-V 架构，运行在 Linux 系统上的二进制文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-march=rv64g: 这个参数指定了编译器使用的指令集。rv64g 表示使用 RISC-V 架构的 64 位指令集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-ffreestanding: 这个参数指示编译器生成的代码将在 freestanding 运行环境中运行。在 freestanding 运行环境中，程序不会自动链接标准 C 库，也不会自动调用 main 函数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-nostdlib: 这个参数表示编译器不需要链接标准 C 库。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-static: 这个参数表示生成的代码是静态链接的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-Wl,-Ttext=0: 这个参数是传递给链接器的，表示设置代码段的起始地址为 0。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-O2: 这个参数指示编译器使用优化级别为 2 的优化选项。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-o prog: 这个参数指定生成的可执行文件的名称为 prog。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a.c: 这是要编译的 C 源文件的名称。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;llvm-objdump -d prog
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;反汇编结果如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nl&#34;&gt;prog:&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;format&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;elf64-littleriscv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;Disassembly&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;section&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;.text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;0000000000000000&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;_start&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;0:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;13&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;li&lt;/span&gt;      &lt;span class=&#34;no&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;4:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;93&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;04&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;li&lt;/span&gt;      &lt;span class=&#34;no&#34;&gt;a1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;65&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;8:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;73&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;ebreak&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;nl&#34;&gt;c:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;13&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;li&lt;/span&gt;      &lt;span class=&#34;no&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;err&#34;&gt;10:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;93&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;li&lt;/span&gt;      &lt;span class=&#34;no&#34;&gt;a1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;err&#34;&gt;14:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;73&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;ebreak&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;err&#34;&gt;18:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;f&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;no&#34;&gt;j&lt;/span&gt;       &lt;span class=&#34;mh&#34;&gt;0x18&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0x18&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们约定中没有&lt;code&gt;li&lt;/code&gt;指令，但是汇编中却出现了，这是因为&lt;code&gt;li&lt;/code&gt;是一条伪指令，它的实际实现依然是&lt;code&gt;addi&lt;/code&gt;。如果不使用伪指令可以使用以下命令反汇编：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;llvm-objdump -M no-aliases -d prog
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果如下，没有伪指令，只有我们约定的几条指令。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nl&#34;&gt;prog:&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;format&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;elf64-littleriscv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;Disassembly&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;section&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;.text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;0000000000000000&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;_start&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;0:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;13&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;addi&lt;/span&gt;    &lt;span class=&#34;no&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;4:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;93&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;04&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;addi&lt;/span&gt;    &lt;span class=&#34;no&#34;&gt;a1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;65&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;8:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;73&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;ebreak&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;nl&#34;&gt;c:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;13&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;addi&lt;/span&gt;    &lt;span class=&#34;no&#34;&gt;a0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;err&#34;&gt;10:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;93&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;05&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;addi&lt;/span&gt;    &lt;span class=&#34;no&#34;&gt;a1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;err&#34;&gt;14:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;73&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;ebreak&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;err&#34;&gt;18:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;f&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt;   &lt;span class=&#34;no&#34;&gt;jal&lt;/span&gt;     &lt;span class=&#34;no&#34;&gt;zero&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x18&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0x18&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;yemu-指令如何执行&#34;&gt;YEMU 指令如何执行&lt;/h2&gt;
&lt;p&gt;ISA 手册定义了一个状态机。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;状态集合 S = {&amp;lt;R, M&amp;gt;}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;R = {PC, x0, x1, x2, &amp;hellip;}
&lt;ul&gt;
&lt;li&gt;RISC-V 手册 -&amp;gt; 2.1 Programmers’Model for Base Integer ISA&lt;/li&gt;
&lt;li&gt;PC = 程序计数器 = 当前执行的指令位置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;M = 内存
&lt;ul&gt;
&lt;li&gt;RISC-V 手册 -&amp;gt; 1.4 Memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;激励事件：执行 PC 指向的指令
状态转移规则：指令的语义 (semantics)
初始状态 S0 = &amp;lt;R0, M0&amp;gt;&lt;/p&gt;
&lt;p&gt;我们只要把这个状态机实现出来，就可以用它来执行指令了！&lt;/p&gt;
&lt;h3 id=&#34;用变量实现内存&#34;&gt;用变量实现内存&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// according to the RISC-V manual
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;M&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// 64-Byte memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Q: 为什么不使用 &lt;code&gt;int64_t&lt;/code&gt; 和 &lt;code&gt;int8_t&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;A: C语言标准规定, 有符号数溢出是undefined behavior, 但无符号数不会溢出&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;6.5 Expressions
5 If an exceptional condition occurs during the evaluation of an expression (that is,
if the result is not mathematically defined or not in the range of representable
values for its type), the behavior is undefined.
6.2.5 Types
9 A computation involving unsigned operands can never overflow, because a result that
cannot be represented by the resulting unsigned integer type is reduced modulo the
number that is one greater than the largest value that can be represented by the
resulting type.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;用语句实现指令的语义&#34;&gt;用语句实现指令的语义&lt;/h3&gt;
&lt;p&gt;指令周期 (instruction cycle): 执行一条指令的步骤&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;取指 (fetch): 从 PC 所指示的内存位置读取一条指令&lt;/li&gt;
&lt;li&gt;译码 (decode): 按照手册解析指令的操作码 (opcode) 和操作数 (operand)&lt;/li&gt;
&lt;li&gt;执行 (execute): 按解析出的操作码，对操作数进行处理&lt;/li&gt;
&lt;li&gt;更新 PC: 让 PC 指向下一条指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;状态机不断执行指令，直到结束运行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;halt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;halt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;inst_cycle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 31           20 19 15 14 12 11  7 6       0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------+-----+-----+-----+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|   imm[11:0]   | rs1 | 000 | rd  | 0010011 |    ADDI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------+-----+-----+-----+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------+-----+-----+-----+---------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 000000000001  |00000| 000 |00000| 1110011 |   EBREAK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------+-----+-----+-----+---------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一个简单的实现：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;inst_cycle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;M&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x7f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x13&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// addi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x1f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x1f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x1f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x7ff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x80000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4096&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inst&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00100073&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// ebreak
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;putchar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0xff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;halt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Unsupported ebreak command&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Unsupported instuction&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;PC&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;nemu-代码导读&#34;&gt;NEMU 代码导读&lt;/h1&gt;
&lt;h2 id=&#34;make-项目构&#34;&gt;make 项目构&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示make踪迹&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;strace make
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示构建过程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示更详细的构建构过程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make --debug&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Reading makefiles...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Reading makefile `Makefile&amp;#39;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Updating goal targets....
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; File `all&amp;#39; does not exist.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   File `all&amp;#39; does not exist.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Looking for an implicit rule for `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.c&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.cc&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.C&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.cpp&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.CPP&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.cxx&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.CXX&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.c++&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying pattern rule with stem `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Trying implicit prerequisite `all.C++&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   No implicit rule found for `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Finished prerequisites of target file `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Must remake target `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -o all all.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Finished prerequisites of target file `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Must remake target `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -o all all.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Successfully remade target file `all&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 只打印命令不执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -n
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出目标被构建的原因和执行的命令&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make --trace
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如，如果您有一个 makefile，其目标 &lt;code&gt;all&lt;/code&gt; 依赖于目标 &lt;code&gt;foo&lt;/code&gt; 和 &lt;code&gt;bar&lt;/code&gt;，并且您运行 &lt;code&gt;make --trace all&lt;/code&gt;，您可能会看到如下输出：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;: Entering directory &lt;span class=&#34;s1&#34;&gt;&amp;#39;/path/to/project&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -o foo foo.c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;: Leaving directory &lt;span class=&#34;s1&#34;&gt;&amp;#39;/path/to/project&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;: Entering directory &lt;span class=&#34;s1&#34;&gt;&amp;#39;/path/to/project&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -o bar bar.c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;: Leaving directory &lt;span class=&#34;s1&#34;&gt;&amp;#39;/path/to/project&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;: Entering directory &lt;span class=&#34;s1&#34;&gt;&amp;#39;/path/to/project&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -o all foo.o bar.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;: Leaving directory &lt;span class=&#34;s1&#34;&gt;&amp;#39;/path/to/project&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -nB  &lt;span class=&#34;c1&#34;&gt;# -B 可以强制 make 构建所有目标，即使它们已经是最新的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -nB &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; vim -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 vim 编辑器中进行二次处理，过滤不需要的信息。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 只保留 gcc 或 g++开头的行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;:%!grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;^\(gcc\|g++\)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将环境变量$NEMU_HOME 所指示字符串替换为$NEMU_HOME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;:%!sed -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;s+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$NEMU_HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;+\$NEMU_HOME+g&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将$NEMU_HOME/build/obj-riscv64-nemu-interpreter 替换为$OBJ_DIR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;:%s+&lt;span class=&#34;se&#34;&gt;\$&lt;/span&gt;NEMU_HOME/build/obj-riscv64-nemu-interpreter+&lt;span class=&#34;nv&#34;&gt;$OBJ_DIR&lt;/span&gt;+g
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将-c 之前的内容替换为$CFLAGS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;:%s/-O2.*&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv64/&lt;span class=&#34;nv&#34;&gt;$CFLAGS&lt;/span&gt;/g
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将最后一行的空格替换成换行并缩进两格&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;:&lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;/  */&lt;span class=&#34;se&#34;&gt;\r&lt;/span&gt;  /g
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;调试技巧选将&#34;&gt;调试技巧选将&lt;/h1&gt;
&lt;h2 id=&#34;断言&#34;&gt;断言&lt;/h2&gt;
&lt;p&gt;在 C 程序中使用断言（assert）不会增加额外的内存空间，也不会增加数据段空间。断言是一种在运行时检查程序假设是否为真的方法，当断言失败时，程序会终止执行并显示错误信息。&lt;/p&gt;
&lt;p&gt;在 C 语言中，断言通常使用宏来实现。它在编译时被解释为一个简单的条件语句，因此它不会增加程序的内存空间或数据段空间。断言宏的定义通常类似于以下代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;assert.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define assert(expression) ((void)0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的 expression 是要检查的条件。如果 &lt;code&gt;expression&lt;/code&gt; 为假，则 &lt;code&gt;assert()&lt;/code&gt; 函数会发出错误消息并终止程序的执行。如果 &lt;code&gt;expression&lt;/code&gt; 为真，则 &lt;code&gt;assert()&lt;/code&gt; 函数不会产生任何操作，并且被解释为 &lt;code&gt;((void)0)&lt;/code&gt;。这个语句不会增加任何内存或数据段空间。&lt;/p&gt;
&lt;p&gt;需要注意的是，当一个程序使用大量的断言时，它可能会对程序的性能产生一些影响，因为每个断言都需要在运行时进行检查。因此，在生产环境中，应该尽可能减少使用断言，并在测试和调试阶段使用它们来确保代码的正
确性。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// nemu/src/isa/riscv64/local-include/reg.h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;check_reg_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;IFDEF&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CONFIG_RT_CHECK&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;编译器工具-sanitizer&#34;&gt;编译器工具 sanitizer&lt;/h2&gt;
&lt;p&gt;让编译器自动插入 assert, 拦截常见的非预期行为&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AddressSanitizer - 检查指针越界，use-after-free&lt;/li&gt;
&lt;li&gt;ThreadSanitizer - 检查多线程数据竞争&lt;/li&gt;
&lt;li&gt;LeakSanitizer - 检查内存泄漏&lt;/li&gt;
&lt;li&gt;UndefinedBehaviorSanitizer - 检查 UB&lt;/li&gt;
&lt;li&gt;还能检查指针的比较和相减&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;打开后程序运行效率有所下降&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;但调试的时候非常值得，躺着就能让工具帮你找 bug&lt;/li&gt;
&lt;li&gt;man gcc 查看具体用法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;使用方法&#34;&gt;使用方法&lt;/h3&gt;
&lt;p&gt;GCC 提供了多种 Sanitizer 工具，可以帮助开发者在编译时检测和修复常见的编程错误，例如内存泄漏、缓冲区溢出、使用未初始化的变量等。以下是几个 Sanitizer 工具的示例用法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Address Sanitizer（ASAN）：检测内存错误，例如使用已经释放的内存、堆栈和全局缓冲区的溢出和下溢等。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -fsanitize=address -g &amp;lt;source files&amp;gt; -o &amp;lt;output file&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Undefined Behavior Sanitizer（UBSAN）：检测未定义行为，例如除以零、使用未初始化的变量、指针溢出等。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -fsanitize=undefined -g &amp;lt;source files&amp;gt; -o &amp;lt;output file&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Thread Sanitizer（TSAN）：检测并发问题，例如竞争条件、死锁等。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -fsanitize=thread -g &amp;lt;source files&amp;gt; -o &amp;lt;output file&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Memory Sanitizer（MSAN）：检测使用未初始化的内存，例如读取未初始化的内存、使用已释放的内存等。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc -fsanitize=memory -g &amp;lt;source files&amp;gt; -o &amp;lt;output file&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;需要注意的是，Sanitizer 工具可能会增加程序的执行时间和内存消耗，并且可能会产生误报，因此在生产环境中应该禁用 Sanitizer 工具。通常情况下，开发者可以在开发和测试阶段启用 Sanitizer 工具，以帮助他们发现和修复代码中的问题。&lt;/p&gt;
&lt;h2 id=&#34;自顶向下理解程序行为&#34;&gt;自顶向下理解程序行为&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ftrace - 函数调用层次，理解程序的大体行为
itrace - 指令执行层次，理解指令级别的行为
mtrace - 访存的踪迹
dtrace - 设备访问的踪迹
sdb - 灵活细致地检查客户程序的状态
si - 细粒度的状态转移
info r/x - 检查R/M
监视点 - 捕捉某状态发生变化的时刻
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sdb 与 gdb 结合使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;先用 sdb 定位到出错点附近
再用 gdb 观察 NEMU 的细节行为
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;程序的运行时间都花在了哪里&#34;&gt;程序的运行时间都花在了哪里&lt;/h2&gt;
&lt;p&gt;Linux 的性能分析工具 perf 是一款功能强大的性能分析工具，它可以通过硬件计数器（Hardware counter）或者性能事件（Performance event）来对 Linux 系统的性能进行分析。以下是 perf 工具的安装和使用方法。&lt;/p&gt;
&lt;h3 id=&#34;安装-perf-工具&#34;&gt;安装 perf 工具&lt;/h3&gt;
&lt;p&gt;在大部分 Linux 发行版中，perf 工具已经预先安装，如果没有预先安装，可以通过以下命令进行安装。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debian/Ubuntu 系统：&lt;code&gt;sudo apt-get install linux-tools-common linux-tools-generic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fedora 系统：&lt;code&gt;sudo dnf install perf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;CentOS/RHEL 系统：&lt;code&gt;sudo yum install perf&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安装完毕之后，可以通过 &lt;code&gt;perf version&lt;/code&gt; 命令来检查 perf 版本信息。&lt;/p&gt;
&lt;h3 id=&#34;编写一个简单的-c-代码&#34;&gt;编写一个简单的 C 代码&lt;/h3&gt;
&lt;p&gt;这里我们编写一个简单的 C 代码，用于测试 perf 工具的使用。代码如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;sum = %d&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;代码的作用是计算 1 到 1000000 的和。&lt;/p&gt;
&lt;h3 id=&#34;使用-perf-工具&#34;&gt;使用 perf 工具&lt;/h3&gt;
&lt;p&gt;下面我们使用 perf 工具来对上述代码进行性能分析。假设代码保存在文件 test.c 中。&lt;/p&gt;
&lt;p&gt;统计 CPU 周期数
以下命令用于统计程序的 CPU 周期数：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;perf stat ./test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果类似于：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; Performance counter stats &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;./test&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           19,23 msec task-clock:u              &lt;span class=&#34;c1&#34;&gt;#    0.988 CPUs utilized          &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;      context-switches:u        &lt;span class=&#34;c1&#34;&gt;#    0.000 K/sec                  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;      cpu-migrations:u          &lt;span class=&#34;c1&#34;&gt;#    0.000 K/sec                  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               &lt;span class=&#34;m&#34;&gt;575&lt;/span&gt;      page-faults:u             &lt;span class=&#34;c1&#34;&gt;#    0.030 M/sec                  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    64,013,620,231      cycles:u                  &lt;span class=&#34;c1&#34;&gt;#    3.324 GHz                      (49.80%)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    40,010,335,480      instructions:u            &lt;span class=&#34;c1&#34;&gt;#    0.62  insn per cycle           (62.34%)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     9,998,469,566      branches:u                &lt;span class=&#34;c1&#34;&gt;#  518.693 M/sec                    (62.27%)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           763,176      branch-misses:u           &lt;span class=&#34;c1&#34;&gt;#    0.01% of all branches          (62.32%)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      0.019438122 seconds &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; elapsed
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      0.019411000 seconds user
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      0.000007000 seconds sys
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果中的 cycles 表示 CPU 周期数，instructions 表示指令数，branches 表示分支指令数。其中，cycles 和 instructions 的比例代表了 CPU 的效率，即 IPC（Instructions Per Cycle）。&lt;/p&gt;
&lt;h3 id=&#34;统计函数调用次数&#34;&gt;统计函数调用次数&lt;/h3&gt;
&lt;p&gt;以下命令用于统计程序中函数的调用次数：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;perf record -e cycles -g ./test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个命令将启动 perf 工具，并使用 -g 选项记录调用关系图。我们还需要使用 sudo 权限运行该命令，以便 perf 工具可以访问系统的硬件计数器。&lt;/p&gt;
&lt;h2 id=&#34;成为专业码农&#34;&gt;成为专业码农&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;要熟悉项目了 -&amp;gt; STFW/RTFM/RTFSC, 尝试理解一切细节&lt;/li&gt;
&lt;li&gt;要写代码了
&lt;ul&gt;
&lt;li&gt;仔细 RTFM, 正确理解需求&lt;/li&gt;
&lt;li&gt;编写可读，可维护，易验证的代码 (不言自明，不言自证)&lt;/li&gt;
&lt;li&gt;用 lint 工具检查代码&lt;/li&gt;
&lt;li&gt;进行充分的测试&lt;/li&gt;
&lt;li&gt;添加充分的断言&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;要调试了
&lt;ul&gt;
&lt;li&gt;默念“机器永远是对的/未测试代码永远是错的”&lt;/li&gt;
&lt;li&gt;sanitizer, trace, printf, gdb, …&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;平时 -&amp;gt; 用正确的工具/方法做事情&lt;/li&gt;
&lt;li&gt;感到不爽了 -&amp;gt; 找正确的工具/搭基础设施&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;总线选讲&#34;&gt;总线选讲&lt;/h1&gt;
&lt;h2 id=&#34;定义&#34;&gt;定义&lt;/h2&gt;
&lt;p&gt;广义上讲总线就是一个通信系统，以下这些都属于广义的总线概念:TCP/IP, 以太网，网线，RTL 信号，系统调用。&lt;/p&gt;
&lt;p&gt;主动发起通信的叫 master，响应通信的叫 slave。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h1 id="一生一芯概述">一生一芯概述</h1>
<p><a href="https://www.bilibili.com/video/BV12e4y1Y76i/?spm_id_from=333.788&amp;vd_source=7ff88341de4b5111bdf3db48b4e9ca44">“一生一芯”概述 _哔哩哔哩_bilibili</a></p>
<h1 id="程序的执行和模拟器">程序的执行和模拟器</h1>
<h2 id="freestanding-运行时环境">freestanding 运行时环境</h2>
<h3 id="程序如何结束运行">程序如何结束运行</h3>
<p>在正常的环境中，写了一段代码<code>return</code>之后，实际上调用了一个系统调用<code>exit</code>。但是在 freestanding 环境中，没有操作系统支持，根据 C99 手册规定，在 freestanding 环境中结束运行是由用户实现决定的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">5.1.2.1 Freestanding environment
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">2 The effect of program termination in a freestanding environment is
</span></span><span class="line"><span class="cl">implementation-defined.
</span></span></code></pre></div><p>在 qemu-system-riscv64 中的 virt 机器模型中，往一个特殊的地址写入一个特殊的“暗号”即可结束 QEMU</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">_start</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="mh">0x10000000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="sc">&#39;A&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">exit</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="mh">0x100000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="n">exit</span> <span class="o">=</span> <span class="mh">0x5555</span><span class="p">;</span>   <span class="c1">// magic number
</span></span></span><span class="line"><span class="cl">  <span class="nf">_start</span><span class="p">();</span>         <span class="c1">// 递归调用，如果正常退出将不会再次打印A
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="在自制-freestanding-运行时环境上运行-hello-程序">在自制 freestanding 运行时环境上运行 Hello 程序</h3>
<p>QEMU 虽然是个开源项目，但还挺复杂，不利于我们理解细节。让我们来设计一个面向 RISC-V 程序的简单 freestanding 运行时环境，我做以下约定。</p>
<ul>
<li>程序从地址 0 开始执行</li>
<li>只支持两条指令
<ul>
<li>addi 指令</li>
<li>ebreak 指令
<ul>
<li>寄存器 a0=0 时，输出寄存器 a1 低 8 位的字符</li>
<li>寄存器 a0=1 时，结束运行
<ul>
<li>ABI Mnemonic（RISC-V 官方为每个寄存器起个名字）</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">ebreak</span><span class="p">(</span><span class="kt">long</span> <span class="n">arg0</span><span class="p">,</span> <span class="kt">long</span> <span class="n">arg1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">asm</span> <span class="k">volatile</span><span class="p">(</span><span class="s">&#34;addi a0, x0, %0;&#34;</span>
</span></span><span class="line"><span class="cl">               <span class="s">&#34;addi a1, x0, %1;&#34;</span>
</span></span><span class="line"><span class="cl">               <span class="s">&#34;ebreak&#34;</span> <span class="o">:</span> <span class="o">:</span> <span class="s">&#34;i&#34;</span><span class="p">(</span><span class="n">arg0</span><span class="p">),</span> <span class="s">&#34;i&#34;</span><span class="p">(</span><span class="n">arg1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">putch</span><span class="p">(</span><span class="kt">char</span> <span class="n">ch</span><span class="p">)</span> <span class="p">{</span> <span class="nf">ebreak</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">ch</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">halt</span><span class="p">(</span><span class="kt">int</span> <span class="n">code</span><span class="p">)</span> <span class="p">{</span> <span class="nf">ebreak</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">code</span><span class="p">);</span> <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">_start</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nf">putch</span><span class="p">(</span><span class="sc">&#39;A&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nf">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/** 
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 这段代码定义了三个函数：ebreak、putch 和 halt。
</span></span></span><span class="line"><span class="cl"><span class="cm"> * ebreak 函数是一个内联汇编函数，它执行 ebreak 指令。
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 该指令是 RISC-V 架构中的一条调试指令，可以在调试器的控制下执行。
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 该函数接受两个参数 arg0 和 arg1，它们将被存储在寄存器 a0 和 a1 中。
</span></span></span><span class="line"><span class="cl"><span class="cm"> * putch 函数调用了 ebreak 函数，并将第一个参数设为 0，
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 第二个参数设为函数参数 ch。这样做的目的可能是为了在调试器的控制下输出一个字符。
</span></span></span><span class="line"><span class="cl"><span class="cm"> * halt 函数调用了 ebreak 函数，并将第一个参数设为 1，
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 第二个参数设为函数参数 code。这样做的目的可能是为了通知调试器程序已经结束，
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 并使用 code 作为结束状态。然后，halt 函数进入一个死循环，等待调试器的操作。
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 最后，_start 函数调用了 putch 函数输出字符 &#39;A&#39;，然后调用 halt 函数结束程序 
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> 
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">riscv64-linux-gnu-gcc -march<span class="o">=</span>rv64g -ffreestanding -nostdlib -static -Wl,-Ttext<span class="o">=</span><span class="m">0</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -O2 -o prog a.c
</span></span></code></pre></div><ul>
<li>
<p>riscv64-linux-gnu-gcc: 这是 GCC 的可执行文件的名称，表示使用的是 GCC 编译器。riscv64-linux-gnu 是编译器的目标平台，表示生成的代码是针对 RISC-V 架构，运行在 Linux 系统上的二进制文件。</p>
</li>
<li>
<p>-march=rv64g: 这个参数指定了编译器使用的指令集。rv64g 表示使用 RISC-V 架构的 64 位指令集。</p>
</li>
<li>
<p>-ffreestanding: 这个参数指示编译器生成的代码将在 freestanding 运行环境中运行。在 freestanding 运行环境中，程序不会自动链接标准 C 库，也不会自动调用 main 函数。</p>
</li>
<li>
<p>-nostdlib: 这个参数表示编译器不需要链接标准 C 库。</p>
</li>
<li>
<p>-static: 这个参数表示生成的代码是静态链接的。</p>
</li>
<li>
<p>-Wl,-Ttext=0: 这个参数是传递给链接器的，表示设置代码段的起始地址为 0。</p>
</li>
<li>
<p>-O2: 这个参数指示编译器使用优化级别为 2 的优化选项。</p>
</li>
<li>
<p>-o prog: 这个参数指定生成的可执行文件的名称为 prog。</p>
</li>
<li>
<p>a.c: 这是要编译的 C 源文件的名称。</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llvm-objdump -d prog
</span></span></code></pre></div><p>反汇编结果如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nl">prog:</span>   <span class="nf">file</span> <span class="no">format</span> <span class="no">elf64-littleriscv</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">Disassembly</span> <span class="no">of</span> <span class="no">section</span> <span class="no">.text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">0000000000000000</span> <span class="err">&lt;</span><span class="nf">_start</span><span class="err">&gt;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">       <span class="err">0:</span> <span class="err">13</span> <span class="err">05</span> <span class="err">00</span> <span class="err">00</span>   <span class="nf">li</span>      <span class="no">a0</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">       <span class="err">4:</span> <span class="err">93</span> <span class="err">05</span> <span class="err">10</span> <span class="err">04</span>   <span class="nf">li</span>      <span class="no">a1</span><span class="p">,</span> <span class="mi">65</span>
</span></span><span class="line"><span class="cl">       <span class="err">8:</span> <span class="err">73</span> <span class="err">00</span> <span class="err">10</span> <span class="err">00</span>   <span class="nf">ebreak</span>
</span></span><span class="line"><span class="cl">       <span class="nl">c:</span> <span class="err">13</span> <span class="err">05</span> <span class="err">10</span> <span class="err">00</span>   <span class="nf">li</span>      <span class="no">a0</span><span class="p">,</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">      <span class="err">10:</span> <span class="err">93</span> <span class="err">05</span> <span class="err">00</span> <span class="err">00</span>   <span class="nf">li</span>      <span class="no">a1</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">      <span class="err">14:</span> <span class="err">73</span> <span class="err">00</span> <span class="err">10</span> <span class="err">00</span>   <span class="nf">ebreak</span>
</span></span><span class="line"><span class="cl">      <span class="err">18:</span> <span class="err">6</span><span class="nf">f</span> <span class="mi">00</span> <span class="mi">00</span> <span class="mi">00</span>   <span class="no">j</span>       <span class="mh">0x18</span> <span class="p">&lt;</span><span class="no">_start</span><span class="p">+</span><span class="mi">0x18</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>我们约定中没有<code>li</code>指令，但是汇编中却出现了，这是因为<code>li</code>是一条伪指令，它的实际实现依然是<code>addi</code>。如果不使用伪指令可以使用以下命令反汇编：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llvm-objdump -M no-aliases -d prog
</span></span></code></pre></div><p>结果如下，没有伪指令，只有我们约定的几条指令。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nl">prog:</span>   <span class="nf">file</span> <span class="no">format</span> <span class="no">elf64-littleriscv</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">Disassembly</span> <span class="no">of</span> <span class="no">section</span> <span class="no">.text</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">0000000000000000</span> <span class="err">&lt;</span><span class="nf">_start</span><span class="err">&gt;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">       <span class="err">0:</span> <span class="err">13</span> <span class="err">05</span> <span class="err">00</span> <span class="err">00</span>   <span class="nf">addi</span>    <span class="no">a0</span><span class="p">,</span> <span class="no">zero</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">       <span class="err">4:</span> <span class="err">93</span> <span class="err">05</span> <span class="err">10</span> <span class="err">04</span>   <span class="nf">addi</span>    <span class="no">a1</span><span class="p">,</span> <span class="no">zero</span><span class="p">,</span> <span class="mi">65</span>
</span></span><span class="line"><span class="cl">       <span class="err">8:</span> <span class="err">73</span> <span class="err">00</span> <span class="err">10</span> <span class="err">00</span>   <span class="nf">ebreak</span>
</span></span><span class="line"><span class="cl">       <span class="nl">c:</span> <span class="err">13</span> <span class="err">05</span> <span class="err">10</span> <span class="err">00</span>   <span class="nf">addi</span>    <span class="no">a0</span><span class="p">,</span> <span class="no">zero</span><span class="p">,</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">      <span class="err">10:</span> <span class="err">93</span> <span class="err">05</span> <span class="err">00</span> <span class="err">00</span>   <span class="nf">addi</span>    <span class="no">a1</span><span class="p">,</span> <span class="no">zero</span><span class="p">,</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">      <span class="err">14:</span> <span class="err">73</span> <span class="err">00</span> <span class="err">10</span> <span class="err">00</span>   <span class="nf">ebreak</span>
</span></span><span class="line"><span class="cl">      <span class="err">18:</span> <span class="err">6</span><span class="nf">f</span> <span class="mi">00</span> <span class="mi">00</span> <span class="mi">00</span>   <span class="no">jal</span>     <span class="no">zero</span><span class="p">,</span> <span class="mh">0x18</span> <span class="p">&lt;</span><span class="no">_start</span><span class="p">+</span><span class="mi">0x18</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="yemu-指令如何执行">YEMU 指令如何执行</h2>
<p>ISA 手册定义了一个状态机。</p>
<ul>
<li>
<p>状态集合 S = {&lt;R, M&gt;}</p>
<ul>
<li>R = {PC, x0, x1, x2, &hellip;}
<ul>
<li>RISC-V 手册 -&gt; 2.1 Programmers’Model for Base Integer ISA</li>
<li>PC = 程序计数器 = 当前执行的指令位置</li>
</ul>
</li>
<li>M = 内存
<ul>
<li>RISC-V 手册 -&gt; 1.4 Memory</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>激励事件：执行 PC 指向的指令
状态转移规则：指令的语义 (semantics)
初始状态 S0 = &lt;R0, M0&gt;</p>
<p>我们只要把这个状态机实现出来，就可以用它来执行指令了！</p>
<h3 id="用变量实现内存">用变量实现内存</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="kt">uint64_t</span> <span class="n">R</span><span class="p">[</span><span class="mi">32</span><span class="p">],</span> <span class="n">PC</span><span class="p">;</span> <span class="c1">// according to the RISC-V manual
</span></span></span><span class="line"><span class="cl"><span class="kt">uint8_t</span> <span class="n">M</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>      <span class="c1">// 64-Byte memory
</span></span></span></code></pre></div><p>Q: 为什么不使用 <code>int64_t</code> 和 <code>int8_t</code>?</p>
<p>A: C语言标准规定, 有符号数溢出是undefined behavior, 但无符号数不会溢出</p>
<blockquote>
<p>6.5 Expressions
5 If an exceptional condition occurs during the evaluation of an expression (that is,
if the result is not mathematically defined or not in the range of representable
values for its type), the behavior is undefined.
6.2.5 Types
9 A computation involving unsigned operands can never overflow, because a result that
cannot be represented by the resulting unsigned integer type is reduced modulo the
number that is one greater than the largest value that can be represented by the
resulting type.</p>
</blockquote>
<h3 id="用语句实现指令的语义">用语句实现指令的语义</h3>
<p>指令周期 (instruction cycle): 执行一条指令的步骤</p>
<ul>
<li>取指 (fetch): 从 PC 所指示的内存位置读取一条指令</li>
<li>译码 (decode): 按照手册解析指令的操作码 (opcode) 和操作数 (operand)</li>
<li>执行 (execute): 按解析出的操作码，对操作数进行处理</li>
<li>更新 PC: 让 PC 指向下一条指令</li>
</ul>
<p>状态机不断执行指令，直到结束运行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdbool.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">halt</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">halt</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nf">inst_cycle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"> 31           20 19 15 14 12 11  7 6       0
</span></span><span class="line"><span class="cl">+---------------+-----+-----+-----+---------+
</span></span><span class="line"><span class="cl">|   imm[11:0]   | rs1 | 000 | rd  | 0010011 |    ADDI
</span></span><span class="line"><span class="cl">+---------------+-----+-----+-----+---------+
</span></span><span class="line"><span class="cl">+---------------+-----+-----+-----+---------+
</span></span><span class="line"><span class="cl">| 000000000001  |00000| 000 |00000| 1110011 |   EBREAK
</span></span><span class="line"><span class="cl">+---------------+-----+-----+-----+---------+
</span></span></code></pre></div><p>一个简单的实现：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">inst_cycle</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint32_t</span> <span class="n">inst</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">M</span><span class="p">[</span><span class="n">PC</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(((</span><span class="n">inst</span> <span class="o">&amp;</span> <span class="mh">0x7f</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x13</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">((</span><span class="n">inst</span> <span class="o">&gt;&gt;</span> <span class="mi">12</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x7</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// addi
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(((</span><span class="n">inst</span> <span class="o">&gt;&gt;</span> <span class="mi">7</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x1f</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">R</span><span class="p">[(</span><span class="n">inst</span> <span class="o">&gt;&gt;</span> <span class="mi">7</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x1f</span><span class="p">]</span> <span class="o">=</span> <span class="n">R</span><span class="p">[(</span><span class="n">inst</span> <span class="o">&gt;&gt;</span> <span class="mi">15</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x1f</span><span class="p">]</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="p">(((</span><span class="n">inst</span> <span class="o">&gt;&gt;</span> <span class="mi">20</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x7ff</span><span class="p">)</span> <span class="o">-</span> <span class="p">((</span><span class="n">inst</span> <span class="o">&amp;</span> <span class="mh">0x80000000</span><span class="p">)</span> <span class="o">?</span> <span class="mi">4096</span> <span class="o">:</span> <span class="mi">0</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">inst</span> <span class="o">==</span> <span class="mh">0x00100073</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// ebreak
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">R</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="nf">putchar</span><span class="p">(</span><span class="n">R</span><span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">R</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="n">halt</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="p">{</span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Unsupported ebreak command</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Unsupported instuction</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">PC</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="nemu-代码导读">NEMU 代码导读</h1>
<h2 id="make-项目构">make 项目构</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 显示make踪迹</span>
</span></span><span class="line"><span class="cl">strace make
</span></span><span class="line"><span class="cl"><span class="c1"># 显示构建过程</span>
</span></span><span class="line"><span class="cl">make -d
</span></span><span class="line"><span class="cl"><span class="c1"># 显示更详细的构建构过程</span>
</span></span><span class="line"><span class="cl">make --debug<span class="o">=</span>v
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Reading makefiles...
</span></span><span class="line"><span class="cl">Reading makefile `Makefile&#39;...
</span></span><span class="line"><span class="cl">Updating goal targets....
</span></span><span class="line"><span class="cl"> File `all&#39; does not exist.
</span></span><span class="line"><span class="cl">   File `all&#39; does not exist.
</span></span><span class="line"><span class="cl">   Looking for an implicit rule for `all&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.c&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.cc&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.C&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.cpp&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.CPP&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.cxx&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.CXX&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.c++&#39;.
</span></span><span class="line"><span class="cl">   Trying pattern rule with stem `all&#39;.
</span></span><span class="line"><span class="cl">   Trying implicit prerequisite `all.C++&#39;.
</span></span><span class="line"><span class="cl">   No implicit rule found for `all&#39;.
</span></span><span class="line"><span class="cl">   Finished prerequisites of target file `all&#39;.
</span></span><span class="line"><span class="cl"> Must remake target `all&#39;.
</span></span><span class="line"><span class="cl">gcc -o all all.o
</span></span><span class="line"><span class="cl">Finished prerequisites of target file `all&#39;.
</span></span><span class="line"><span class="cl">Must remake target `all&#39;.
</span></span><span class="line"><span class="cl">gcc -o all all.o
</span></span><span class="line"><span class="cl">Successfully remade target file `all&#39;.
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 只打印命令不执行</span>
</span></span><span class="line"><span class="cl">make -n
</span></span><span class="line"><span class="cl"><span class="c1"># 输出目标被构建的原因和执行的命令</span>
</span></span><span class="line"><span class="cl">make --trace
</span></span></code></pre></div><p>例如，如果您有一个 makefile，其目标 <code>all</code> 依赖于目标 <code>foo</code> 和 <code>bar</code>，并且您运行 <code>make --trace all</code>，您可能会看到如下输出：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make<span class="o">[</span>1<span class="o">]</span>: Entering directory <span class="s1">&#39;/path/to/project&#39;</span>
</span></span><span class="line"><span class="cl">gcc -o foo foo.c
</span></span><span class="line"><span class="cl">make<span class="o">[</span>1<span class="o">]</span>: Leaving directory <span class="s1">&#39;/path/to/project&#39;</span>
</span></span><span class="line"><span class="cl">make<span class="o">[</span>1<span class="o">]</span>: Entering directory <span class="s1">&#39;/path/to/project&#39;</span>
</span></span><span class="line"><span class="cl">gcc -o bar bar.c
</span></span><span class="line"><span class="cl">make<span class="o">[</span>1<span class="o">]</span>: Leaving directory <span class="s1">&#39;/path/to/project&#39;</span>
</span></span><span class="line"><span class="cl">make<span class="o">[</span>1<span class="o">]</span>: Entering directory <span class="s1">&#39;/path/to/project&#39;</span>
</span></span><span class="line"><span class="cl">gcc -o all foo.o bar.o
</span></span><span class="line"><span class="cl">make<span class="o">[</span>1<span class="o">]</span>: Leaving directory <span class="s1">&#39;/path/to/project&#39;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make -nB  <span class="c1"># -B 可以强制 make 构建所有目标，即使它们已经是最新的</span>
</span></span><span class="line"><span class="cl">make -nB <span class="p">|</span> vim -
</span></span></code></pre></div><p>在 vim 编辑器中进行二次处理，过滤不需要的信息。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 只保留 gcc 或 g++开头的行</span>
</span></span><span class="line"><span class="cl">:%!grep <span class="s2">&#34;^\(gcc\|g++\)&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将环境变量$NEMU_HOME 所指示字符串替换为$NEMU_HOME</span>
</span></span><span class="line"><span class="cl">:%!sed -e <span class="s2">&#34;s+</span><span class="nv">$NEMU_HOME</span><span class="s2">+\$NEMU_HOME+g&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将$NEMU_HOME/build/obj-riscv64-nemu-interpreter 替换为$OBJ_DIR</span>
</span></span><span class="line"><span class="cl">:%s+<span class="se">\$</span>NEMU_HOME/build/obj-riscv64-nemu-interpreter+<span class="nv">$OBJ_DIR</span>+g
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将-c 之前的内容替换为$CFLAGS</span>
</span></span><span class="line"><span class="cl">:%s/-O2.*<span class="o">=</span>riscv64/<span class="nv">$CFLAGS</span>/g
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 将最后一行的空格替换成换行并缩进两格</span>
</span></span><span class="line"><span class="cl">:<span class="nv">$s</span>/  */<span class="se">\r</span>  /g
</span></span></code></pre></div><h1 id="调试技巧选将">调试技巧选将</h1>
<h2 id="断言">断言</h2>
<p>在 C 程序中使用断言（assert）不会增加额外的内存空间，也不会增加数据段空间。断言是一种在运行时检查程序假设是否为真的方法，当断言失败时，程序会终止执行并显示错误信息。</p>
<p>在 C 语言中，断言通常使用宏来实现。它在编译时被解释为一个简单的条件语句，因此它不会增加程序的内存空间或数据段空间。断言宏的定义通常类似于以下代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;assert.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define assert(expression) ((void)0)
</span></span></span></code></pre></div><p>这里的 expression 是要检查的条件。如果 <code>expression</code> 为假，则 <code>assert()</code> 函数会发出错误消息并终止程序的执行。如果 <code>expression</code> 为真，则 <code>assert()</code> 函数不会产生任何操作，并且被解释为 <code>((void)0)</code>。这个语句不会增加任何内存或数据段空间。</p>
<p>需要注意的是，当一个程序使用大量的断言时，它可能会对程序的性能产生一些影响，因为每个断言都需要在运行时进行检查。因此，在生产环境中，应该尽可能减少使用断言，并在测试和调试阶段使用它们来确保代码的正
确性。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// nemu/src/isa/riscv64/local-include/reg.h
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="kt">int</span> <span class="nf">check_reg_idx</span><span class="p">(</span><span class="kt">int</span> <span class="n">idx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nf">IFDEF</span><span class="p">(</span><span class="n">CONFIG_RT_CHECK</span><span class="p">,</span> <span class="nf">assert</span><span class="p">(</span><span class="n">idx</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">idx</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">idx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="编译器工具-sanitizer">编译器工具 sanitizer</h2>
<p>让编译器自动插入 assert, 拦截常见的非预期行为</p>
<ul>
<li>AddressSanitizer - 检查指针越界，use-after-free</li>
<li>ThreadSanitizer - 检查多线程数据竞争</li>
<li>LeakSanitizer - 检查内存泄漏</li>
<li>UndefinedBehaviorSanitizer - 检查 UB</li>
<li>还能检查指针的比较和相减</li>
</ul>
<p>打开后程序运行效率有所下降</p>
<ul>
<li>但调试的时候非常值得，躺着就能让工具帮你找 bug</li>
<li>man gcc 查看具体用法</li>
</ul>
<h3 id="使用方法">使用方法</h3>
<p>GCC 提供了多种 Sanitizer 工具，可以帮助开发者在编译时检测和修复常见的编程错误，例如内存泄漏、缓冲区溢出、使用未初始化的变量等。以下是几个 Sanitizer 工具的示例用法：</p>
<ol>
<li>
<p>Address Sanitizer（ASAN）：检测内存错误，例如使用已经释放的内存、堆栈和全局缓冲区的溢出和下溢等。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gcc -fsanitize=address -g &lt;source files&gt; -o &lt;output file&gt;
</span></span></code></pre></div></li>
<li>
<p>Undefined Behavior Sanitizer（UBSAN）：检测未定义行为，例如除以零、使用未初始化的变量、指针溢出等。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gcc -fsanitize=undefined -g &lt;source files&gt; -o &lt;output file&gt;
</span></span></code></pre></div></li>
<li>
<p>Thread Sanitizer（TSAN）：检测并发问题，例如竞争条件、死锁等。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gcc -fsanitize=thread -g &lt;source files&gt; -o &lt;output file&gt;
</span></span></code></pre></div></li>
<li>
<p>Memory Sanitizer（MSAN）：检测使用未初始化的内存，例如读取未初始化的内存、使用已释放的内存等。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">gcc -fsanitize=memory -g &lt;source files&gt; -o &lt;output file&gt;
</span></span></code></pre></div></li>
</ol>
<p>需要注意的是，Sanitizer 工具可能会增加程序的执行时间和内存消耗，并且可能会产生误报，因此在生产环境中应该禁用 Sanitizer 工具。通常情况下，开发者可以在开发和测试阶段启用 Sanitizer 工具，以帮助他们发现和修复代码中的问题。</p>
<h2 id="自顶向下理解程序行为">自顶向下理解程序行为</h2>
<pre><code>ftrace - 函数调用层次，理解程序的大体行为
itrace - 指令执行层次，理解指令级别的行为
mtrace - 访存的踪迹
dtrace - 设备访问的踪迹
sdb - 灵活细致地检查客户程序的状态
si - 细粒度的状态转移
info r/x - 检查R/M
监视点 - 捕捉某状态发生变化的时刻
</code></pre>
<p>sdb 与 gdb 结合使用</p>
<pre><code>先用 sdb 定位到出错点附近
再用 gdb 观察 NEMU 的细节行为
</code></pre>
<h2 id="程序的运行时间都花在了哪里">程序的运行时间都花在了哪里</h2>
<p>Linux 的性能分析工具 perf 是一款功能强大的性能分析工具，它可以通过硬件计数器（Hardware counter）或者性能事件（Performance event）来对 Linux 系统的性能进行分析。以下是 perf 工具的安装和使用方法。</p>
<h3 id="安装-perf-工具">安装 perf 工具</h3>
<p>在大部分 Linux 发行版中，perf 工具已经预先安装，如果没有预先安装，可以通过以下命令进行安装。</p>
<ul>
<li>Debian/Ubuntu 系统：<code>sudo apt-get install linux-tools-common linux-tools-generic</code></li>
<li>Fedora 系统：<code>sudo dnf install perf</code></li>
<li>CentOS/RHEL 系统：<code>sudo yum install perf</code></li>
</ul>
<p>安装完毕之后，可以通过 <code>perf version</code> 命令来检查 perf 版本信息。</p>
<h3 id="编写一个简单的-c-代码">编写一个简单的 C 代码</h3>
<p>这里我们编写一个简单的 C 代码，用于测试 perf 工具的使用。代码如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="mi">1000000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">sum</span> <span class="o">+=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;sum = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">sum</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>代码的作用是计算 1 到 1000000 的和。</p>
<h3 id="使用-perf-工具">使用 perf 工具</h3>
<p>下面我们使用 perf 工具来对上述代码进行性能分析。假设代码保存在文件 test.c 中。</p>
<p>统计 CPU 周期数
以下命令用于统计程序的 CPU 周期数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">perf stat ./test
</span></span></code></pre></div><p>输出结果类似于：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> Performance counter stats <span class="k">for</span> <span class="s1">&#39;./test&#39;</span>:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">           19,23 msec task-clock:u              <span class="c1">#    0.988 CPUs utilized          </span>
</span></span><span class="line"><span class="cl">                 <span class="m">0</span>      context-switches:u        <span class="c1">#    0.000 K/sec                  </span>
</span></span><span class="line"><span class="cl">                 <span class="m">0</span>      cpu-migrations:u          <span class="c1">#    0.000 K/sec                  </span>
</span></span><span class="line"><span class="cl">               <span class="m">575</span>      page-faults:u             <span class="c1">#    0.030 M/sec                  </span>
</span></span><span class="line"><span class="cl">    64,013,620,231      cycles:u                  <span class="c1">#    3.324 GHz                      (49.80%)</span>
</span></span><span class="line"><span class="cl">    40,010,335,480      instructions:u            <span class="c1">#    0.62  insn per cycle           (62.34%)</span>
</span></span><span class="line"><span class="cl">     9,998,469,566      branches:u                <span class="c1">#  518.693 M/sec                    (62.27%)</span>
</span></span><span class="line"><span class="cl">           763,176      branch-misses:u           <span class="c1">#    0.01% of all branches          (62.32%)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      0.019438122 seconds <span class="nb">time</span> elapsed
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      0.019411000 seconds user
</span></span><span class="line"><span class="cl">      0.000007000 seconds sys
</span></span></code></pre></div><p>输出结果中的 cycles 表示 CPU 周期数，instructions 表示指令数，branches 表示分支指令数。其中，cycles 和 instructions 的比例代表了 CPU 的效率，即 IPC（Instructions Per Cycle）。</p>
<h3 id="统计函数调用次数">统计函数调用次数</h3>
<p>以下命令用于统计程序中函数的调用次数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">perf record -e cycles -g ./test
</span></span></code></pre></div><p>这个命令将启动 perf 工具，并使用 -g 选项记录调用关系图。我们还需要使用 sudo 权限运行该命令，以便 perf 工具可以访问系统的硬件计数器。</p>
<h2 id="成为专业码农">成为专业码农</h2>
<ul>
<li>要熟悉项目了 -&gt; STFW/RTFM/RTFSC, 尝试理解一切细节</li>
<li>要写代码了
<ul>
<li>仔细 RTFM, 正确理解需求</li>
<li>编写可读，可维护，易验证的代码 (不言自明，不言自证)</li>
<li>用 lint 工具检查代码</li>
<li>进行充分的测试</li>
<li>添加充分的断言</li>
</ul>
</li>
<li>要调试了
<ul>
<li>默念“机器永远是对的/未测试代码永远是错的”</li>
<li>sanitizer, trace, printf, gdb, …</li>
</ul>
</li>
<li>平时 -&gt; 用正确的工具/方法做事情</li>
<li>感到不爽了 -&gt; 找正确的工具/搭基础设施</li>
</ul>
<h1 id="总线选讲">总线选讲</h1>
<h2 id="定义">定义</h2>
<p>广义上讲总线就是一个通信系统，以下这些都属于广义的总线概念:TCP/IP, 以太网，网线，RTL 信号，系统调用。</p>
<p>主动发起通信的叫 master，响应通信的叫 slave。</p>
]]></content:encoded>
    </item>
    <item>
      <title>JAVA 小白笔记</title>
      <link>https://lifeislife.cn/posts/java%E5%B0%8F%E7%99%BD%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Sun, 12 Mar 2023 09:55:55 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/java%E5%B0%8F%E7%99%BD%E7%AC%94%E8%AE%B0/</guid>
      <description>&lt;h1 id=&#34;基础设施&#34;&gt;基础设施&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;本章记录一些配置笔记，不是 step by step 教程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;安装-java&#34;&gt;安装 JAVA&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 免登陆下载java&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://xiandan.io/posts/jdk-download.html
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 高速镜像&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://github.com/LilithBristol/javajdkforwinx64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Linux 环境变量 PATH&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%JAVA_HOME%&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;in
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%JAVA_HOME%&lt;span class=&#34;se&#34;&gt;\j&lt;/span&gt;re&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;in
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Windows 环境变量&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# JAVA_HOME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;C:&lt;span class=&#34;se&#34;&gt;\P&lt;/span&gt;rogram Files&lt;span class=&#34;se&#34;&gt;\J&lt;/span&gt;ava&lt;span class=&#34;se&#34;&gt;\j&lt;/span&gt;dk1.8.0_212
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# CLASSPATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;%JAVA_HOME%&lt;span class=&#34;se&#34;&gt;\b&lt;/span&gt;in&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;%JAVA_HOME%&lt;span class=&#34;se&#34;&gt;\l&lt;/span&gt;ib&lt;span class=&#34;se&#34;&gt;\d&lt;/span&gt;t.jar&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;%JAVA_HOME%&lt;span class=&#34;se&#34;&gt;\l&lt;/span&gt;ib&lt;span class=&#34;se&#34;&gt;\t&lt;/span&gt;ools.jar
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;vscode-开发环境&#34;&gt;VSCode 开发环境&lt;/h2&gt;
&lt;h3 id=&#34;基础插件&#34;&gt;基础插件&lt;/h3&gt;
&lt;p&gt;安装 &lt;code&gt;Extension Pack for Java&lt;/code&gt; 即可，会把用到的开发插件都安装。不需要安装 Java Language Support 会和 Extension Pack for Java 中的 Language Support for Java by Red Hat 冲突。目前使用过程中也没有遇到必须使用 Java Language Support 的情况。&lt;/p&gt;
&lt;h3 id=&#34;基本使用&#34;&gt;基本使用&lt;/h3&gt;
&lt;p&gt;使用 CTRL+SHIFT+P 输入 Java: create Project，输入项目名，在 src 文件夹中，选择 Run 运行 Java 代码。&lt;/p&gt;
&lt;h3 id=&#34;常用快捷键&#34;&gt;常用快捷键&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 导包&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nb&#34;&gt;shift&lt;/span&gt; + alt + o
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;idea-开发环境&#34;&gt;IDEA 开发环境&lt;/h2&gt;
&lt;h3 id=&#34;下载安装-idea&#34;&gt;下载安装 IDEA&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.ifengsoft.com/149.html&#34;&gt;Java 集成开发环境 IntelliJ IDEA 2022.3 Ultimate 永久激活版 - 风软资源站&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;配置-mavenidea-中下载速度慢&#34;&gt;配置 MAVEN,IDEA 中下载速度慢&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# IDEA中编辑区右键--maven--create xml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;mirrors&amp;gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;id&amp;gt;alimaven&amp;lt;/id&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;name&amp;gt;aliyun maven&amp;lt;/name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;url&amp;gt;http://maven.aliyun.com/nexus/content/groups/public/&amp;lt;/url&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;mirrorOf&amp;gt;central&amp;lt;/mirrorOf&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;/mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;id&amp;gt;uk&amp;lt;/id&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;mirrorOf&amp;gt;central&amp;lt;/mirrorOf&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;name&amp;gt;Human Readable Name &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; this Mirror.&amp;lt;/name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;url&amp;gt;http://uk.maven.org/maven2/&amp;lt;/url&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;/mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;id&amp;gt;CN&amp;lt;/id&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;name&amp;gt;OSChina Central&amp;lt;/name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;url&amp;gt;http://maven.oschina.net/content/groups/public/&amp;lt;/url&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;mirrorOf&amp;gt;central&amp;lt;/mirrorOf&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;/mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;id&amp;gt;nexus&amp;lt;/id&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;name&amp;gt;internal nexus repository&amp;lt;/name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;url&amp;gt;http://repo.maven.apache.org/maven2&amp;lt;/url&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &amp;lt;mirrorOf&amp;gt;central&amp;lt;/mirrorOf&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &amp;lt;/mirror&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;/mirrors&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;安装-mysql&#34;&gt;安装 MYSQL&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install mysql
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mysql -u root -p
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; /home/user/oa_system/VBlog/blogserver/src/main/resources/vueblog.sql
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;常用命令&#34;&gt;常用命令&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建数据库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CREATE DATABASE ryvue&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 切换当前数据库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;use ryvue&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; character &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; utf8&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 执行sql脚本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; /home/user/oa_system/RuoYi-Vue/sql/ry_20220822.sql
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; /home/user/oa_system/RuoYi-Vue/sql/quartz.sql
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除数据库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;drop database 数据库名&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示所有数据库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;show databases&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建数据库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;create database 数据库名&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 显示数据库编码格式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; SHOW VARIABLES LIKE &lt;span class=&#34;s1&#34;&gt;&amp;#39;character_set_%&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 删除 mysql 密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SET PASSWORD FOR root@localhost&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;PASSWORD&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 重建数据库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;drop database ryvue&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;create database ryvue&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;use ryvue&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;安装数据库可视化工具&#34;&gt;安装数据库可视化工具&lt;/h2&gt;
&lt;h3 id=&#34;mysql-workbench&#34;&gt;mysql-workbench&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deb http://security.ubuntu.com/ubuntu focal-security main&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee /etc/apt/sources.list.d/focal-security.list
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install libssl1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://downloads.mysql.com/archives/get/p/8/file/mysql-workbench-community_8.0.12-1ubuntu18.04_amd64.deb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dpkg -i mysql*.deb 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install -f 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dpkg -i mysql*.deb  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vscode-插件-mysql-by-weijan-chen&#34;&gt;VSCode 插件-MySQL by weijan Chen&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/cweijan/vscode-database-client/blob/HEAD/README_CN.md&#34;&gt;vscode-database-client 官方文档&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//2023/03/12/f35c4232ab20d9bd4f8dac306f617fdc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/03/12/f35c4232ab20d9bd4f8dac306f617fdc.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;h2 id=&#34;安装-npm&#34;&gt;安装 NPM&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; sudo apt install npm -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;progress&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm config &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; registry http://registry.npmjs.org/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install --legacy-peer-deps
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;reify:abbrev: sill audit bulk request
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;删除 package-lock.json 文件
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;安装-nvm&#34;&gt;安装 nvm&lt;/h2&gt;
&lt;p&gt;打开终端并输入以下命令以在 Ubuntu 上下载 nvm：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该命令将从 nvm GitHub 存储库下载安装脚本，并使用 bash 在您的 Ubuntu 系统上运行它。运行此命令后，nvm 将被安装在您的家目录中。&lt;/p&gt;
&lt;p&gt;安装完成后，在终端中运行以下命令，以使 nvm 生效：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;确认 nvm 是否正确安装：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果一切顺利，您将看到 nvm 的版本号。&lt;/p&gt;
&lt;h2 id=&#34;安装-nodejs-和-npm&#34;&gt;安装 node.js 和 npm&lt;/h2&gt;
&lt;p&gt;使用 nvm 安装特定版本的 Node.js：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm install &amp;lt;node-version&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm install 14.17.6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将安装 Node.js 版本 14.17.6。&lt;/p&gt;
&lt;p&gt;安装完成后，使用以下命令将已安装的 Node.js 版本设置为默认版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm &lt;span class=&#34;nb&#34;&gt;alias&lt;/span&gt; default &amp;lt;node-version&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm &lt;span class=&#34;nb&#34;&gt;alias&lt;/span&gt; default 14.17.6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将设置 Node.js 版本 14.17.6 为默认版本。&lt;/p&gt;
&lt;p&gt;确认 Node.js 和 npm 是否正确安装：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;node -v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果一切顺利，您将看到 Node.js 和 npm 的版本号。&lt;/p&gt;
&lt;p&gt;使用 nvm 切换 Node.js 版本：&lt;/p&gt;
&lt;p&gt;使用以下命令查看可用的 Node.js 版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm ls-remote
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该命令将显示可用的 Node.js 版本列表。&lt;/p&gt;
&lt;p&gt;使用以下命令安装特定版本的 Node.js：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm install &amp;lt;node-version&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm install 12.22.6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将安装 Node.js 版本 12.22.6。&lt;/p&gt;
&lt;p&gt;使用以下命令切换到特定版本的 Node.js：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm use &amp;lt;node-version&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nvm use 12.22.6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这将切换到 Node.js 版本 12.22.6。&lt;/p&gt;
&lt;p&gt;确认当前使用的 Node.js 版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;node -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果一切顺利，您将看到当前使用的 Node.js 版本号。&lt;/p&gt;
&lt;h1 id=&#34;error-合集&#34;&gt;ERROR 合集&lt;/h1&gt;
&lt;h2 id=&#34;java&#34;&gt;Java&lt;/h2&gt;
&lt;h3 id=&#34;error-could-not-find-or-load-main-class-orgapachemavenwrappermavenwrappermain&#34;&gt;Error: Could not find or load main class org.apache.maven.wrapper.MavenWrapperMain&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;maven 相关依赖还没下载完
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;json-parse-error-cannot-construct-instance-of&#34;&gt;JSON parse error: Cannot construct instance of&lt;/h3&gt;
&lt;p&gt;确认请求方式是 get 还是 post，如果是 post 是不是前端发了一个空串。空串要用{}包裹&lt;/p&gt;
&lt;h3 id=&#34;error-java_home-is-not-defined-correctly&#34;&gt;Error: JAVA_HOME is not defined correctly&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 缺少.mavenrc 配置文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim ~/.mavenrc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将 JAVA 配置放进去&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;JAVA_HOME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/lib/jvm/jdk1dot8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;配置数据表中不存在的字段&#34;&gt;配置数据表中不存在的字段&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@TableField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exist&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;error-creating-bean-with-name-miniocontroller--endpoint-must-not-be-null&#34;&gt;Error creating bean with name &amp;lsquo;minioController&amp;rsquo;  endpoint must not be null&lt;/h3&gt;
&lt;p&gt;检查配置文件是否配置了 endpoint&lt;/p&gt;
&lt;h3 id=&#34;解决-mybatis-报错-orgapacheibatisbindingbindingexception-invalid-bound-statement-not-found&#34;&gt;解决 MyBatis 报错 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)&lt;/h3&gt;
&lt;p&gt;1、检查 xml 文件的 namespace 是否对应接口，要是全路径。&lt;/p&gt;
&lt;p&gt;2、xml 中的函数 id 和接口中的函数名是否对得上，参数类型、返回值类型是否对得上&lt;/p&gt;
&lt;p&gt;3、去看输出目录中有没有 xml 映射文件，maven 项目默认把资源文件放在 src/main/resources 下，默认只识别 src/main/resources 下的资源文件。&lt;/p&gt;
&lt;h3 id=&#34;unable-to-obtain-localdatetime-from-temporalaccessor&#34;&gt;Unable to obtain LocalDateTime from TemporalAccessor&lt;/h3&gt;
&lt;p&gt;You can&amp;rsquo;t parse a date string into LocalDateTime without a time.&lt;/p&gt;
&lt;p&gt;LocalDateTime.parse(&amp;ldquo;2019-10-25&amp;rdquo;, DateTimeFormatter.ofPattern(&amp;ldquo;yyyy-MM-dd&amp;rdquo;))&lt;/p&gt;
&lt;p&gt;You should parse the string into &lt;code&gt;LocalDate&lt;/code&gt; and call &lt;code&gt;LocalDate.atStartOfDay()&lt;/code&gt; to return &lt;code&gt;LocalDateTime&lt;/code&gt; with time &lt;code&gt;00:00:00&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;LocalDate.parse(&amp;ldquo;2019-10-25&amp;rdquo;, DateTimeFormatter.ofPattern(&amp;ldquo;yyyy-MM-dd&amp;rdquo;)).atStartOfDay()&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 传入的时间格式要和解析的时间格式保持一致，如以下解析方式，传入参数  2023-03-08 11:11:11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormatter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ofPattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;yyyy-MM-dd HH:mm:ss&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormatter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ofPattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;yyyy-MM-dd HH:mm:ss&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormatter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;localDateFmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormatter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ofPattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;yyyy-MM-dd&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormatter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;localDateFmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DateTimeFormatter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ofPattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;yyyy-MM-dd&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;equals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;equals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startDate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;localDateFmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endDate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;localDateFmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startDate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;MIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endDateTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endDate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LocalTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;MAX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;startDateTime: &amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;endDateTime: &amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;queryWrapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;between&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;create_time&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endDateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;mybatisplus-提交数据后无法立即被查询到&#34;&gt;mybatisplus 提交数据后无法立即被查询到&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;提升事务隔离级别&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Transactional&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isolation&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Isolation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;READ_UNCOMMITTED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;one-record-is-expected-but-the-query-result-is-multiple-records&#34;&gt;One record is expected, but the query result is multiple records&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;解决方案&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;：&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;如果想取一条并不想报错时使用&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;queryWrapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;注意-mybatisplus-的-sql-返回值&#34;&gt;注意 mybatisplus 的 sql 返回值&lt;/h3&gt;
&lt;h3 id=&#34;javalangnumberformatexception-null&#34;&gt;java.lang.NumberFormatException: null&lt;/h3&gt;
&lt;p&gt;检查使用 Integer.parseInt 转换时，是否转换的数可能为 null&lt;/p&gt;
&lt;h4 id=&#34;unexpected-error-occurred-in-scheduled-taskjavalangnullpointerexception-null&#34;&gt;Unexpected error occurred in scheduled taskjava.lang.NullPointerException: null&lt;/h4&gt;
&lt;p&gt;服务类没有正确注入，每一个需要注入类都需要添加 Autowire 注解&lt;/p&gt;
&lt;h3 id=&#34;unhandled-exception-type&#34;&gt;Unhandled exception type&lt;/h3&gt;
&lt;p&gt;原因：被强制异常处理的代码块，必须进行异常处理，否则编译器会提示“Unhandled exception type Exception”错误警告。&lt;/p&gt;
&lt;p&gt;需要将代码写到 try catch 里！&lt;/p&gt;
&lt;h2 id=&#34;mysql&#34;&gt;MySQL&lt;/h2&gt;
&lt;h3 id=&#34;数据库乱码前端乱码&#34;&gt;数据库乱码，前端乱码&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/mysql/my.cnf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 填写如下配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;client&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;default-character-set&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;utf8mb4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;mysqld&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;character-set-server &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; utf8mb4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;collation-server &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; utf8mb4_unicode_ci
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;init_connect&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SET NAMES utf8mb4&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;skip-character-set-client-handshake &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;mysql&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;default-character-set &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; utf8mb4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;nested-exception-is-javalangnullpointerexception-with-root-cause&#34;&gt;nested exception is java.lang.NullPointerException] with root cause&lt;/h3&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/11-21-20-55f1e2de81e6858f99707dea6a5e0292-20230216112119-174d50.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/11-21-20-55f1e2de81e6858f99707dea6a5e0292-20230216112119-174d50.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;error-attempting-to-get-column-motion_id-from-result-set&#34;&gt;Error attempting to get column &amp;lsquo;motion_id&amp;rsquo; from result set&lt;/h3&gt;
&lt;p&gt;数据库字段类型与后端类型不一致&lt;/p&gt;
&lt;h2 id=&#34;前端&#34;&gt;前端&lt;/h2&gt;
&lt;h3 id=&#34;digital-envelope-routinesunsupported&#34;&gt;digital envelope routines::unsupported&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;NODE_OPTIONS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;--openssl-legacy-provider
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;禁止跨域策略-cors-policy&#34;&gt;禁止跨域策略 (CORS policy)&lt;/h3&gt;
&lt;h3 id=&#34;node-openssl-legacy-provider-is-not-allowed-in-node_options&#34;&gt;node: &amp;ndash;openssl-legacy-provider is not allowed in NODE_OPTIONS&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NODE_OPTIONS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;unexpected-character-&#34;&gt;Unexpected character (&amp;rsquo;}&amp;rsquo;&lt;/h3&gt;
&lt;p&gt;请求的时候最后一个字段后面不要加逗号&lt;/p&gt;
&lt;h3 id=&#34;the-value-of-the-access-control-allow-origin-header-in-the-response-must-not-be-the-wildcard--when-the-requests-credentials-mode-is-include-the-credentials-mode-of-requests-initiated-by-the-xmlhttprequest-is-controlled-by-the-withcredentials-attribute&#34;&gt;The value of the &amp;lsquo;Access-Control-Allow-Origin&amp;rsquo; header in the response must not be the wildcard &amp;lsquo;*&amp;rsquo; when the request&amp;rsquo;s credentials mode is &amp;lsquo;include&amp;rsquo;. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute&lt;/h3&gt;
&lt;p&gt;这个错误通常是因为在使用 XMLHttpRequest 对象进行跨域请求时，服务器返回的响应头中的 Access-Control-Allow-Origin 的值为*，但请求的 withCredentials 属性被设置为 true，这两者之间是相互冲突的。&lt;/p&gt;
&lt;p&gt;XMLHttpRequest 对象具有 withCredentials 属性，如果设置为 true，它将在请求中包括来自其他域的 cookie 等凭据信息。但是，如果服务器在响应头中将 Access-Control-Allow-Origin 设置为*，浏览器会禁止访问这些凭据信息。这是一项安全保护措施，防止敏感信息泄露。&lt;/p&gt;
&lt;p&gt;解决这个问题的方法是，在服务器端，将 Access-Control-Allow-Origin 设置为请求来源的域名，而不是使用通配符*。这可以让浏览器安全地发送凭据信息。&lt;/p&gt;
&lt;p&gt;在前端，需要将 withCredentials 属性设置为 true，以便在请求中包含凭据信息。同时，需要确保请求的来源域名与服务器端设置的 Access-Control-Allow-Origin 一致。&lt;/p&gt;
&lt;p&gt;如果你无法更改服务器端的设置，可以考虑使用代理或者 JSONP 等跨域解决方案。&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//2023/02/22/3c3171236ca43b99771480eeea4a6f2f.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/02/22/3c3171236ca43b99771480eeea4a6f2f.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;vue-项目端口不固定&#34;&gt;VUE 项目端口不固定&lt;/h3&gt;
&lt;h3 id=&#34;application-run-failed-orgspringframeworkbeansfactorybeancreationexception-error-creating-bean-with-name-communityinfocontroller&#34;&gt;Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name &amp;lsquo;communityInfoController&amp;rsquo;&lt;/h3&gt;
&lt;p&gt;检查target/classes/mapper/DepartmentMapper.xml中的格式是否正确，检查引号是否多了，少了&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;findIdByOrgId&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resultType&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;resultType=&amp;#34;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;java&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lang&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    SELECT CAST(id AS UNSIGNED) AS id FROM department WHERE organization_id = #{orgId}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    &amp;lt;/select&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;错误码&#34;&gt;错误码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;400-前后端参数对不上&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;postget-there-is-already-xx-bean-method&#34;&gt;{POST/GET} there is already xx bean method&lt;/h3&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/19-15-47-0a06b9a67f496f6ec023534fc4381876-20230217191545-f22ccc.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-15-47-0a06b9a67f496f6ec023534fc4381876-20230217191545-f22ccc.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;npm-启动digital-envelope-routinesunsupported&#34;&gt;NPM 启动:digital envelope routines::unsupported&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;NODE_OPTIONS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;--openssl-legacy-provider
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run serve
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;the-field-file-exceeds-its-maximum-permitted-size-of-1048576-bytes&#34;&gt;The field file exceeds its maximum permitted size of 1048576 bytes&lt;/h3&gt;
&lt;p&gt;spring boot 上传文件时接口报错 The field file exceeds its maximum permitted size of 1048576 bytes.经排查官方设置每个文件的配置最大为 1Mb，单次请求的文件的总数不能大于 10Mb，上传大于 1Mb 的文件需要修改配置文件（application.properties）
1.Spring Boot 1.3.x 或者之前&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;multipart.maxFileSize=100Mb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;multipart.maxRequestSize=1000Mb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;2.Spring Boot 1.4.x 以后&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;spring.http.multipart.maxFileSize=100Mb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;spring.http.multipart.maxRequestSize=1000Mb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;3.Spring Boot 2.0 之后&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;spring.servlet.multipart.max-file-size=100MB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;spring.servlet.multipart.max-request-size=1000MB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;字段不存在&#34;&gt;字段不存在&lt;/h3&gt;
&lt;p&gt;请求的时候字段名字和 java 中命名保持一直，而不是和数据库名字保持一样&lt;/p&gt;
&lt;h3 id=&#34;数据库-communications-link-failure&#34;&gt;数据库 communications link failure&lt;/h3&gt;
&lt;h3 id=&#34;配置请求超时时间&#34;&gt;配置请求超时时间&lt;/h3&gt;
&lt;p&gt;src/utils/request.js&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/14-18-57-4ffa9b2c1ef5817aa81ba91b0257cddc-20230214141856-8b5215.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-18-57-4ffa9b2c1ef5817aa81ba91b0257cddc-20230214141856-8b5215.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;request-method-get-not-supported&#34;&gt;Request method GET not supported&lt;/h3&gt;
&lt;p&gt;前端请求事件没有设置请求方式  post 还是 get&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/10-27-11-20135e8d24b909084fc2d36e3ca3d469-20230208102710-382029.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-27-11-20135e8d24b909084fc2d36e3ca3d469-20230208102710-382029.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;invalid-cros-request&#34;&gt;Invalid cros request&lt;/h3&gt;
&lt;p&gt;跨域&lt;/p&gt;
&lt;h3 id=&#34;处理未来数据&#34;&gt;处理未来数据&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.bilibili.com/video/BV1U44y1W77D?t=1655.5&amp;amp;p=23&#34;&gt;https://www.bilibili.com/video/BV1U44y1W77D?t=1655.5&amp;amp;p=23&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;tips&#34;&gt;TIPS&lt;/h1&gt;
&lt;h3 id=&#34;前端保存代码需要等待一段时间生效&#34;&gt;前端保存代码需要等待一段时间生效&lt;/h3&gt;
&lt;h3 id=&#34;获取用户-ip&#34;&gt;获取用户 IP&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userIp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getRemoteAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;解决方案java-实体类字段-不返回给前端&#34;&gt;解决方案：Java 实体类字段 不返回给前端&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@JsonIgnore&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@ApiModelProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;不重要&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@TableField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exist&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unimportant&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;dateutil-包&#34;&gt;Dateutil 包&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.text.SimpleDateFormat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.util.Calendar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.util.Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;获取请求参数&#34;&gt;获取请求参数&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;preHandle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpServletResponse&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//获取请求参数&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;queryString&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getQueryString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;请求参数:{}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;queryString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;后端设置-header-前端获取不到&#34;&gt;后端设置 header 前端获取不到&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;必须要加这条字段控制能够获取的&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addHeader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Access-Control-Expose-Headers&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addHeader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;sdfdsfdsf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;axios-请求&#34;&gt;axios 请求&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;axios&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//opm.eswincomputing.com:9090/user/page&amp;#39;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;查询端口占用&#34;&gt;查询端口占用&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;lsof&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;lsof&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;：&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;查看&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;端口占用&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;打印输出&#34;&gt;打印输出&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Application&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;yml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nl&#34;&gt;logging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;eswincomputing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;springboot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;使用&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Slf4j&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@RestController&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/patch-record&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;PatchRecordController&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Resource&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IPatchRecordService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;patchRecordServic&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;校验字符串是否为空&#34;&gt;校验字符串是否为空&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;StrUtil.isBlank&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;查询主键&#34;&gt;查询主键&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SELECT id FROM department WHERE &lt;span class=&#34;nv&#34;&gt;organization_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#{orgId}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;字符整型互转&#34;&gt;字符整型互转&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Integer.parseInt&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;user.getDelFlag&lt;span class=&#34;o&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;解析-json-字符串&#34;&gt;解析 json 字符串&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;       
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;!--&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;解析&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dependency&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;groupId&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;alibaba&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;groupId&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;artifactId&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fastjson&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;artifactId&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dependency&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;JSONObject&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;JSONObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parseObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;patchRecord&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getAttachmentList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fileUID&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;file_uid&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fileName&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;file_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;逗号分割字符串&#34;&gt;逗号分割字符串&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Java&#34; data-lang=&#34;Java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;张三，李四，王五，马六，小气&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;substring&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;substring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;substring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;substring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//以逗号分割&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;数据--&amp;gt;&amp;gt;&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h1 id="基础设施">基础设施</h1>
<blockquote>
<p>本章记录一些配置笔记，不是 step by step 教程</p>
</blockquote>
<h2 id="安装-java">安装 JAVA</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 免登陆下载java</span>
</span></span><span class="line"><span class="cl">https://xiandan.io/posts/jdk-download.html
</span></span><span class="line"><span class="cl"><span class="c1"># 高速镜像</span>
</span></span><span class="line"><span class="cl">https://github.com/LilithBristol/javajdkforwinx64
</span></span></code></pre></div><p>Linux 环境变量 PATH</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">%JAVA_HOME%<span class="se">\b</span>in
</span></span><span class="line"><span class="cl">%JAVA_HOME%<span class="se">\j</span>re<span class="se">\b</span>in
</span></span></code></pre></div><p>Windows 环境变量</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># JAVA_HOME</span>
</span></span><span class="line"><span class="cl">C:<span class="se">\P</span>rogram Files<span class="se">\J</span>ava<span class="se">\j</span>dk1.8.0_212
</span></span><span class="line"><span class="cl"><span class="c1"># CLASSPATH</span>
</span></span><span class="line"><span class="cl">.<span class="p">;</span>%JAVA_HOME%<span class="se">\b</span>in<span class="p">;</span>%JAVA_HOME%<span class="se">\l</span>ib<span class="se">\d</span>t.jar<span class="p">;</span>%JAVA_HOME%<span class="se">\l</span>ib<span class="se">\t</span>ools.jar
</span></span></code></pre></div><h2 id="vscode-开发环境">VSCode 开发环境</h2>
<h3 id="基础插件">基础插件</h3>
<p>安装 <code>Extension Pack for Java</code> 即可，会把用到的开发插件都安装。不需要安装 Java Language Support 会和 Extension Pack for Java 中的 Language Support for Java by Red Hat 冲突。目前使用过程中也没有遇到必须使用 Java Language Support 的情况。</p>
<h3 id="基本使用">基本使用</h3>
<p>使用 CTRL+SHIFT+P 输入 Java: create Project，输入项目名，在 src 文件夹中，选择 Run 运行 Java 代码。</p>
<h3 id="常用快捷键">常用快捷键</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 导包</span>
</span></span><span class="line"><span class="cl"> <span class="nb">shift</span> + alt + o
</span></span></code></pre></div><h2 id="idea-开发环境">IDEA 开发环境</h2>
<h3 id="下载安装-idea">下载安装 IDEA</h3>
<p><a href="https://www.ifengsoft.com/149.html">Java 集成开发环境 IntelliJ IDEA 2022.3 Ultimate 永久激活版 - 风软资源站</a></p>
<h3 id="配置-mavenidea-中下载速度慢">配置 MAVEN,IDEA 中下载速度慢</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># IDEA中编辑区右键--maven--create xml</span>
</span></span><span class="line"><span class="cl">&lt;mirrors&gt;  
</span></span><span class="line"><span class="cl">    &lt;mirror&gt;
</span></span><span class="line"><span class="cl">        &lt;id&gt;alimaven&lt;/id&gt;
</span></span><span class="line"><span class="cl">        &lt;name&gt;aliyun maven&lt;/name&gt;
</span></span><span class="line"><span class="cl">        &lt;url&gt;http://maven.aliyun.com/nexus/content/groups/public/&lt;/url&gt;
</span></span><span class="line"><span class="cl">        &lt;mirrorOf&gt;central&lt;/mirrorOf&gt;
</span></span><span class="line"><span class="cl">    &lt;/mirror&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    &lt;mirror&gt;
</span></span><span class="line"><span class="cl">        &lt;id&gt;uk&lt;/id&gt;
</span></span><span class="line"><span class="cl">        &lt;mirrorOf&gt;central&lt;/mirrorOf&gt;
</span></span><span class="line"><span class="cl">        &lt;name&gt;Human Readable Name <span class="k">for</span> this Mirror.&lt;/name&gt;
</span></span><span class="line"><span class="cl">        &lt;url&gt;http://uk.maven.org/maven2/&lt;/url&gt;
</span></span><span class="line"><span class="cl">    &lt;/mirror&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    &lt;mirror&gt;
</span></span><span class="line"><span class="cl">        &lt;id&gt;CN&lt;/id&gt;
</span></span><span class="line"><span class="cl">        &lt;name&gt;OSChina Central&lt;/name&gt;
</span></span><span class="line"><span class="cl">        &lt;url&gt;http://maven.oschina.net/content/groups/public/&lt;/url&gt;
</span></span><span class="line"><span class="cl">        &lt;mirrorOf&gt;central&lt;/mirrorOf&gt;
</span></span><span class="line"><span class="cl">    &lt;/mirror&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    &lt;mirror&gt;
</span></span><span class="line"><span class="cl">        &lt;id&gt;nexus&lt;/id&gt;
</span></span><span class="line"><span class="cl">        &lt;name&gt;internal nexus repository&lt;/name&gt;
</span></span><span class="line"><span class="cl">        &lt;url&gt;http://repo.maven.apache.org/maven2&lt;/url&gt;
</span></span><span class="line"><span class="cl">        &lt;mirrorOf&gt;central&lt;/mirrorOf&gt;
</span></span><span class="line"><span class="cl">    &lt;/mirror&gt;
</span></span><span class="line"><span class="cl">&lt;/mirrors&gt;
</span></span></code></pre></div><h2 id="安装-mysql">安装 MYSQL</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt install mysql
</span></span><span class="line"><span class="cl">sudo mysql -u root -p
</span></span><span class="line"><span class="cl"><span class="nb">source</span> /home/user/oa_system/VBlog/blogserver/src/main/resources/vueblog.sql
</span></span></code></pre></div><h3 id="常用命令">常用命令</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 创建数据库</span>
</span></span><span class="line"><span class="cl">CREATE DATABASE ryvue<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 切换当前数据库</span>
</span></span><span class="line"><span class="cl">use ryvue<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> character <span class="nb">set</span> utf8<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 执行sql脚本</span>
</span></span><span class="line"><span class="cl"><span class="nb">source</span> /home/user/oa_system/RuoYi-Vue/sql/ry_20220822.sql
</span></span><span class="line"><span class="cl"><span class="nb">source</span> /home/user/oa_system/RuoYi-Vue/sql/quartz.sql
</span></span><span class="line"><span class="cl"><span class="c1"># 删除数据库</span>
</span></span><span class="line"><span class="cl">drop database 数据库名<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 显示所有数据库</span>
</span></span><span class="line"><span class="cl">show databases<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 创建数据库</span>
</span></span><span class="line"><span class="cl">create database 数据库名<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 显示数据库编码格式</span>
</span></span><span class="line"><span class="cl"> SHOW VARIABLES LIKE <span class="s1">&#39;character_set_%&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 删除 mysql 密码</span>
</span></span><span class="line"><span class="cl">SET PASSWORD FOR root@localhost<span class="o">=</span>PASSWORD<span class="o">(</span><span class="s1">&#39;&#39;</span><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 重建数据库</span>
</span></span><span class="line"><span class="cl">drop database ryvue<span class="p">;</span>
</span></span><span class="line"><span class="cl">create database ryvue<span class="p">;</span>
</span></span><span class="line"><span class="cl">use ryvue<span class="p">;</span>
</span></span></code></pre></div><h2 id="安装数据库可视化工具">安装数据库可视化工具</h2>
<h3 id="mysql-workbench">mysql-workbench</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;deb http://security.ubuntu.com/ubuntu focal-security main&#34;</span> <span class="p">|</span> sudo tee /etc/apt/sources.list.d/focal-security.list
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get install libssl1.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">wget https://downloads.mysql.com/archives/get/p/8/file/mysql-workbench-community_8.0.12-1ubuntu18.04_amd64.deb
</span></span><span class="line"><span class="cl">sudo dpkg -i mysql*.deb 
</span></span><span class="line"><span class="cl">sudo apt-get install -f 
</span></span><span class="line"><span class="cl">sudo dpkg -i mysql*.deb  
</span></span></code></pre></div><h3 id="vscode-插件-mysql-by-weijan-chen">VSCode 插件-MySQL by weijan Chen</h3>
<p><a href="https://github.com/cweijan/vscode-database-client/blob/HEAD/README_CN.md">vscode-database-client 官方文档</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//2023/03/12/f35c4232ab20d9bd4f8dac306f617fdc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/03/12/f35c4232ab20d9bd4f8dac306f617fdc.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>
<h2 id="安装-npm">安装 NPM</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"> sudo apt install npm -y
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">npm <span class="nb">set</span> <span class="nv">progress</span><span class="o">=</span><span class="nb">false</span> 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">npm config <span class="nb">set</span> registry http://registry.npmjs.org/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">npm install --legacy-peer-deps
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">reify:abbrev: sill audit bulk request
</span></span><span class="line"><span class="cl">删除 package-lock.json 文件
</span></span></code></pre></div><h2 id="安装-nvm">安装 nvm</h2>
<p>打开终端并输入以下命令以在 Ubuntu 上下载 nvm：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh <span class="p">|</span> bash
</span></span></code></pre></div><p>该命令将从 nvm GitHub 存储库下载安装脚本，并使用 bash 在您的 Ubuntu 系统上运行它。运行此命令后，nvm 将被安装在您的家目录中。</p>
<p>安装完成后，在终端中运行以下命令，以使 nvm 生效：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">source</span> ~/.bashrc
</span></span></code></pre></div><p>确认 nvm 是否正确安装：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm --version
</span></span></code></pre></div><p>如果一切顺利，您将看到 nvm 的版本号。</p>
<h2 id="安装-nodejs-和-npm">安装 node.js 和 npm</h2>
<p>使用 nvm 安装特定版本的 Node.js：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm install &lt;node-version&gt;
</span></span></code></pre></div><p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm install 14.17.6
</span></span></code></pre></div><p>这将安装 Node.js 版本 14.17.6。</p>
<p>安装完成后，使用以下命令将已安装的 Node.js 版本设置为默认版本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm <span class="nb">alias</span> default &lt;node-version&gt;
</span></span></code></pre></div><p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm <span class="nb">alias</span> default 14.17.6
</span></span></code></pre></div><p>这将设置 Node.js 版本 14.17.6 为默认版本。</p>
<p>确认 Node.js 和 npm 是否正确安装：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">node -v
</span></span><span class="line"><span class="cl">npm -v
</span></span></code></pre></div><p>如果一切顺利，您将看到 Node.js 和 npm 的版本号。</p>
<p>使用 nvm 切换 Node.js 版本：</p>
<p>使用以下命令查看可用的 Node.js 版本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm ls-remote
</span></span></code></pre></div><p>该命令将显示可用的 Node.js 版本列表。</p>
<p>使用以下命令安装特定版本的 Node.js：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm install &lt;node-version&gt;
</span></span></code></pre></div><p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm install 12.22.6
</span></span></code></pre></div><p>这将安装 Node.js 版本 12.22.6。</p>
<p>使用以下命令切换到特定版本的 Node.js：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm use &lt;node-version&gt;
</span></span></code></pre></div><p>例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nvm use 12.22.6
</span></span></code></pre></div><p>这将切换到 Node.js 版本 12.22.6。</p>
<p>确认当前使用的 Node.js 版本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">node -v
</span></span></code></pre></div><p>如果一切顺利，您将看到当前使用的 Node.js 版本号。</p>
<h1 id="error-合集">ERROR 合集</h1>
<h2 id="java">Java</h2>
<h3 id="error-could-not-find-or-load-main-class-orgapachemavenwrappermavenwrappermain">Error: Could not find or load main class org.apache.maven.wrapper.MavenWrapperMain</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">maven 相关依赖还没下载完
</span></span></code></pre></div><h3 id="json-parse-error-cannot-construct-instance-of">JSON parse error: Cannot construct instance of</h3>
<p>确认请求方式是 get 还是 post，如果是 post 是不是前端发了一个空串。空串要用{}包裹</p>
<h3 id="error-java_home-is-not-defined-correctly">Error: JAVA_HOME is not defined correctly</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 缺少.mavenrc 配置文件</span>
</span></span><span class="line"><span class="cl">vim ~/.mavenrc
</span></span><span class="line"><span class="cl"><span class="c1"># 将 JAVA 配置放进去</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">JAVA_HOME</span><span class="o">=</span>/usr/lib/jvm/jdk1dot8
</span></span></code></pre></div><h3 id="配置数据表中不存在的字段">配置数据表中不存在的字段</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@TableField</span><span class="p">(</span><span class="n">exist</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><h3 id="error-creating-bean-with-name-miniocontroller--endpoint-must-not-be-null">Error creating bean with name &lsquo;minioController&rsquo;  endpoint must not be null</h3>
<p>检查配置文件是否配置了 endpoint</p>
<h3 id="解决-mybatis-报错-orgapacheibatisbindingbindingexception-invalid-bound-statement-not-found">解决 MyBatis 报错 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)</h3>
<p>1、检查 xml 文件的 namespace 是否对应接口，要是全路径。</p>
<p>2、xml 中的函数 id 和接口中的函数名是否对得上，参数类型、返回值类型是否对得上</p>
<p>3、去看输出目录中有没有 xml 映射文件，maven 项目默认把资源文件放在 src/main/resources 下，默认只识别 src/main/resources 下的资源文件。</p>
<h3 id="unable-to-obtain-localdatetime-from-temporalaccessor">Unable to obtain LocalDateTime from TemporalAccessor</h3>
<p>You can&rsquo;t parse a date string into LocalDateTime without a time.</p>
<p>LocalDateTime.parse(&ldquo;2019-10-25&rdquo;, DateTimeFormatter.ofPattern(&ldquo;yyyy-MM-dd&rdquo;))</p>
<p>You should parse the string into <code>LocalDate</code> and call <code>LocalDate.atStartOfDay()</code> to return <code>LocalDateTime</code> with time <code>00:00:00</code>.</p>
<p>LocalDate.parse(&ldquo;2019-10-25&rdquo;, DateTimeFormatter.ofPattern(&ldquo;yyyy-MM-dd&rdquo;)).atStartOfDay()</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="c1">// 传入的时间格式要和解析的时间格式保持一致，如以下解析方式，传入参数  2023-03-08 11:11:11</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">LocalDateTime</span><span class="w"> </span><span class="n">startDateTime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LocalDateTime</span><span class="p">.</span><span class="na">parse</span><span class="p">(</span><span class="n">startTime</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">DateTimeFormatter</span><span class="p">.</span><span class="na">ofPattern</span><span class="p">(</span><span class="s">&#34;yyyy-MM-dd HH:mm:ss&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">LocalDateTime</span><span class="w"> </span><span class="n">endDateTime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LocalDateTime</span><span class="p">.</span><span class="na">parse</span><span class="p">(</span><span class="n">endTime</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">DateTimeFormatter</span><span class="p">.</span><span class="na">ofPattern</span><span class="p">(</span><span class="s">&#34;yyyy-MM-dd HH:mm:ss&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">DateTimeFormatter</span><span class="w"> </span><span class="n">localDateFmt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">DateTimeFormatter</span><span class="p">.</span><span class="na">ofPattern</span><span class="p">(</span><span class="s">&#34;yyyy-MM-dd&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DateTimeFormatter</span><span class="w"> </span><span class="n">localDateFmt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">DateTimeFormatter</span><span class="p">.</span><span class="na">ofPattern</span><span class="p">(</span><span class="s">&#34;yyyy-MM-dd&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="s">&#34;&#34;</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">startTime</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="o">!</span><span class="s">&#34;&#34;</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">endTime</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">LocalDate</span><span class="w"> </span><span class="n">startDate</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LocalDate</span><span class="p">.</span><span class="na">parse</span><span class="p">(</span><span class="n">startTime</span><span class="p">,</span><span class="w"> </span><span class="n">localDateFmt</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">LocalDate</span><span class="w"> </span><span class="n">endDate</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LocalDate</span><span class="p">.</span><span class="na">parse</span><span class="p">(</span><span class="n">endTime</span><span class="p">,</span><span class="w"> </span><span class="n">localDateFmt</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">LocalDateTime</span><span class="w"> </span><span class="n">startDateTime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LocalDateTime</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">startDate</span><span class="p">,</span><span class="w"> </span><span class="n">LocalTime</span><span class="p">.</span><span class="na">MIN</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">LocalDateTime</span><span class="w"> </span><span class="n">endDateTime</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">LocalDateTime</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">endDate</span><span class="p">,</span><span class="w"> </span><span class="n">LocalTime</span><span class="p">.</span><span class="na">MAX</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&#34;startDateTime: &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">startDateTime</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&#34;endDateTime: &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">endDateTime</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="n">queryWrapper</span><span class="p">.</span><span class="na">between</span><span class="p">(</span><span class="s">&#34;create_time&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">startDateTime</span><span class="p">,</span><span class="w"> </span><span class="n">endDateTime</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="mybatisplus-提交数据后无法立即被查询到">mybatisplus 提交数据后无法立即被查询到</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">提升事务隔离级别</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Transactional</span><span class="p">(</span><span class="n">isolation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Isolation</span><span class="p">.</span><span class="na">READ_UNCOMMITTED</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><h3 id="one-record-is-expected-but-the-query-result-is-multiple-records">One record is expected, but the query result is multiple records</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">解决方案</span><span class="err">：</span><span class="n">如果想取一条并不想报错时使用</span><span class="w"> </span><span class="nf">getOne</span><span class="p">(</span><span class="n">queryWrapper</span><span class="p">,</span><span class="kc">false</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><h3 id="注意-mybatisplus-的-sql-返回值">注意 mybatisplus 的 sql 返回值</h3>
<h3 id="javalangnumberformatexception-null">java.lang.NumberFormatException: null</h3>
<p>检查使用 Integer.parseInt 转换时，是否转换的数可能为 null</p>
<h4 id="unexpected-error-occurred-in-scheduled-taskjavalangnullpointerexception-null">Unexpected error occurred in scheduled taskjava.lang.NullPointerException: null</h4>
<p>服务类没有正确注入，每一个需要注入类都需要添加 Autowire 注解</p>
<h3 id="unhandled-exception-type">Unhandled exception type</h3>
<p>原因：被强制异常处理的代码块，必须进行异常处理，否则编译器会提示“Unhandled exception type Exception”错误警告。</p>
<p>需要将代码写到 try catch 里！</p>
<h2 id="mysql">MySQL</h2>
<h3 id="数据库乱码前端乱码">数据库乱码，前端乱码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/mysql/my.cnf
</span></span><span class="line"><span class="cl"><span class="c1"># 填写如下配置</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>client<span class="o">]</span>
</span></span><span class="line"><span class="cl">default-character-set<span class="o">=</span>utf8mb4
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>mysqld<span class="o">]</span>
</span></span><span class="line"><span class="cl">character-set-server <span class="o">=</span> utf8mb4
</span></span><span class="line"><span class="cl">collation-server <span class="o">=</span> utf8mb4_unicode_ci
</span></span><span class="line"><span class="cl"><span class="nv">init_connect</span><span class="o">=</span><span class="s1">&#39;SET NAMES utf8mb4&#39;</span>
</span></span><span class="line"><span class="cl">skip-character-set-client-handshake <span class="o">=</span> <span class="nb">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>mysql<span class="o">]</span>
</span></span><span class="line"><span class="cl">default-character-set <span class="o">=</span> utf8mb4
</span></span></code></pre></div><h3 id="nested-exception-is-javalangnullpointerexception-with-root-cause">nested exception is java.lang.NullPointerException] with root cause</h3>
<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/11-21-20-55f1e2de81e6858f99707dea6a5e0292-20230216112119-174d50.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/11-21-20-55f1e2de81e6858f99707dea6a5e0292-20230216112119-174d50.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="error-attempting-to-get-column-motion_id-from-result-set">Error attempting to get column &lsquo;motion_id&rsquo; from result set</h3>
<p>数据库字段类型与后端类型不一致</p>
<h2 id="前端">前端</h2>
<h3 id="digital-envelope-routinesunsupported">digital envelope routines::unsupported</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"> <span class="nb">export</span> <span class="nv">NODE_OPTIONS</span><span class="o">=</span>--openssl-legacy-provider
</span></span></code></pre></div><h3 id="禁止跨域策略-cors-policy">禁止跨域策略 (CORS policy)</h3>
<h3 id="node-openssl-legacy-provider-is-not-allowed-in-node_options">node: &ndash;openssl-legacy-provider is not allowed in NODE_OPTIONS</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">unset</span><span class="w"> </span><span class="n">NODE_OPTIONS</span><span class="w">
</span></span></span></code></pre></div><h3 id="unexpected-character-">Unexpected character (&rsquo;}&rsquo;</h3>
<p>请求的时候最后一个字段后面不要加逗号</p>
<h3 id="the-value-of-the-access-control-allow-origin-header-in-the-response-must-not-be-the-wildcard--when-the-requests-credentials-mode-is-include-the-credentials-mode-of-requests-initiated-by-the-xmlhttprequest-is-controlled-by-the-withcredentials-attribute">The value of the &lsquo;Access-Control-Allow-Origin&rsquo; header in the response must not be the wildcard &lsquo;*&rsquo; when the request&rsquo;s credentials mode is &lsquo;include&rsquo;. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute</h3>
<p>这个错误通常是因为在使用 XMLHttpRequest 对象进行跨域请求时，服务器返回的响应头中的 Access-Control-Allow-Origin 的值为*，但请求的 withCredentials 属性被设置为 true，这两者之间是相互冲突的。</p>
<p>XMLHttpRequest 对象具有 withCredentials 属性，如果设置为 true，它将在请求中包括来自其他域的 cookie 等凭据信息。但是，如果服务器在响应头中将 Access-Control-Allow-Origin 设置为*，浏览器会禁止访问这些凭据信息。这是一项安全保护措施，防止敏感信息泄露。</p>
<p>解决这个问题的方法是，在服务器端，将 Access-Control-Allow-Origin 设置为请求来源的域名，而不是使用通配符*。这可以让浏览器安全地发送凭据信息。</p>
<p>在前端，需要将 withCredentials 属性设置为 true，以便在请求中包含凭据信息。同时，需要确保请求的来源域名与服务器端设置的 Access-Control-Allow-Origin 一致。</p>
<p>如果你无法更改服务器端的设置，可以考虑使用代理或者 JSONP 等跨域解决方案。</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//2023/02/22/3c3171236ca43b99771480eeea4a6f2f.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2023/02/22/3c3171236ca43b99771480eeea4a6f2f.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="vue-项目端口不固定">VUE 项目端口不固定</h3>
<h3 id="application-run-failed-orgspringframeworkbeansfactorybeancreationexception-error-creating-bean-with-name-communityinfocontroller">Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name &lsquo;communityInfoController&rsquo;</h3>
<p>检查target/classes/mapper/DepartmentMapper.xml中的格式是否正确，检查引号是否多了，少了</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="w">    </span><span class="o">&lt;</span><span class="n">select</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="s">&#34;findIdByOrgId&#34;</span><span class="w"> </span><span class="n">resultType</span><span class="o">=</span><span class="s">&#34;resultType=&#34;</span><span class="n">java</span><span class="p">.</span><span class="na">lang</span><span class="p">.</span><span class="na">Integer</span><span class="s">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">    SELECT CAST(id AS UNSIGNED) AS id FROM department WHERE organization_id = #{orgId}
</span></span></span><span class="line"><span class="cl"><span class="s">    &lt;/select&gt;
</span></span></span></code></pre></div><h3 id="错误码">错误码</h3>
<ul>
<li>400-前后端参数对不上</li>
</ul>
<h3 id="postget-there-is-already-xx-bean-method">{POST/GET} there is already xx bean method</h3>
<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/19-15-47-0a06b9a67f496f6ec023534fc4381876-20230217191545-f22ccc.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/19-15-47-0a06b9a67f496f6ec023534fc4381876-20230217191545-f22ccc.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="npm-启动digital-envelope-routinesunsupported">NPM 启动:digital envelope routines::unsupported</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">NODE_OPTIONS</span><span class="o">=</span>--openssl-legacy-provider
</span></span><span class="line"><span class="cl">npm run serve
</span></span></code></pre></div><h3 id="the-field-file-exceeds-its-maximum-permitted-size-of-1048576-bytes">The field file exceeds its maximum permitted size of 1048576 bytes</h3>
<p>spring boot 上传文件时接口报错 The field file exceeds its maximum permitted size of 1048576 bytes.经排查官方设置每个文件的配置最大为 1Mb，单次请求的文件的总数不能大于 10Mb，上传大于 1Mb 的文件需要修改配置文件（application.properties）
1.Spring Boot 1.3.x 或者之前</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">multipart.maxFileSize=100Mb
</span></span><span class="line"><span class="cl">multipart.maxRequestSize=1000Mb
</span></span></code></pre></div><p>2.Spring Boot 1.4.x 以后</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">spring.http.multipart.maxFileSize=100Mb
</span></span><span class="line"><span class="cl">spring.http.multipart.maxRequestSize=1000Mb
</span></span></code></pre></div><p>3.Spring Boot 2.0 之后</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">spring.servlet.multipart.max-file-size=100MB
</span></span><span class="line"><span class="cl">spring.servlet.multipart.max-request-size=1000MB
</span></span></code></pre></div><h3 id="字段不存在">字段不存在</h3>
<p>请求的时候字段名字和 java 中命名保持一直，而不是和数据库名字保持一样</p>
<h3 id="数据库-communications-link-failure">数据库 communications link failure</h3>
<h3 id="配置请求超时时间">配置请求超时时间</h3>
<p>src/utils/request.js</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/14-18-57-4ffa9b2c1ef5817aa81ba91b0257cddc-20230214141856-8b5215.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/14-18-57-4ffa9b2c1ef5817aa81ba91b0257cddc-20230214141856-8b5215.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="request-method-get-not-supported">Request method GET not supported</h3>
<p>前端请求事件没有设置请求方式  post 还是 get</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/10-27-11-20135e8d24b909084fc2d36e3ca3d469-20230208102710-382029.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/10-27-11-20135e8d24b909084fc2d36e3ca3d469-20230208102710-382029.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="invalid-cros-request">Invalid cros request</h3>
<p>跨域</p>
<h3 id="处理未来数据">处理未来数据</h3>
<p><a href="https://www.bilibili.com/video/BV1U44y1W77D?t=1655.5&amp;p=23">https://www.bilibili.com/video/BV1U44y1W77D?t=1655.5&amp;p=23</a></p>
<h1 id="tips">TIPS</h1>
<h3 id="前端保存代码需要等待一段时间生效">前端保存代码需要等待一段时间生效</h3>
<h3 id="获取用户-ip">获取用户 IP</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">String</span><span class="w"> </span><span class="n">userIp</span><span class="w"> </span><span class="o">=</span><span class="w">  </span><span class="n">request</span><span class="p">.</span><span class="na">getRemoteAddr</span><span class="p">();</span><span class="w">
</span></span></span></code></pre></div><h3 id="解决方案java-实体类字段-不返回给前端">解决方案：Java 实体类字段 不返回给前端</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@JsonIgnore</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@ApiModelProperty</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;不重要&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@TableField</span><span class="p">(</span><span class="n">exist</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">unimportant</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h3 id="dateutil-包">Dateutil 包</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.text.SimpleDateFormat</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.Calendar</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.Date</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h3 id="获取请求参数">获取请求参数</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">preHandle</span><span class="p">(</span><span class="n">HttpServletRequest</span><span class="w"> </span><span class="n">request</span><span class="p">,</span><span class="w"> </span><span class="n">HttpServletResponse</span><span class="w"> </span><span class="n">response</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="w"> </span><span class="n">handler</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">//获取请求参数</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">String</span><span class="w"> </span><span class="n">queryString</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">request</span><span class="p">.</span><span class="na">getQueryString</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">log</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">&#34;请求参数:{}&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">queryString</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="后端设置-header-前端获取不到">后端设置 header 前端获取不到</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="err">#</span><span class="w"> </span><span class="n">必须要加这条字段控制能够获取的</span><span class="w"> </span><span class="n">header</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">response</span><span class="p">.</span><span class="na">addHeader</span><span class="p">(</span><span class="s">&#34;Access-Control-Expose-Headers&#34;</span><span class="p">,</span><span class="s">&#34;test&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="n">response</span><span class="p">.</span><span class="na">addHeader</span><span class="p">(</span><span class="s">&#34;test&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;sdfdsfdsf&#34;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="axios-请求">axios 请求</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">axios</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="err">&#39;</span><span class="n">http</span><span class="p">:</span><span class="c1">//opm.eswincomputing.com:9090/user/page&#39;)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">.</span><span class="na">then</span><span class="p">(</span><span class="n">function</span><span class="w"> </span><span class="p">(</span><span class="n">response</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="na">headers</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">.</span><span class="na">catch</span><span class="p">(</span><span class="n">function</span><span class="w"> </span><span class="p">(</span><span class="n">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="n">error</span><span class="p">.</span><span class="na">headers</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">});</span><span class="w">
</span></span></span></code></pre></div><h3 id="查询端口占用">查询端口占用</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">lsof</span><span class="w"> </span><span class="o">-</span><span class="n">i</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">lsof</span><span class="w"> </span><span class="o">-</span><span class="n">i</span><span class="p">:</span><span class="n">8080</span><span class="err">：</span><span class="n">查看</span><span class="w"> </span><span class="n">8080</span><span class="w"> </span><span class="n">端口占用</span><span class="w">
</span></span></span></code></pre></div><h3 id="打印输出">打印输出</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="err">#</span><span class="w"> </span><span class="n">Application</span><span class="p">.</span><span class="na">yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nl">logging</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">level</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">com</span><span class="p">.</span><span class="na">eswincomputing</span><span class="p">.</span><span class="na">springboot</span><span class="p">:</span><span class="w"> </span><span class="n">debug</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="err">#</span><span class="w"> </span><span class="n">使用</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Slf4j</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@RestController</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@RequestMapping</span><span class="p">(</span><span class="s">&#34;/patch-record&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">PatchRecordController</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">IPatchRecordService</span><span class="w"> </span><span class="n">patchRecordServic</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">log</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="n">version</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h3 id="校验字符串是否为空">校验字符串是否为空</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">StrUtil.isBlank<span class="o">()</span>
</span></span></code></pre></div><h3 id="查询主键">查询主键</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">SELECT id FROM department WHERE <span class="nv">organization_id</span> <span class="o">=</span> <span class="c1">#{orgId}</span>
</span></span></code></pre></div><h3 id="字符整型互转">字符整型互转</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">Integer.parseInt<span class="o">(</span>user.getDelFlag<span class="o">())</span>
</span></span></code></pre></div><h3 id="解析-json-字符串">解析 json 字符串</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="w">       
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">&lt;!--</span><span class="w"> </span><span class="n">json</span><span class="w"> </span><span class="n">解析</span><span class="w"> </span><span class="o">--&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">&lt;</span><span class="n">dependency</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">&lt;</span><span class="n">groupId</span><span class="o">&gt;</span><span class="n">com</span><span class="p">.</span><span class="na">alibaba</span><span class="o">&lt;/</span><span class="n">groupId</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">&lt;</span><span class="n">artifactId</span><span class="o">&gt;</span><span class="n">fastjson</span><span class="o">&lt;/</span><span class="n">artifactId</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">&lt;</span><span class="n">version</span><span class="o">&gt;</span><span class="n">2</span><span class="p">.</span><span class="na">0</span><span class="p">.</span><span class="na">21</span><span class="o">&lt;/</span><span class="n">version</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="o">&lt;/</span><span class="n">dependency</span><span class="o">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="n">JSONObject</span><span class="w"> </span><span class="n">object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">JSONObject</span><span class="p">.</span><span class="na">parseObject</span><span class="p">(</span><span class="n">patchRecord</span><span class="p">.</span><span class="na">getAttachmentList</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">String</span><span class="w"> </span><span class="n">fileUID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">object</span><span class="p">.</span><span class="na">getString</span><span class="p">(</span><span class="s">&#34;file_uid&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">String</span><span class="w"> </span><span class="n">fileName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">object</span><span class="p">.</span><span class="na">getString</span><span class="p">(</span><span class="s">&#34;file_name&#34;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><h3 id="逗号分割字符串">逗号分割字符串</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;张三，李四，王五，马六，小气&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">String</span><span class="w"> </span><span class="n">substring</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">string</span><span class="p">.</span><span class="na">substring</span><span class="p">(</span><span class="n">0</span><span class="p">,</span><span class="w"> </span><span class="n">string</span><span class="p">.</span><span class="na">length</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">substring</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">substring</span><span class="p">.</span><span class="na">split</span><span class="p">(</span><span class="s">&#34;,&#34;</span><span class="p">);</span><span class="c1">//以逗号分割</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">string2</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">split</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;数据--&gt;&gt;&gt;&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">string2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>如何使用 Gitlab CI Pipeline</title>
      <link>https://lifeislife.cn/posts/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8gitlab-ci-pipeline/</link>
      <pubDate>Sat, 07 Jan 2023 11:08:19 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8gitlab-ci-pipeline/</guid>
      <description>&lt;p&gt;GitLab CI/CD 是一个强大的工具，可以帮助开发团队实现自动化构建、测试和部署。本文将介绍如何使用 GitLab CI/CD 的 Pipeline 功能，以实现将 Markdown 文件自动编译为 PDF 并上传至 GitLab Release 界面的功能。&lt;/p&gt;
&lt;h2 id=&#34;准备工作&#34;&gt;准备工作&lt;/h2&gt;
&lt;p&gt;在开始使用 GitLab CI/CD 的 Pipeline 功能之前，需要进行一些准备工作。具体步骤如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建 GitLab 项目：在 GitLab 上创建一个新项目，并将 Markdown 文件上传至项目的某个目录下。例如，我们将 Markdown 文件上传至项目的根目录下，并命名为 &lt;code&gt;example.md&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;安装 Pandoc：Pandoc 是一个用于文档转换的工具，我们将使用它将 Markdown 文件转换为 PDF。在安装 Pandoc 之前，需要先安装 LaTeX，因为 Pandoc 使用 LaTeX 进行 PDF 渲染。具体安装步骤请参考 Pandoc 和 LaTeX 的官方文档。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;创建 Release：在 GitLab 上创建一个 Release，用于存储编译好的 PDF 文件。具体操作方法请参考 GitLab 的官方文档。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;创建 CI/CD 配置文件：在项目根目录下创建一个&lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 文件，并在其中定义 Pipeline 的流程。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;编写-cicd-配置文件&#34;&gt;编写 CI/CD 配置文件&lt;/h2&gt;
&lt;p&gt;下面是一个样例的&lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 文件，用于实现将 Markdown 文件编译为 PDF 并上传至 GitLab Release 界面的功能。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pandoc/core:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;stages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;pdf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;stage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;pandoc example.md -o example.pdf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;curl --header &amp;#34;PRIVATE-TOKEN: ${CI_PRIVATE_TOKEN}&amp;#34; --upload-file example.pdf &amp;#34;${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/uploads&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;artifacts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;paths&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;example.pdf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述配置文件中，我们使用了 &lt;code&gt;pandoc/core:latest&lt;/code&gt; 作为 Docker 镜像，该镜像已经预安装了 Pandoc 工具。&lt;/p&gt;
&lt;p&gt;该文件中包含了一个 build 阶段，其中包含了一个 pdf 任务。在 pdf 任务中，我们使用 Pandoc 工具将 Markdown 文件转换为 PDF 文件，并使用 cURL 工具将编译好的 PDF 文件上传至 GitLab Release 界面。注意，我们使用了环境变量&lt;code&gt;${CI_PRIVATE_TOKEN}&lt;/code&gt;和$&lt;code&gt;{CI_API_V4_URL}&lt;/code&gt;，这些变量是 GitLab 自动注入的，用于进行身份验证和上传文件。&lt;/p&gt;
&lt;p&gt;最后，我们将编译好的 PDF 文件定义为 Pipeline 的 artifacts，这样可以确保文件能够被保留并可用于后续的部署。&lt;/p&gt;
&lt;h2 id=&#34;运行-pipeline&#34;&gt;运行 Pipeline&lt;/h2&gt;
&lt;p&gt;完成 CI/CD 配置文件的编写后，我们可以在 GitLab 上启动 Pipeline，将 Markdown 文件自动编译为 PDF 并上传至 GitLab Release 界面。具体步骤如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;提交代码：将&lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 文件提交到 GitLab&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;启动 Pipeline：在 GitLab 上打开项目，并点击“CI/CD”-&amp;gt;“Pipelines”选项卡。点击“Run Pipeline”按钮，启动 Pipeline 流程。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;等待 Pipeline 完成：在 Pipeline 启动后，GitLab 会自动创建一个 Runner 并分配任务。Pipeline 的状态会在页面上实时更新，直到 Pipeline 执行完成。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看 Release：Pipeline 执行完成后，我们可以在 GitLab Release 界面中找到编译好的 PDF 文件。点击 PDF 文件链接，即可下载并查看编译好的 PDF 文件。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<p>GitLab CI/CD 是一个强大的工具，可以帮助开发团队实现自动化构建、测试和部署。本文将介绍如何使用 GitLab CI/CD 的 Pipeline 功能，以实现将 Markdown 文件自动编译为 PDF 并上传至 GitLab Release 界面的功能。</p>
<h2 id="准备工作">准备工作</h2>
<p>在开始使用 GitLab CI/CD 的 Pipeline 功能之前，需要进行一些准备工作。具体步骤如下：</p>
<ol>
<li>创建 GitLab 项目：在 GitLab 上创建一个新项目，并将 Markdown 文件上传至项目的某个目录下。例如，我们将 Markdown 文件上传至项目的根目录下，并命名为 <code>example.md</code>。</li>
</ol>
<ul>
<li>
<p>安装 Pandoc：Pandoc 是一个用于文档转换的工具，我们将使用它将 Markdown 文件转换为 PDF。在安装 Pandoc 之前，需要先安装 LaTeX，因为 Pandoc 使用 LaTeX 进行 PDF 渲染。具体安装步骤请参考 Pandoc 和 LaTeX 的官方文档。</p>
</li>
<li>
<p>创建 Release：在 GitLab 上创建一个 Release，用于存储编译好的 PDF 文件。具体操作方法请参考 GitLab 的官方文档。</p>
</li>
<li>
<p>创建 CI/CD 配置文件：在项目根目录下创建一个<code>.gitlab-ci.yml</code> 文件，并在其中定义 Pipeline 的流程。</p>
</li>
</ul>
<h2 id="编写-cicd-配置文件">编写 CI/CD 配置文件</h2>
<p>下面是一个样例的<code>.gitlab-ci.yml</code> 文件，用于实现将 Markdown 文件编译为 PDF 并上传至 GitLab Release 界面的功能。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">pandoc/core:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">stages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">pdf</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">pandoc example.md -o example.pdf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">curl --header &#34;PRIVATE-TOKEN: ${CI_PRIVATE_TOKEN}&#34; --upload-file example.pdf &#34;${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/uploads&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">artifacts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">example.pdf</span><span class="w">
</span></span></span></code></pre></div><p>上述配置文件中，我们使用了 <code>pandoc/core:latest</code> 作为 Docker 镜像，该镜像已经预安装了 Pandoc 工具。</p>
<p>该文件中包含了一个 build 阶段，其中包含了一个 pdf 任务。在 pdf 任务中，我们使用 Pandoc 工具将 Markdown 文件转换为 PDF 文件，并使用 cURL 工具将编译好的 PDF 文件上传至 GitLab Release 界面。注意，我们使用了环境变量<code>${CI_PRIVATE_TOKEN}</code>和$<code>{CI_API_V4_URL}</code>，这些变量是 GitLab 自动注入的，用于进行身份验证和上传文件。</p>
<p>最后，我们将编译好的 PDF 文件定义为 Pipeline 的 artifacts，这样可以确保文件能够被保留并可用于后续的部署。</p>
<h2 id="运行-pipeline">运行 Pipeline</h2>
<p>完成 CI/CD 配置文件的编写后，我们可以在 GitLab 上启动 Pipeline，将 Markdown 文件自动编译为 PDF 并上传至 GitLab Release 界面。具体步骤如下：</p>
<ul>
<li>
<p>提交代码：将<code>.gitlab-ci.yml</code> 文件提交到 GitLab</p>
</li>
<li>
<p>启动 Pipeline：在 GitLab 上打开项目，并点击“CI/CD”-&gt;“Pipelines”选项卡。点击“Run Pipeline”按钮，启动 Pipeline 流程。</p>
</li>
<li>
<p>等待 Pipeline 完成：在 Pipeline 启动后，GitLab 会自动创建一个 Runner 并分配任务。Pipeline 的状态会在页面上实时更新，直到 Pipeline 执行完成。</p>
</li>
<li>
<p>查看 Release：Pipeline 执行完成后，我们可以在 GitLab Release 界面中找到编译好的 PDF 文件。点击 PDF 文件链接，即可下载并查看编译好的 PDF 文件。</p>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Markdown 嵌入 Draw.io</title>
      <link>https://lifeislife.cn/posts/markdown%E5%B5%8C%E5%85%A5draw-io/</link>
      <pubDate>Sat, 07 Jan 2023 10:42:06 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/markdown%E5%B5%8C%E5%85%A5draw-io/</guid>
      <description>&lt;p&gt;Markdown 是支持嵌入 HTML 的，大部分阅读器也都支持解析。Draw.io 可以导出为 HTML 格式。&lt;/p&gt;
&lt;p&gt;文件—导出为 HTML—导出—新窗口打开—复制 HTML 代码—只保留&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;标签之间的内容，不包含&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;和&lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;mxgraph&#34; style=&#34;max-width:100%;border:1px solid transparent;&#34; data-mxgraph=&#34;{&amp;quot;highlight&amp;quot;:&amp;quot;#0000ff&amp;quot;,&amp;quot;nav&amp;quot;:true,&amp;quot;resize&amp;quot;:true,&amp;quot;toolbar&amp;quot;:&amp;quot;zoom layers tags lightbox&amp;quot;,&amp;quot;edit&amp;quot;:&amp;quot;_blank&amp;quot;,&amp;quot;xml&amp;quot;:&amp;quot;&amp;lt;mxfile host=\&amp;quot;app.diagrams.net\&amp;quot; modified=\&amp;quot;2023-01-07T02:39:36.940Z\&amp;quot; agent=\&amp;quot;5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36\&amp;quot; etag=\&amp;quot;bw-obfhzFCRdb4Hl-GTj\&amp;quot; version=\&amp;quot;20.7.4\&amp;quot; type=\&amp;quot;device\&amp;quot;&amp;gt;&amp;lt;diagram id=\&amp;quot;qJU06MX9joVzOyRxsnLc\&amp;quot; name=\&amp;quot;第 1 页\&amp;quot;&amp;gt;7VvbcqM4EP0aHkMBErdH23F2pjapTW2m9vKUIiCDJhh5QTj2fP0KEGAQJk5iDNkdv9g0DUjnSEfdaiyBxXr3S+xsgjvioVDSFG8ngWtJ01TN0NhXZtkXFsO0CoMfY4871YYH/ANxo8KtKfZQ0nCkhIQUb5pGl0QRcmnD5sQxeWm6rUjYfOrG8ZFgeHCdULT+iT0aFFZLM2v7F4T9oHyyatjFmbVTOvOeJIHjkZcDE1hKYBETQotf690ChRl4JS7FdTdHzlYNi1FET7ng7jcPBNY/FHz7vt4u5ks0/3Z7pXI2tk6Y8h7z1tJ9CYEfk3QjgfmKRPSBm1V2nNCYPFeosP7MxSbxVm5RTNGuizDnqXxMjQkbTIisEY33zI9fZenFZXwYVePjpSbF0A1Z427BASfA4r4OHwt+dfcaLvaDI/YW9LTX0WPgRR7K7qIwhF4CTNHDxnGzsy9syjBbQNchh3SFw3BBQhKz44hE6J2o91Pd5kLEfFxQgQjq0pRsKM0saWlIliXNzcwys6X5TFrakm1KcygAzzpJm+gW0LXxFSF3QuxH7NBlgCJmn2eQYaYJM35ijT0ve0wnnU3Cx6CPzxGgd8wRpYPKoZiEpsDkgsHh4AjFV+JMSZ4RdQOOm5NsCjlf4V0G5iGPG4IjmjdWn0v6dRdjeJ3regF/saioWm2/xmufdSnET1nHEtdB7PuPu8dbHKU7Odn6IzHHz0KzpXaGyKTewSQ0hpqTApHXxH1mSGvK78jHSfYMMKtsX9KnybFbjbxH3mKMkg8wfWxhO8opMO0Gpx2UGrZsmiKrxlDzE4iiiTwW/fBDEtOA+CRywmVtbclb7XNLyIbj9x1Ruue8OCklXSp8iG5CnZjOsmCt1uDcdoOzDuUPQjtM/+J3yH7/ndllnR9d7w7crvcHB/coxgytbNjktqNMJiSNXdSDFp+SrF0+6pvufA5mSPaOixiFDsXbZpx5fo5FDX5KMQuCu5i/dZ5Y+N5g6/S1MEYJ/sHDOaVzHp+ol+Ww/HjQqMiqafFw4mS4+d3us+bXtypDPT57r6DevANZrRI2LNp0VW16P4P6Ee1lgQsSWCzZyZm8JwmmmGQsPRFKybpJbZtJmk1gkfBXoyaJRRDKfHmTT+XA2WRNWe/8LB+UnR9pjGQ3JKn3mKB4i12UPDIWV9hPYydr3GPeDz5cULzcomLUqJcSZk2FstFkV4OiOENT7giD9aG0uZKlC2vzgRK7oZMk2P0W4KhDkEUummy9W2fPrp/dE7pKYDjndovJQuf5RTWZDBxnf+DGVe64bhjNxwCeyN6c6G8rve6g3E7pdGc/ivaeVZCAGA1u0iRg99qk2dWTXVjOtxtxpciKbalN0fjYOjP8QgJHkpSPhHuKnMF8GPJBxb5g0GecGPTZg4jWW8VGK4OdSmz61aPtD/VX/IHV5z+M3EBxky1OownLDDynzECr7P++cafpyowh0JXv0emSvZCWusSGnHWT7ePZhmSZ0hJK87k0ZxfdfM2z9+nuG3yNmEpF7mW3DTQVyGUOV87qE3eDBotN/3fbem/f7LGsNmvQHHkPz+5hTYwMRmcNxzR1wjvHZekHoyfyyMvYE290CsuiV6s0MlOl2eJASVtUvq0CdQlsW8U8owNXQzcuqGiqmNL8iuIIidnM9MFUxgYTgs7gbNJ5hiorKmjkGcCEF8wzVL7n+WqiwRVp5ESjUsIyJOHFjWOJQ9sfwv5EA6qgz3+gRKPcd/40iUZZKT9LoqErrS3uyW9oqCJhP1ONswato6caQP+Ea4k0VolSPbVGWc6cqRQpxbfQJl+kLMty5yhS6mozhvvgHk/ZnOZNr9ppyYDC/LNmObRU222pnkDNEn4msY5Yj4sKg66apaGQbEWzSkOt2vnR/vCordsHuQRo5RIXXQROTSXgMLmEoEOGCmWoKdWnWTYDVh571p/WixVFf4U6rPAUnUUvZjOChYYlQxbCVB/YvPWZSrwGbCdDVm9L2/6vJUO6ZfT5D5QMidv4E0+GzrYg51UX1TjPGlzOxlKlW9o73Bpc/kNhUmLc/0rJxzXvP/LSCNBaivJKHbftf5G3QDRBID7JWyBl+eoMkTtgy0sD+RGrs+yw/jNR4V7/JQss/wU=&amp;lt;/diagram&amp;gt;&amp;lt;/mxfile&amp;gt;&amp;quot;}&#34;&gt;&lt;/div&gt;
&lt;script type=&#34;text/javascript&#34; src=&#34;https://viewer.diagrams.net/js/viewer-static.min.js&#34;&gt;&lt;/script&gt;
</description>
      <content:encoded><![CDATA[<p>Markdown 是支持嵌入 HTML 的，大部分阅读器也都支持解析。Draw.io 可以导出为 HTML 格式。</p>
<p>文件—导出为 HTML—导出—新窗口打开—复制 HTML 代码—只保留<code>&lt;body&gt;</code>标签之间的内容，不包含<code>&lt;body&gt;</code>和<code>&lt;/body&gt;</code>。</p>
<div class="mxgraph" style="max-width:100%;border:1px solid transparent;" data-mxgraph="{&quot;highlight&quot;:&quot;#0000ff&quot;,&quot;nav&quot;:true,&quot;resize&quot;:true,&quot;toolbar&quot;:&quot;zoom layers tags lightbox&quot;,&quot;edit&quot;:&quot;_blank&quot;,&quot;xml&quot;:&quot;&lt;mxfile host=\&quot;app.diagrams.net\&quot; modified=\&quot;2023-01-07T02:39:36.940Z\&quot; agent=\&quot;5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36\&quot; etag=\&quot;bw-obfhzFCRdb4Hl-GTj\&quot; version=\&quot;20.7.4\&quot; type=\&quot;device\&quot;&gt;&lt;diagram id=\&quot;qJU06MX9joVzOyRxsnLc\&quot; name=\&quot;第 1 页\&quot;&gt;7VvbcqM4EP0aHkMBErdH23F2pjapTW2m9vKUIiCDJhh5QTj2fP0KEGAQJk5iDNkdv9g0DUjnSEfdaiyBxXr3S+xsgjvioVDSFG8ngWtJ01TN0NhXZtkXFsO0CoMfY4871YYH/ANxo8KtKfZQ0nCkhIQUb5pGl0QRcmnD5sQxeWm6rUjYfOrG8ZFgeHCdULT+iT0aFFZLM2v7F4T9oHyyatjFmbVTOvOeJIHjkZcDE1hKYBETQotf690ChRl4JS7FdTdHzlYNi1FET7ng7jcPBNY/FHz7vt4u5ks0/3Z7pXI2tk6Y8h7z1tJ9CYEfk3QjgfmKRPSBm1V2nNCYPFeosP7MxSbxVm5RTNGuizDnqXxMjQkbTIisEY33zI9fZenFZXwYVePjpSbF0A1Z427BASfA4r4OHwt+dfcaLvaDI/YW9LTX0WPgRR7K7qIwhF4CTNHDxnGzsy9syjBbQNchh3SFw3BBQhKz44hE6J2o91Pd5kLEfFxQgQjq0pRsKM0saWlIliXNzcwys6X5TFrakm1KcygAzzpJm+gW0LXxFSF3QuxH7NBlgCJmn2eQYaYJM35ijT0ve0wnnU3Cx6CPzxGgd8wRpYPKoZiEpsDkgsHh4AjFV+JMSZ4RdQOOm5NsCjlf4V0G5iGPG4IjmjdWn0v6dRdjeJ3regF/saioWm2/xmufdSnET1nHEtdB7PuPu8dbHKU7Odn6IzHHz0KzpXaGyKTewSQ0hpqTApHXxH1mSGvK78jHSfYMMKtsX9KnybFbjbxH3mKMkg8wfWxhO8opMO0Gpx2UGrZsmiKrxlDzE4iiiTwW/fBDEtOA+CRywmVtbclb7XNLyIbj9x1Ruue8OCklXSp8iG5CnZjOsmCt1uDcdoOzDuUPQjtM/+J3yH7/ndllnR9d7w7crvcHB/coxgytbNjktqNMJiSNXdSDFp+SrF0+6pvufA5mSPaOixiFDsXbZpx5fo5FDX5KMQuCu5i/dZ5Y+N5g6/S1MEYJ/sHDOaVzHp+ol+Ww/HjQqMiqafFw4mS4+d3us+bXtypDPT57r6DevANZrRI2LNp0VW16P4P6Ee1lgQsSWCzZyZm8JwmmmGQsPRFKybpJbZtJmk1gkfBXoyaJRRDKfHmTT+XA2WRNWe/8LB+UnR9pjGQ3JKn3mKB4i12UPDIWV9hPYydr3GPeDz5cULzcomLUqJcSZk2FstFkV4OiOENT7giD9aG0uZKlC2vzgRK7oZMk2P0W4KhDkEUummy9W2fPrp/dE7pKYDjndovJQuf5RTWZDBxnf+DGVe64bhjNxwCeyN6c6G8rve6g3E7pdGc/ivaeVZCAGA1u0iRg99qk2dWTXVjOtxtxpciKbalN0fjYOjP8QgJHkpSPhHuKnMF8GPJBxb5g0GecGPTZg4jWW8VGK4OdSmz61aPtD/VX/IHV5z+M3EBxky1OownLDDynzECr7P++cafpyowh0JXv0emSvZCWusSGnHWT7ePZhmSZ0hJK87k0ZxfdfM2z9+nuG3yNmEpF7mW3DTQVyGUOV87qE3eDBotN/3fbem/f7LGsNmvQHHkPz+5hTYwMRmcNxzR1wjvHZekHoyfyyMvYE290CsuiV6s0MlOl2eJASVtUvq0CdQlsW8U8owNXQzcuqGiqmNL8iuIIidnM9MFUxgYTgs7gbNJ5hiorKmjkGcCEF8wzVL7n+WqiwRVp5ESjUsIyJOHFjWOJQ9sfwv5EA6qgz3+gRKPcd/40iUZZKT9LoqErrS3uyW9oqCJhP1ONswato6caQP+Ea4k0VolSPbVGWc6cqRQpxbfQJl+kLMty5yhS6mozhvvgHk/ZnOZNr9ppyYDC/LNmObRU222pnkDNEn4msY5Yj4sKg66apaGQbEWzSkOt2vnR/vCordsHuQRo5RIXXQROTSXgMLmEoEOGCmWoKdWnWTYDVh571p/WixVFf4U6rPAUnUUvZjOChYYlQxbCVB/YvPWZSrwGbCdDVm9L2/6vJUO6ZfT5D5QMidv4E0+GzrYg51UX1TjPGlzOxlKlW9o73Bpc/kNhUmLc/0rJxzXvP/LSCNBaivJKHbftf5G3QDRBID7JWyBl+eoMkTtgy0sD+RGrs+yw/jNR4V7/JQss/wU=&lt;/diagram&gt;&lt;/mxfile&gt;&quot;}"></div>
<script type="text/javascript" src="https://viewer.diagrams.net/js/viewer-static.min.js"></script>
]]></content:encoded>
    </item>
    <item>
      <title>Ubuntu 18.04 安装Clang/LLVM 11</title>
      <link>https://lifeislife.cn/posts/ubuntu-18-04-%E5%AE%89%E8%A3%85clang-llvm-11/</link>
      <pubDate>Sat, 24 Dec 2022 15:53:22 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/ubuntu-18-04-%E5%AE%89%E8%A3%85clang-llvm-11/</guid>
      <description>&lt;h2 id=&#34;从-apt-安装&#34;&gt;从 APT 安装&lt;/h2&gt;
&lt;p&gt;Install the GPG Key for &lt;code&gt;https://apt.llvm.org/&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo apt-key add -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add the repo for Clang 11 &lt;code&gt;stable-old&lt;/code&gt; for Ubuntu 18.04 Bionic&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee -a /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install practically everything (except &lt;code&gt;python-clang-11&lt;/code&gt; which for some reason doesn&amp;rsquo;t work)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install libllvm-11-ocaml-dev libllvm11 llvm-11 llvm-11-dev llvm-11-doc llvm-11-examples llvm-11-runtime &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;clang-11 clang-tools-11 clang-11-doc libclang-common-11-dev libclang-11-dev libclang1-11 clang-format-11 clangd-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;libfuzzer-11-dev lldb-11 lld-11 libc++-11-dev libc++abi-11-dev libomp-11-dev -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make Clang 11 and everything related to it defaults&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo update-alternatives &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --install /usr/lib/llvm              llvm             /usr/lib/llvm-11         &lt;span class=&#34;m&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-config       llvm-config      /usr/bin/llvm-config-11  &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-ar           llvm-ar          /usr/bin/llvm-ar-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-as           llvm-as          /usr/bin/llvm-as-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-bcanalyzer   llvm-bcanalyzer  /usr/bin/llvm-bcanalyzer-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-cov          llvm-cov         /usr/bin/llvm-cov-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-diff         llvm-diff        /usr/bin/llvm-diff-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-dis          llvm-dis         /usr/bin/llvm-dis-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-dwarfdump    llvm-dwarfdump   /usr/bin/llvm-dwarfdump-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-extract      llvm-extract     /usr/bin/llvm-extract-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-link         llvm-link        /usr/bin/llvm-link-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-mc           llvm-mc          /usr/bin/llvm-mc-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-mcmarkup     llvm-mcmarkup    /usr/bin/llvm-mcmarkup-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-nm           llvm-nm          /usr/bin/llvm-nm-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-objdump      llvm-objdump     /usr/bin/llvm-objdump-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-ranlib       llvm-ranlib      /usr/bin/llvm-ranlib-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-readobj      llvm-readobj     /usr/bin/llvm-readobj-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-rtdyld       llvm-rtdyld      /usr/bin/llvm-rtdyld-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-size         llvm-size        /usr/bin/llvm-size-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-stress       llvm-stress      /usr/bin/llvm-stress-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-symbolizer   llvm-symbolizer  /usr/bin/llvm-symbolizer-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/llvm-tblgen       llvm-tblgen      /usr/bin/llvm-tblgen-11
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo update-alternatives &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --install /usr/bin/clang                 clang                  /usr/bin/clang-11    &lt;span class=&#34;m&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang++               clang++                /usr/bin/clang++-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/lld                   lld                    /usr/bin/lld-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-format          clang-format           /usr/bin/clang-format-11  &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-tidy            clang-tidy             /usr/bin/clang-tidy-11  &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-tidy-diff.py    clang-tidy-diff.py     /usr/bin/clang-tidy-diff-11.py &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-include-fixer   clang-include-fixer    /usr/bin/clang-include-fixer-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-offload-bundler clang-offload-bundler  /usr/bin/clang-offload-bundler-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clangd                clangd                 /usr/bin/clangd-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-check           clang-check            /usr/bin/clang-check-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/scan-view             scan-view              /usr/bin/scan-view-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-apply-replacements clang-apply-replacements /usr/bin/clang-apply-replacements-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-query           clang-query            /usr/bin/clang-query-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/modularize            modularize             /usr/bin/modularize-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/sancov                sancov                 /usr/bin/sancov-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/c-index-test          c-index-test           /usr/bin/c-index-test-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-reorder-fields  clang-reorder-fields   /usr/bin/clang-reorder-fields-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-change-namespace clang-change-namespace  /usr/bin/clang-change-namespace-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-import-test     clang-import-test      /usr/bin/clang-import-test-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/scan-build            scan-build             /usr/bin/scan-build-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/scan-build-py         scan-build-py          /usr/bin/scan-build-py-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-cl              clang-cl               /usr/bin/clang-cl-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/clang-rename          clang-rename           /usr/bin/clang-rename-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/find-all-symbols      find-all-symbols       /usr/bin/find-all-symbols-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/lldb                  lldb                   /usr/bin/lldb-11 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --slave   /usr/bin/lldb-server           lldb-server            /usr/bin/lldb-server-11
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;installing-cmake&#34;&gt;Installing CMake&lt;/h2&gt;
&lt;p&gt;Install Kitware&amp;rsquo;s GPG Key&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2&amp;gt;/dev/null &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; gpg --dearmor - &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee /etc/apt/trusted.gpg.d/kitware.gpg &amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add repository&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;deb https://apt.kitware.com/ubuntu/ bionic main&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo tee -a /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install this optional package so you don&amp;rsquo;t have to mess with GPG keys anymore&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install kitware-archive-keyring
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo rm /etc/apt/trusted.gpg.d/kitware.gpg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now upgrade &lt;code&gt;cmake&lt;/code&gt; if you already have it installed with &lt;code&gt;sudo apt-get upgrade -y&lt;/code&gt; or just install it using &lt;code&gt;sudo apt-get install cmake -y&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;使用源码安装&#34;&gt;使用源码安装&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 更新软件包列表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装必要的依赖包&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install build-essential cmake python3-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 下载 Clang/LLVM 11 的源代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/llvm-11.0.0.src.tar.xz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 解压源代码文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar xvf llvm-11.0.0.src.tar.xz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 进入解压后的目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; llvm-11.0.0.src
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建一个新的目录，用于存放 Clang/LLVM 编译的结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir build
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; build
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 使用 cmake 编译 Clang/LLVM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cmake ..
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 编译 Clang/LLVM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装 Clang/LLVM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo make install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h2 id="从-apt-安装">从 APT 安装</h2>
<p>Install the GPG Key for <code>https://apt.llvm.org/</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key <span class="p">|</span> sudo apt-key add -
</span></span></code></pre></div><p>Add the repo for Clang 11 <code>stable-old</code> for Ubuntu 18.04 Bionic</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main&#34;</span> <span class="p">|</span> sudo tee -a /etc/apt/sources.list
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span></code></pre></div><p>Install practically everything (except <code>python-clang-11</code> which for some reason doesn&rsquo;t work)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt-get install libllvm-11-ocaml-dev libllvm11 llvm-11 llvm-11-dev llvm-11-doc llvm-11-examples llvm-11-runtime <span class="se">\
</span></span></span><span class="line"><span class="cl">clang-11 clang-tools-11 clang-11-doc libclang-common-11-dev libclang-11-dev libclang1-11 clang-format-11 clangd-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">libfuzzer-11-dev lldb-11 lld-11 libc++-11-dev libc++abi-11-dev libomp-11-dev -y
</span></span></code></pre></div><p>Make Clang 11 and everything related to it defaults</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo update-alternatives <span class="se">\
</span></span></span><span class="line"><span class="cl">  --install /usr/lib/llvm              llvm             /usr/lib/llvm-11         <span class="m">50</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-config       llvm-config      /usr/bin/llvm-config-11  <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-ar           llvm-ar          /usr/bin/llvm-ar-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-as           llvm-as          /usr/bin/llvm-as-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-bcanalyzer   llvm-bcanalyzer  /usr/bin/llvm-bcanalyzer-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-cov          llvm-cov         /usr/bin/llvm-cov-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-diff         llvm-diff        /usr/bin/llvm-diff-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-dis          llvm-dis         /usr/bin/llvm-dis-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-dwarfdump    llvm-dwarfdump   /usr/bin/llvm-dwarfdump-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-extract      llvm-extract     /usr/bin/llvm-extract-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-link         llvm-link        /usr/bin/llvm-link-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-mc           llvm-mc          /usr/bin/llvm-mc-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-mcmarkup     llvm-mcmarkup    /usr/bin/llvm-mcmarkup-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-nm           llvm-nm          /usr/bin/llvm-nm-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-objdump      llvm-objdump     /usr/bin/llvm-objdump-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-ranlib       llvm-ranlib      /usr/bin/llvm-ranlib-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-readobj      llvm-readobj     /usr/bin/llvm-readobj-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-rtdyld       llvm-rtdyld      /usr/bin/llvm-rtdyld-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-size         llvm-size        /usr/bin/llvm-size-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-stress       llvm-stress      /usr/bin/llvm-stress-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-symbolizer   llvm-symbolizer  /usr/bin/llvm-symbolizer-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/llvm-tblgen       llvm-tblgen      /usr/bin/llvm-tblgen-11
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo update-alternatives <span class="se">\
</span></span></span><span class="line"><span class="cl">  --install /usr/bin/clang                 clang                  /usr/bin/clang-11    <span class="m">50</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang++               clang++                /usr/bin/clang++-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/lld                   lld                    /usr/bin/lld-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-format          clang-format           /usr/bin/clang-format-11  <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-tidy            clang-tidy             /usr/bin/clang-tidy-11  <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-tidy-diff.py    clang-tidy-diff.py     /usr/bin/clang-tidy-diff-11.py <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-include-fixer   clang-include-fixer    /usr/bin/clang-include-fixer-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-offload-bundler clang-offload-bundler  /usr/bin/clang-offload-bundler-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clangd                clangd                 /usr/bin/clangd-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-check           clang-check            /usr/bin/clang-check-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/scan-view             scan-view              /usr/bin/scan-view-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-apply-replacements clang-apply-replacements /usr/bin/clang-apply-replacements-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-query           clang-query            /usr/bin/clang-query-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/modularize            modularize             /usr/bin/modularize-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/sancov                sancov                 /usr/bin/sancov-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/c-index-test          c-index-test           /usr/bin/c-index-test-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-reorder-fields  clang-reorder-fields   /usr/bin/clang-reorder-fields-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-change-namespace clang-change-namespace  /usr/bin/clang-change-namespace-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-import-test     clang-import-test      /usr/bin/clang-import-test-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/scan-build            scan-build             /usr/bin/scan-build-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/scan-build-py         scan-build-py          /usr/bin/scan-build-py-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-cl              clang-cl               /usr/bin/clang-cl-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/clang-rename          clang-rename           /usr/bin/clang-rename-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/find-all-symbols      find-all-symbols       /usr/bin/find-all-symbols-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/lldb                  lldb                   /usr/bin/lldb-11 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --slave   /usr/bin/lldb-server           lldb-server            /usr/bin/lldb-server-11
</span></span></code></pre></div><h2 id="installing-cmake">Installing CMake</h2>
<p>Install Kitware&rsquo;s GPG Key</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2&gt;/dev/null <span class="p">|</span> gpg --dearmor - <span class="p">|</span> sudo tee /etc/apt/trusted.gpg.d/kitware.gpg &gt;/dev/null
</span></span></code></pre></div><p>Add repository</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;deb https://apt.kitware.com/ubuntu/ bionic main&#34;</span> <span class="p">|</span> sudo tee -a /etc/apt/sources.list
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span></code></pre></div><p>Install this optional package so you don&rsquo;t have to mess with GPG keys anymore</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt-get install kitware-archive-keyring
</span></span><span class="line"><span class="cl">sudo rm /etc/apt/trusted.gpg.d/kitware.gpg
</span></span></code></pre></div><p>Now upgrade <code>cmake</code> if you already have it installed with <code>sudo apt-get upgrade -y</code> or just install it using <code>sudo apt-get install cmake -y</code></p>
<h2 id="使用源码安装">使用源码安装</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 更新软件包列表</span>
</span></span><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 安装必要的依赖包</span>
</span></span><span class="line"><span class="cl">sudo apt install build-essential cmake python3-dev
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 下载 Clang/LLVM 11 的源代码</span>
</span></span><span class="line"><span class="cl">wget https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/llvm-11.0.0.src.tar.xz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 解压源代码文件</span>
</span></span><span class="line"><span class="cl">tar xvf llvm-11.0.0.src.tar.xz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 进入解压后的目录</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> llvm-11.0.0.src
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建一个新的目录，用于存放 Clang/LLVM 编译的结果</span>
</span></span><span class="line"><span class="cl">mkdir build
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> build
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 cmake 编译 Clang/LLVM</span>
</span></span><span class="line"><span class="cl">cmake ..
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 编译 Clang/LLVM</span>
</span></span><span class="line"><span class="cl">make
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 安装 Clang/LLVM</span>
</span></span><span class="line"><span class="cl">sudo make install
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>每天学命令-chown 修改文件拥有者</title>
      <link>https://lifeislife.cn/posts/%E6%AF%8F%E5%A4%A9%E5%AD%A6%E5%91%BD%E4%BB%A4-chown%E4%BF%AE%E6%94%B9%E6%96%87%E4%BB%B6%E6%8B%A5%E6%9C%89%E8%80%85/</link>
      <pubDate>Sun, 04 Dec 2022 16:32:59 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E6%AF%8F%E5%A4%A9%E5%AD%A6%E5%91%BD%E4%BB%A4-chown%E4%BF%AE%E6%94%B9%E6%96%87%E4%BB%B6%E6%8B%A5%E6%9C%89%E8%80%85/</guid>
      <description>&lt;p&gt;chown 命令用来变更文件或目录的拥有者或所属群组，通过 chown 改变文件的拥有者和群组。用户可以是用户名或者用户 ID；组可以是组名或者组 ID；文件是以空格分开的文件列表，文件名也支持通配符。&lt;/p&gt;
&lt;h2 id=&#34;命令格式&#34;&gt;命令格式&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chown &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;选项&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;用户或组&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;文件或目录&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-c或--changes           &lt;span class=&#34;c1&#34;&gt;#效果类似“-v”参数，但仅回报更改的部分；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-f或--quite或—-silent    &lt;span class=&#34;c1&#34;&gt;#不显示错误信息；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-h或--no-dereference    &lt;span class=&#34;c1&#34;&gt;#只对符号连接的文件作修改，而不更改其他任何相关文件；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-R或--recursive         &lt;span class=&#34;c1&#34;&gt;#递归处理，将指定目录下的所有文件及子目录一并处理；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-v或--version           &lt;span class=&#34;c1&#34;&gt;#显示指令执行过程；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--dereference          &lt;span class=&#34;c1&#34;&gt;#效果和“-h”参数相同；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--help                 &lt;span class=&#34;c1&#34;&gt;#在线帮助&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--reference&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;参考文件或目录&amp;gt;   &lt;span class=&#34;c1&#34;&gt;#把指定文件或目录的拥有者与所属群组全部设成和参考文件或目录的拥有者与所属群组相同；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--version    &lt;span class=&#34;c1&#34;&gt;#显示版本信息。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;实例&#34;&gt;实例&lt;/h2&gt;
&lt;p&gt;将文件&lt;code&gt;test.md&lt;/code&gt;拥有者改为&lt;code&gt;nic&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chown nic test.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将目录&lt;code&gt;/home/nic/develop&lt;/code&gt;及其下面的所有文件、子目录的文件拥有者改为&lt;code&gt;nic&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chown -R nic /home/nic/develop
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>chown 命令用来变更文件或目录的拥有者或所属群组，通过 chown 改变文件的拥有者和群组。用户可以是用户名或者用户 ID；组可以是组名或者组 ID；文件是以空格分开的文件列表，文件名也支持通配符。</p>
<h2 id="命令格式">命令格式</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">chown <span class="o">[</span>选项<span class="o">]</span> <span class="o">[</span>用户或组<span class="o">]</span> <span class="o">[</span>文件或目录<span class="o">]</span>
</span></span><span class="line"><span class="cl">-c或--changes           <span class="c1">#效果类似“-v”参数，但仅回报更改的部分；</span>
</span></span><span class="line"><span class="cl">-f或--quite或—-silent    <span class="c1">#不显示错误信息；</span>
</span></span><span class="line"><span class="cl">-h或--no-dereference    <span class="c1">#只对符号连接的文件作修改，而不更改其他任何相关文件；</span>
</span></span><span class="line"><span class="cl">-R或--recursive         <span class="c1">#递归处理，将指定目录下的所有文件及子目录一并处理；</span>
</span></span><span class="line"><span class="cl">-v或--version           <span class="c1">#显示指令执行过程；</span>
</span></span><span class="line"><span class="cl">--dereference          <span class="c1">#效果和“-h”参数相同；</span>
</span></span><span class="line"><span class="cl">--help                 <span class="c1">#在线帮助</span>
</span></span><span class="line"><span class="cl">--reference<span class="o">=</span>&lt;参考文件或目录&gt;   <span class="c1">#把指定文件或目录的拥有者与所属群组全部设成和参考文件或目录的拥有者与所属群组相同；</span>
</span></span><span class="line"><span class="cl">--version    <span class="c1">#显示版本信息。</span>
</span></span></code></pre></div><h2 id="实例">实例</h2>
<p>将文件<code>test.md</code>拥有者改为<code>nic</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">chown nic test.md
</span></span></code></pre></div><p>将目录<code>/home/nic/develop</code>及其下面的所有文件、子目录的文件拥有者改为<code>nic</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">chown -R nic /home/nic/develop
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>每天学命令-tree 显示目录结构</title>
      <link>https://lifeislife.cn/posts/%E6%AF%8F%E5%A4%A9%E5%AD%A6%E5%91%BD%E4%BB%A4-tree%E6%98%BE%E7%A4%BA%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84/</link>
      <pubDate>Sun, 04 Dec 2022 16:31:54 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E6%AF%8F%E5%A4%A9%E5%AD%A6%E5%91%BD%E4%BB%A4-tree%E6%98%BE%E7%A4%BA%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-a   &lt;span class=&#34;c1&#34;&gt;#显示所有文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-d   &lt;span class=&#34;c1&#34;&gt;#只显示目录（名称）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-l   &lt;span class=&#34;c1&#34;&gt;#显示链接文件的原始文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-f   &lt;span class=&#34;c1&#34;&gt;#显示所列出的文件或目录的完整目录路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-i   &lt;span class=&#34;c1&#34;&gt;#不以阶梯的形式显示文件或目录名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-q   &lt;span class=&#34;c1&#34;&gt;#将控制字符以?字符代替，显示文件和目录名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-N   &lt;span class=&#34;c1&#34;&gt;#直接显示文件或目录的名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-p   &lt;span class=&#34;c1&#34;&gt;#显示每个文件的权限信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-u   &lt;span class=&#34;c1&#34;&gt;#显示文件所有者或者uid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-g   &lt;span class=&#34;c1&#34;&gt;#显示文件所属组或者gid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-s   &lt;span class=&#34;c1&#34;&gt;#显示每个文件的大小信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-h   &lt;span class=&#34;c1&#34;&gt;#以可读的方式显示文件的大小信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-D   &lt;span class=&#34;c1&#34;&gt;#显示最后修改日期&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-v   &lt;span class=&#34;c1&#34;&gt;#按字母数字正序显示文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-r   &lt;span class=&#34;c1&#34;&gt;#按字母数字倒序显示文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-t   &lt;span class=&#34;c1&#34;&gt;#按最后时间排序显示文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-C   &lt;span class=&#34;c1&#34;&gt;#在文件和目录列表上加上色彩，便于区分文件类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-P pattern    &lt;span class=&#34;c1&#34;&gt;#只显示匹配正则表式的文件或目录名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-I pattern    &lt;span class=&#34;c1&#34;&gt;#与上结果相反&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;实例&#34;&gt;实例&lt;/h2&gt;
&lt;p&gt;显示当前目录及其子目录下的文件及目录名称&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ tree                 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── CODE_OF_CONDUCT.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── CONTRIBUTING.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── Fedora-35
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── Readme.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── LICENSE
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── README.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── Ubuntu-20
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── Readme.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── init_edkrepo_conf.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── ubuntu20_dev_entrypoint.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── Windows-2022
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── Readme.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; directories, &lt;span class=&#34;m&#34;&gt;12&lt;/span&gt; files
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只显示一层目录结构&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ tree -L &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;              
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── CODE_OF_CONDUCT.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── CONTRIBUTING.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── Fedora-35
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── LICENSE
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── README.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── Ubuntu-20
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── Windows-2022
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; directories, &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; files
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只显示目录不显示文件&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ tree -d           
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── Fedora-35
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── Ubuntu-20
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── Windows-2022
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; directories
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">-a   <span class="c1">#显示所有文件</span>
</span></span><span class="line"><span class="cl">-d   <span class="c1">#只显示目录（名称）</span>
</span></span><span class="line"><span class="cl">-l   <span class="c1">#显示链接文件的原始文件</span>
</span></span><span class="line"><span class="cl">-f   <span class="c1">#显示所列出的文件或目录的完整目录路径</span>
</span></span><span class="line"><span class="cl">-i   <span class="c1">#不以阶梯的形式显示文件或目录名称</span>
</span></span><span class="line"><span class="cl">-q   <span class="c1">#将控制字符以?字符代替，显示文件和目录名称</span>
</span></span><span class="line"><span class="cl">-N   <span class="c1">#直接显示文件或目录的名称</span>
</span></span><span class="line"><span class="cl">-p   <span class="c1">#显示每个文件的权限信息</span>
</span></span><span class="line"><span class="cl">-u   <span class="c1">#显示文件所有者或者uid</span>
</span></span><span class="line"><span class="cl">-g   <span class="c1">#显示文件所属组或者gid</span>
</span></span><span class="line"><span class="cl">-s   <span class="c1">#显示每个文件的大小信息</span>
</span></span><span class="line"><span class="cl">-h   <span class="c1">#以可读的方式显示文件的大小信息</span>
</span></span><span class="line"><span class="cl">-D   <span class="c1">#显示最后修改日期</span>
</span></span><span class="line"><span class="cl">-v   <span class="c1">#按字母数字正序显示文件</span>
</span></span><span class="line"><span class="cl">-r   <span class="c1">#按字母数字倒序显示文件</span>
</span></span><span class="line"><span class="cl">-t   <span class="c1">#按最后时间排序显示文件</span>
</span></span><span class="line"><span class="cl">-C   <span class="c1">#在文件和目录列表上加上色彩，便于区分文件类型</span>
</span></span><span class="line"><span class="cl">-P pattern    <span class="c1">#只显示匹配正则表式的文件或目录名称</span>
</span></span><span class="line"><span class="cl">-I pattern    <span class="c1">#与上结果相反</span>
</span></span></code></pre></div><h2 id="实例">实例</h2>
<p>显示当前目录及其子目录下的文件及目录名称</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ tree                 
</span></span><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── CODE_OF_CONDUCT.md
</span></span><span class="line"><span class="cl">├── CONTRIBUTING.md
</span></span><span class="line"><span class="cl">├── Fedora-35
</span></span><span class="line"><span class="cl">│   ├── Dockerfile
</span></span><span class="line"><span class="cl">│   └── Readme.md
</span></span><span class="line"><span class="cl">├── LICENSE
</span></span><span class="line"><span class="cl">├── README.md
</span></span><span class="line"><span class="cl">├── Ubuntu-20
</span></span><span class="line"><span class="cl">│   ├── Dockerfile
</span></span><span class="line"><span class="cl">│   ├── Readme.md
</span></span><span class="line"><span class="cl">│   ├── init_edkrepo_conf.sh
</span></span><span class="line"><span class="cl">│   └── ubuntu20_dev_entrypoint.sh
</span></span><span class="line"><span class="cl">└── Windows-2022
</span></span><span class="line"><span class="cl">    ├── Dockerfile
</span></span><span class="line"><span class="cl">    └── Readme.md
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">3</span> directories, <span class="m">12</span> files
</span></span></code></pre></div><p>只显示一层目录结构</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ tree -L <span class="m">1</span>              
</span></span><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── CODE_OF_CONDUCT.md
</span></span><span class="line"><span class="cl">├── CONTRIBUTING.md
</span></span><span class="line"><span class="cl">├── Fedora-35
</span></span><span class="line"><span class="cl">├── LICENSE
</span></span><span class="line"><span class="cl">├── README.md
</span></span><span class="line"><span class="cl">├── Ubuntu-20
</span></span><span class="line"><span class="cl">└── Windows-2022
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">3</span> directories, <span class="m">4</span> files
</span></span></code></pre></div><p>只显示目录不显示文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ tree -d           
</span></span><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── Fedora-35
</span></span><span class="line"><span class="cl">├── Ubuntu-20
</span></span><span class="line"><span class="cl">└── Windows-2022
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">3</span> directories
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>CodeReview 中常见缩写</title>
      <link>https://lifeislife.cn/posts/codereview%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%BC%A9%E5%86%99/</link>
      <pubDate>Sat, 03 Dec 2022 19:55:07 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/codereview%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%BC%A9%E5%86%99/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;ASAP: As Soon As Possible. 请尽快完成&lt;/li&gt;
&lt;li&gt;ACK: Acknowledgement. 承认，同意。表示接受代码的改动&lt;/li&gt;
&lt;li&gt;CR: Code Review. 请求代码审查&lt;/li&gt;
&lt;li&gt;CCN: Code Comments Needed.需要的代码注释：在这里有一些简短的注释在高层次上描述每个主要代码块的作用（例如，“处理 HTTP 请求中的标头”）会很有帮助&lt;/li&gt;
&lt;li&gt;DOODOO: Documentation Out Of Date Or Obsolete.文档过时或过时：此文档似乎不正确：是否过时？&lt;/li&gt;
&lt;li&gt;DNM: Do not merge. 不要合并&lt;/li&gt;
&lt;li&gt;ditto: 多个重复的表述，下一次可以用 ditto 表示同上&lt;/li&gt;
&lt;li&gt;IMO: In My Opinion 在我看来、依我看、依我所见&lt;/li&gt;
&lt;li&gt;LGT1: Looks Good To 1. 如果有一个回复 LGTM 则可以添加为 LGT1，1 代表目前有 1 个赞&lt;/li&gt;
&lt;li&gt;LGT2: Looks Good To 2. 如果有两个回复 LGTM 则可以添加为 LGT2，2 代表目前有 2 个赞&lt;/li&gt;
&lt;li&gt;LGTM: Looks Good To Me. 代码已经过 review，可以合并&lt;/li&gt;
&lt;li&gt;MCE: Must Check for Errors.必须检查错误：这里可能会发生错误或异常情况，但您没有任何代码来处理此类事件&lt;/li&gt;
&lt;li&gt;MR：merge request. 合并请求&lt;/li&gt;
&lt;li&gt;NACK/NAK: Negative acknowledgement. 不同意，不接受这次的改动&lt;/li&gt;
&lt;li&gt;IMHO: In My Humble Opinion IMO 谦虚的说法&lt;/li&gt;
&lt;li&gt;IMO: In My Opinion. 在我看来&lt;/li&gt;
&lt;li&gt;IIRC: If I Recall Correctly. 如果我没有记错的话&lt;/li&gt;
&lt;li&gt;PR：Pull Request. 拉取请求，给其他项目提交代码&lt;/li&gt;
&lt;li&gt;PTAL: Please Take A Look. 提示项目 Owner/contributor review&lt;/li&gt;
&lt;li&gt;RFC: Request For Comment. 请求进行讨论，表示认为某个想法很好，邀请大家一起讨论一下&lt;/li&gt;
&lt;li&gt;RCP: Repeated Code Pattern.重复代码模式：与上面几行非常相似的代码在许多不同的地方重复出现。找到一种方法来简化它（例如，定义一个隐藏细节的更高级别的 API，或者找到一个更集中的地方来执行这些操作，这样这里就不需要这段代码了）。&lt;/li&gt;
&lt;li&gt;SGTM: Sounds Good To Me. 和上面那句意思差不多，也是已经通过了 review 的意思&lt;/li&gt;
&lt;li&gt;TBD: To Be Done. 未完成，待续&lt;/li&gt;
&lt;li&gt;TL;DR: Too Long; Don’t Read. PR 内容太多，没办法看&lt;/li&gt;
&lt;li&gt;TMLI: Too Many Levels of Indentation.Too Many Levels of Indentation：缩进太深的代码很难阅读。在大多数情况下，可以重构代码以减少嵌套级别。&lt;/li&gt;
&lt;li&gt;WIP: Work In Progress. 告诉项目维护者这个功能还未完成，方便维护者 review 已提交的代码&lt;/li&gt;
&lt;li&gt;TBR: To Be Reviewed. 提示维护者进行 review&lt;/li&gt;
&lt;li&gt;TBD: To Be Done (or Defined/Discussed/Decided/Determined). 根据语境不同意义有所区别，但一般都是还没搞定的意思&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<ul>
<li>ASAP: As Soon As Possible. 请尽快完成</li>
<li>ACK: Acknowledgement. 承认，同意。表示接受代码的改动</li>
<li>CR: Code Review. 请求代码审查</li>
<li>CCN: Code Comments Needed.需要的代码注释：在这里有一些简短的注释在高层次上描述每个主要代码块的作用（例如，“处理 HTTP 请求中的标头”）会很有帮助</li>
<li>DOODOO: Documentation Out Of Date Or Obsolete.文档过时或过时：此文档似乎不正确：是否过时？</li>
<li>DNM: Do not merge. 不要合并</li>
<li>ditto: 多个重复的表述，下一次可以用 ditto 表示同上</li>
<li>IMO: In My Opinion 在我看来、依我看、依我所见</li>
<li>LGT1: Looks Good To 1. 如果有一个回复 LGTM 则可以添加为 LGT1，1 代表目前有 1 个赞</li>
<li>LGT2: Looks Good To 2. 如果有两个回复 LGTM 则可以添加为 LGT2，2 代表目前有 2 个赞</li>
<li>LGTM: Looks Good To Me. 代码已经过 review，可以合并</li>
<li>MCE: Must Check for Errors.必须检查错误：这里可能会发生错误或异常情况，但您没有任何代码来处理此类事件</li>
<li>MR：merge request. 合并请求</li>
<li>NACK/NAK: Negative acknowledgement. 不同意，不接受这次的改动</li>
<li>IMHO: In My Humble Opinion IMO 谦虚的说法</li>
<li>IMO: In My Opinion. 在我看来</li>
<li>IIRC: If I Recall Correctly. 如果我没有记错的话</li>
<li>PR：Pull Request. 拉取请求，给其他项目提交代码</li>
<li>PTAL: Please Take A Look. 提示项目 Owner/contributor review</li>
<li>RFC: Request For Comment. 请求进行讨论，表示认为某个想法很好，邀请大家一起讨论一下</li>
<li>RCP: Repeated Code Pattern.重复代码模式：与上面几行非常相似的代码在许多不同的地方重复出现。找到一种方法来简化它（例如，定义一个隐藏细节的更高级别的 API，或者找到一个更集中的地方来执行这些操作，这样这里就不需要这段代码了）。</li>
<li>SGTM: Sounds Good To Me. 和上面那句意思差不多，也是已经通过了 review 的意思</li>
<li>TBD: To Be Done. 未完成，待续</li>
<li>TL;DR: Too Long; Don’t Read. PR 内容太多，没办法看</li>
<li>TMLI: Too Many Levels of Indentation.Too Many Levels of Indentation：缩进太深的代码很难阅读。在大多数情况下，可以重构代码以减少嵌套级别。</li>
<li>WIP: Work In Progress. 告诉项目维护者这个功能还未完成，方便维护者 review 已提交的代码</li>
<li>TBR: To Be Reviewed. 提示维护者进行 review</li>
<li>TBD: To Be Done (or Defined/Discussed/Decided/Determined). 根据语境不同意义有所区别，但一般都是还没搞定的意思</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>OFFICE-解决 Word 编辑卡顿</title>
      <link>https://lifeislife.cn/posts/office-%E8%A7%A3%E5%86%B3word%E7%BC%96%E8%BE%91%E5%8D%A1%E9%A1%BF/</link>
      <pubDate>Sat, 03 Dec 2022 19:01:27 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/office-%E8%A7%A3%E5%86%B3word%E7%BC%96%E8%BE%91%E5%8D%A1%E9%A1%BF/</guid>
      <description>&lt;p&gt;打开 Word 很快，但是一编辑就特别卡，尤其时拖动表格时几乎是逐帧移动。这是硬件图形加速问题。解决方式如下。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开 Word，点击左上角—&amp;gt;文件—&amp;gt;选项—&amp;gt;高级，一直拉到“显示”；&lt;/li&gt;
&lt;li&gt;勾选&lt;strong&gt;禁用硬件图形加速&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;取消勾选&lt;strong&gt;子像素定位平滑屏幕上的字体&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&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//2022/12/03/7215b5c4712d13d43182a8c610d64c37.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/12/03/7215b5c4712d13d43182a8c610d64c37.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;
</description>
      <content:encoded><![CDATA[<p>打开 Word 很快，但是一编辑就特别卡，尤其时拖动表格时几乎是逐帧移动。这是硬件图形加速问题。解决方式如下。</p>
<ol>
<li>打开 Word，点击左上角—&gt;文件—&gt;选项—&gt;高级，一直拉到“显示”；</li>
<li>勾选<strong>禁用硬件图形加速</strong>；</li>
<li>取消勾选<strong>子像素定位平滑屏幕上的字体</strong>。</li>
</ol>
<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//2022/12/03/7215b5c4712d13d43182a8c610d64c37.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/12/03/7215b5c4712d13d43182a8c610d64c37.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>
]]></content:encoded>
    </item>
    <item>
      <title>Git clone下来的分支不完整</title>
      <link>https://lifeislife.cn/posts/git-clone%E4%B8%8B%E6%9D%A5%E7%9A%84%E5%88%86%E6%94%AF%E4%B8%8D%E5%AE%8C%E6%95%B4/</link>
      <pubDate>Sat, 03 Dec 2022 18:52:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/git-clone%E4%B8%8B%E6%9D%A5%E7%9A%84%E5%88%86%E6%94%AF%E4%B8%8D%E5%AE%8C%E6%95%B4/</guid>
      <description>&lt;p&gt;将仓库&lt;code&gt;git clone&lt;/code&gt;到本地后发现本地缺失了一些远程仓库的分支。一般发生在&lt;code&gt;git clone —depth 1&lt;/code&gt;设置克隆深度时发生。因为有些大型项目一次性克隆容易出错，所以只克隆一层深度。&lt;/p&gt;
&lt;p&gt;如远程有分支&lt;code&gt;branch_a&lt;/code&gt;，克隆下来后使用&lt;code&gt;git branch -av&lt;/code&gt;命令查看所有分支没有显示该分支，该如何解决？&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git remote set-branches origin &lt;span class=&#34;s1&#34;&gt;&amp;#39;branch_a&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git fetch -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>将仓库<code>git clone</code>到本地后发现本地缺失了一些远程仓库的分支。一般发生在<code>git clone —depth 1</code>设置克隆深度时发生。因为有些大型项目一次性克隆容易出错，所以只克隆一层深度。</p>
<p>如远程有分支<code>branch_a</code>，克隆下来后使用<code>git branch -av</code>命令查看所有分支没有显示该分支，该如何解决？</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">git remote set-branches origin <span class="s1">&#39;branch_a&#39;</span>
</span></span><span class="line"><span class="cl">git fetch -v
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>手把手教你向开源社区提 Patch</title>
      <link>https://lifeislife.cn/posts/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%90%91%E5%BC%80%E6%BA%90%E7%A4%BE%E5%8C%BA%E6%8F%90patch/</link>
      <pubDate>Sun, 20 Nov 2022 15:11:57 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%90%91%E5%BC%80%E6%BA%90%E7%A4%BE%E5%8C%BA%E6%8F%90patch/</guid>
      <description>&lt;h1 id=&#34;提交补丁的最佳实践&#34;&gt;提交补丁的最佳实践&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;本文翻译自官方教程&lt;a href=&#34;https://git-scm.com/docs/MyFirstContribution&#34;&gt;Git - MyFirstContribution&lt;/a&gt;，原文包含开发到提交的整个周期。但是想要提交的人应该都已经开发完代码了，所以本文用自己的实际例子重新写了一遍，省去了开发代码等流程，重点介绍如何使用 git send-email。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;环境准备&#34;&gt;环境准备&lt;/h2&gt;
&lt;h3 id=&#34;下载-opensbi-仓库&#34;&gt;下载 OpenSBI 仓库&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/riscv-software-src/opensbi.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; opensbi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;安装依赖&#34;&gt;安装依赖&lt;/h3&gt;
&lt;p&gt;要从源代码构建 OpenSBI：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;注：OpenSBI 的构建是可并行的。上面的命令可以添加&lt;code&gt;-j#&lt;/code&gt;参数，如&lt;code&gt;-j12&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;确认要解决的问题&#34;&gt;确认要解决的问题&lt;/h3&gt;
&lt;p&gt;在本文档中，我们将模拟提交一个简单的 Patch，&lt;code&gt;.gitignore&lt;/code&gt;文件可以过滤不必要的文件，现在使用 VSCode 的用户越来越多，使用 VSCode 开发时常常会生成&lt;code&gt;.vscode&lt;/code&gt;目录，但是这些文件不该被推送至远程，原仓库中的&lt;code&gt;.gitignore&lt;/code&gt;文件中没有过滤该文件，我们给他加上。&lt;/p&gt;
&lt;p&gt;为了能够模拟一次发送多个&lt;code&gt;commit&lt;/code&gt;的场景，我们将再添加一个&lt;code&gt;.so&lt;/code&gt;用来过滤编译过程中生成的&lt;code&gt;.so&lt;/code&gt;文件。&lt;/p&gt;
&lt;h3 id=&#34;建立工作空间&#34;&gt;建立工作空间&lt;/h3&gt;
&lt;p&gt;让我们先建立一个开发分支来进行我们的修改。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout -b update_gitignore origin/master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们将在这里做一些提交，以演示如何将一个带有多个补丁的主题同时送审。&lt;/p&gt;
&lt;h2 id=&#34;实现代码&#34;&gt;实现代码&lt;/h2&gt;
&lt;h3 id=&#34;过滤-vscode&#34;&gt;过滤 .vscode&lt;/h3&gt;
&lt;p&gt;打开文件&lt;code&gt;.gitignore&lt;/code&gt;，为该文件添加&lt;code&gt;/.vscode/&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Object files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.dep
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#Build &amp;amp; install directories&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;build/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;install/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Development friendly files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tags
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cscope*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为以上修改做一次提交：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git status
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;On branch update_gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your branch is up to date with &lt;span class=&#34;s1&#34;&gt;&amp;#39;origin/master&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Changes not staged &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; commit:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;use &lt;span class=&#34;s2&#34;&gt;&amp;#34;git add &amp;lt;file&amp;gt;...&amp;#34;&lt;/span&gt; to update what will be committed&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;use &lt;span class=&#34;s2&#34;&gt;&amp;#34;git checkout -- &amp;lt;file&amp;gt;...&amp;#34;&lt;/span&gt; to discard changes in working directory&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        modified:   .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git add .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git commit -s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行以上命令后将会弹出编辑框用来编写提交信息。主题行要少于 50 个字符，然后是一个空行（必须），然后是您的提交消息的正文。请记住要明确并提供更改的原因（理由），特别是如果无法从您的差异中轻松理解你的提交内容时。编辑提交消息时，不要删除上面 &lt;code&gt;Signed-off-by&lt;/code&gt; 添加的 trailer。（由上面命令&lt;code&gt;-s&lt;/code&gt;参数生成）。&lt;/p&gt;
&lt;p&gt;其他规范请详细查阅目标社区的提交规范，如OpenSBI要求主题行需要以 &lt;code&gt;lib:&lt;/code&gt;， &lt;code&gt;platform:&lt;/code&gt;, &lt;code&gt;firmware:&lt;/code&gt;, &lt;code&gt;docs:&lt;/code&gt;, &lt;code&gt;utils:&lt;/code&gt; 或者 &lt;code&gt;top:&lt;/code&gt;为前缀，修改&lt;code&gt;.gitignore&lt;/code&gt;属于&lt;code&gt;top&lt;/code&gt;范畴，所以我们需要将其加在主题行上。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;top:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;filter&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;.vscode&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;folder&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;Filter&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;the&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;workspace&amp;#39;s&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;&amp;#39;.vscode&amp;#39;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;directory&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;by&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;adding&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;&amp;#39;/.vscode/&amp;#39;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;the.gitignore&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;file.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;Signed-off-by:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Dominic&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Zhang&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;&amp;lt;Dominic&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Zhang@gmail.com&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;继续用 &lt;code&gt;git show&lt;/code&gt; 检查您的新提交。尤其不要出现不需要在本次提交的内容。通常使用不同的 IDE 都可能会无意间生成一些配置文件等，请注意将其剔除。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;commit 5dc340c29979d4c5d8c4d5a6e881348239714434 (HEAD -&amp;gt; update_gitignore)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Author: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date:   Fri Nov 18 16:06:21 2022 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Filter the workspace&amp;#39;s &amp;#39;.vscode&amp;#39; directory by adding &amp;#39;/.vscode/&amp;#39; to the.gitignore file.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Signed-off-by: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;diff --git a/.gitignore b/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;index 95692bb..90cf552 100644
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- a/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+++ b/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@@ -10,3 +10,5 @@ install/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; # Development friendly files
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; tags
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; cscope*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;过滤-cache&#34;&gt;过滤 .cache&lt;/h3&gt;
&lt;p&gt;与上一节步骤类似，我们在&lt;code&gt;.gitignore&lt;/code&gt;文件中再添加一个&lt;code&gt;/.cache/&lt;/code&gt;字段用来过滤&lt;code&gt;.cache&lt;/code&gt;文件夹。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Object files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.dep
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#Build &amp;amp; install directories&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;build/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;install/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Development friendly files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tags
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cscope*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/.cache/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;添加完我们就即使保存工作进度，新生成一个&lt;code&gt;commit&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git add .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Filter the workspace&amp;#39;s &amp;#39;.cache&amp;#39; directory by adding &amp;#39;/.cache/&amp;#39; to the.gitignore file.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Signed-off-by: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上我们就已经准备好所有的代码了，在大部分场景下可能修改的是&lt;code&gt;.c&lt;/code&gt;或者&lt;code&gt;.h&lt;/code&gt;等源文件，这就需要我们能够使代码编译、运行并且测试通过后再提交。&lt;/p&gt;
&lt;p&gt;这里为了演示提交流程，就没有涉及这些步骤。接下来我们就要准备提交的补丁文件了。&lt;/p&gt;
&lt;h2 id=&#34;准备提交补丁&#34;&gt;准备提交补丁&lt;/h2&gt;
&lt;p&gt;OpenSBI 项目是通过电子邮件发送补丁来进行代码审查的，当补丁准备好并得到社区认可后，维护者就会应用（Apply）这些补丁。OpenSBI 项目不接受来自 Pull Request 的贡献，而且通过电子邮件发送的补丁需要以指定的方式进行审核。&lt;/p&gt;
&lt;p&gt;在研究如何将你的提交转化为电子邮件的补丁之前，让我们先分析一下最终的结果，即&lt;strong&gt;补丁系列&lt;/strong&gt;（Patch Series）是什么样子。下面是 OpenSBI 邮件列表存档的网页界面上的补丁系列的摘要视图的一个例子。&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/16-32-16-e3a06471e8dc4a8dc2d7c7ca641043e2-20221118163214-5afd66.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-32-16-e3a06471e8dc4a8dc2d7c7ca641043e2-20221118163214-5afd66.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;code&gt;[PATCH i/n]&lt;/code&gt;，代表&lt;code&gt;n&lt;/code&gt;个提交系列中的第 &lt;code&gt;i&lt;/code&gt; 个提交。&lt;/li&gt;
&lt;li&gt;每个补丁都是作为对&lt;code&gt;cover letter&lt;/code&gt;的回复，&lt;code&gt;cover letter&lt;/code&gt;的前缀为&lt;code&gt;[PATCH 0/n]&lt;/code&gt;，序号为 0 的标题。&lt;/li&gt;
&lt;li&gt;补丁系列的后续迭代被标记为 &lt;code&gt;PATCH v2&lt;/code&gt;、&lt;code&gt;PATCH v3&lt;/code&gt;，等等，以代替 &lt;code&gt;PATCH&lt;/code&gt;。例如，&lt;code&gt;[PATCH v2 1/3]&lt;/code&gt;将是第二次迭代中三个补丁的第一个补丁。每次迭代都有一个新的&lt;code&gt;cover letter&lt;/code&gt;（如上面的&lt;code&gt;[PATCH v2 0/3]&lt;/code&gt;），本身就是对前一次迭代的&lt;code&gt;cover letter&lt;/code&gt;的回复（下面会有更多介绍）。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;注：单一补丁的主题是以&lt;code&gt;[PATCH]&lt;/code&gt;、&lt;code&gt;[PATCH v2]&lt;/code&gt;等发送的，没有 &lt;code&gt;i/n&lt;/code&gt; 编号。如上图中的第四个 Patch，就是一个单一补丁。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;什么是-cover-letter&#34;&gt;什么是 cover letter&lt;/h3&gt;
&lt;p&gt;除了给每个补丁发一封邮件外，OpenSBI 社区还希望你的补丁能附带一封 cover letter。这是修改提交的一个重要组成部分，因为它概括了你想要做什么，以及为什么要这样做，比仅仅看你的补丁更明显。&lt;/p&gt;
&lt;p&gt;你的 cover letter 的标题应该是能简洁地涵盖你整个主题分支的目的。就像我们的提交信息标题一样。下面是我们的系列标题。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Update gitignore ---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;cover letter 的正文是用来给评审员提供额外的背景。一定要解释任何你的补丁自己没有说清楚的东西，但要记住，由于 cover letter 没有记录在提交历史中，任何可能对未来版本库历史的读者有用的东西也应该在你的提交信息中出现。&lt;/p&gt;
&lt;p&gt;下文我们将介绍如何生成 cover letter 以及如何填写 cover letter。&lt;/p&gt;
&lt;h3 id=&#34;用-git-send-email-发送补丁&#34;&gt;用 git send-email 发送补丁&lt;/h3&gt;
&lt;h4 id=&#34;前提条件---设置-git-send-email&#34;&gt;前提条件 - 设置 git send-email&lt;/h4&gt;
&lt;p&gt;对 &lt;code&gt;send-email&lt;/code&gt; 的配置会根据你的操作系统和电子邮件供应商而有所不同，配置可以参考文档&lt;a href=&#34;http://lifeislife.cn/2022/09/28/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8git-send-mail%E7%BB%99%E5%BC%80%E6%BA%90%E7%A4%BE%E5%8C%BA%E6%8F%90%E4%BA%A4Patch/&#34;&gt;如何使用 git-send-mail 给开源社区提交 Patch - 如云泊&lt;/a&gt;。&lt;/p&gt;
&lt;h4 id=&#34;准备初始补丁集&#34;&gt;准备初始补丁集&lt;/h4&gt;
&lt;p&gt;在准备邮件本身之前，你需要准备补丁。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git format-patch --cover-letter -o update_gitignore/ --base&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;auto  update_gitignore@&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;u&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;..update_gitignore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--cover-letter&lt;/code&gt; 选项告诉 &lt;code&gt;format-patch&lt;/code&gt; 为你创建一个 &lt;code&gt;cover letter&lt;/code&gt; 模板。在你准备发送之前，你将需要填写该模板。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o update_gitignore/&lt;/code&gt; 选项告诉 &lt;code&gt;format-patch&lt;/code&gt; 把补丁文件放到目录&lt;code&gt;update_gitignore&lt;/code&gt;中。这样发送多个&lt;code&gt;commit&lt;/code&gt;时就可以使用命令一次性发送，因为 &lt;code&gt;git send-email&lt;/code&gt; 可以接收一个目录并从那里发送所有补丁。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--base=auto&lt;/code&gt; 选项告诉命令记录”基本提交”，接收者将在此基础上应用补丁系列。自动值将使 &lt;code&gt;format-patch&lt;/code&gt; 自动计算基本提交，即远程跟踪分支的最新提交和指定修订范围的合并基数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update_gitignore@{u}..update_gitignore&lt;/code&gt; 选项告诉 &lt;code&gt;format-patch&lt;/code&gt; 为你在 &lt;code&gt;update_gitignore&lt;/code&gt; 分支上创建的提交生成补丁，因为它是从上游分叉出来的。&lt;code&gt;@{u}&lt;/code&gt;的意思就是从分叉开始到最新的提交。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;执行完该命令我们看看生成了哪些内容。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git status
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;On branch update_gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your branch is ahead of &lt;span class=&#34;s1&#34;&gt;&amp;#39;origin/master&amp;#39;&lt;/span&gt; by &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; commits.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;use &lt;span class=&#34;s2&#34;&gt;&amp;#34;git push&amp;#34;&lt;/span&gt; to publish your &lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; commits&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Untracked files:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;use &lt;span class=&#34;s2&#34;&gt;&amp;#34;git add &amp;lt;file&amp;gt;...&amp;#34;&lt;/span&gt; to include in what will be committed&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        update_gitignore/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ls update_gitignore 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0000-cover-letter.patch  0001-top-filter-.vscode-folder.patch  0002-top-filter-.cache-folder.patch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该命令将为每次提交制作一个补丁文件。运行后，您可以用您喜欢的文本编辑器看一下每个补丁，确保一切正常。可以看到创建了一个&lt;code&gt;-o&lt;/code&gt;参数中的&lt;code&gt;update_gitignore&lt;/code&gt;文件夹，该文件夹下有三个文件，分别是 cover letter 和上文我们做的两次提交对应的补丁文件。&lt;/p&gt;
&lt;p&gt;分别打开他们，结果如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date: Fri, 18 Nov 2022 16:41:32 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subject: [PATCH 0/2] *** SUBJECT HERE ***
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*** BLURB HERE ***
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dominic Zhang (2):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; .gitignore | 3 +++
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 1 file changed, 3 insertions(+)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;base-commit: 880685586dcee950d209088a461443449a1693ce
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-- 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2.17.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From 5dc340c29979d4c5d8c4d5a6e881348239714434 Mon Sep 17 00:00:00 2001
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date: Fri, 18 Nov 2022 16:06:21 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subject: [PATCH 1/2] top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Filter the workspace&amp;#39;s &amp;#39;.vscode&amp;#39; directory by adding &amp;#39;/.vscode/&amp;#39; to the.gitignore file.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Signed-off-by: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; .gitignore | 2 ++
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 1 file changed, 2 insertions(+)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;diff --git a/.gitignore b/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;index 95692bb..90cf552 100644
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- a/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+++ b/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@@ -10,3 +10,5 @@ install/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; # Development friendly files
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; tags
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; cscope*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;\ No newline at end of file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-- 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2.17.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date: Fri, 18 Nov 2022 16:20:37 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subject: [PATCH 2/2] top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Filter the workspace&amp;#39;s &amp;#39;.cache&amp;#39; directory by adding &amp;#39;/.cache/&amp;#39; to the.gitignore file.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Signed-off-by: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; .gitignore | 3 ++-
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 1 file changed, 2 insertions(+), 1 deletion(-)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;diff --git a/.gitignore b/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;index 90cf552..bf9d716 100644
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- a/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+++ b/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@@ -11,4 +11,5 @@ install/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; tags
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; cscope*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;\ No newline at end of file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+/.cache/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;\ No newline at end of file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-- 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2.17.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;注：另外，你也可以使用 &lt;code&gt;--rfc&lt;/code&gt; 参数，在你的补丁主题前加上&lt;code&gt;[RFC PATCH]&lt;/code&gt;，而不是&lt;code&gt;[PATCH]&lt;/code&gt;。RFC 是”请求评论”的意思，表示虽然你的代码还没有准备好提交，但你想开始代码审查过程。你也可能在列表中看到标有”WIP”的补丁，这意味着他们还没有完成，但希望审查者能看看他们目前的成果。你可以用&lt;code&gt;--subject-prefix=WIP&lt;/code&gt;来添加这个标志。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;检查并确保你的补丁和 cover letter 模板存在于你指定的目录中，这就完成所有准备了。&lt;/p&gt;
&lt;h4 id=&#34;准备邮件&#34;&gt;准备邮件&lt;/h4&gt;
&lt;p&gt;由于你在调用 &lt;code&gt;format-patch&lt;/code&gt; 时使用了&lt;code&gt;--cover-letter&lt;/code&gt;，你已经准备好了一个 cover letter 模板。在你喜欢的编辑器中打开它。&lt;/p&gt;
&lt;p&gt;你应该看到已经有一些标题存在。检查你的&lt;code&gt;From:&lt;/code&gt;标题是否正确。然后修改你的&lt;code&gt;Subject:&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;确保保留&lt;code&gt;[PATCH 0/X]&lt;/code&gt;的部分；这是向 Git 社区表明这封邮件是一个补丁系列的开始，许多审查者会根据这种类型的标记过滤他们的邮件。&lt;/p&gt;
&lt;p&gt;接下来，你必须填写你的 cover letter 的正文。同样，关于应包括哪些内容，见上文。&lt;/p&gt;
&lt;p&gt;最后，信中会包括用于生成补丁的 Git 的版本。你可以不用管这个字符串。&lt;/p&gt;
&lt;p&gt;完善后的 cover letter 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date: Fri, 18 Nov 2022 16:41:32 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subject: [PATCH 0/2] Update gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vscode is a very popular IDE, and it often needs to generate a.vscode. cache directory to hold workspace configuration files that should not be committed to a remote repository, so we made some modifications to the gitignore file to filter such directories.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dominic Zhang (2):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; .gitignore | 3 +++
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 1 file changed, 3 insertions(+)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;base-commit: 880685586dcee950d209088a461443449a1693ce
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-- 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2.17.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;发送邮件&#34;&gt;发送邮件&lt;/h4&gt;
&lt;p&gt;到这里，你应该有一个目录 &lt;code&gt;update_gitignore/&lt;/code&gt;，里面包含你的补丁和一封 cover letter。是时候把它发出去了！你可以像这样发送。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git send-email --to&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;target@example.com update_gitignore/*.patch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;注：请查看 &lt;code&gt;git help send-email&lt;/code&gt; 中的一些其他选项，你可能会发现这些选项很有价值，比如改变回复地址或添加更多的抄送地址或密送地址。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;注：当你发送一个真正的补丁时，它将被发送到 &lt;code&gt;opensbi@lists.infradead.org&lt;/code&gt; - 但请不要把你的补丁集从教程中发送到真正的邮件列表中！现在你可以把它发送给你自己，以确保你了解它的形式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在你运行上面的命令后，你会为每个即将发出的补丁看到一个交互提示。这给了你最后一次机会来编辑或放弃发送一些东西（但还是那句话，不要用这种方式编辑代码）。一旦你在这些提示下按下 &lt;code&gt;y&lt;/code&gt; 或 &lt;code&gt;a&lt;/code&gt;，你的邮件就会被发送出去！Congratulation!&lt;/p&gt;
&lt;h4 id=&#34;发送补丁的更新版本&#34;&gt;发送补丁的更新版本&lt;/h4&gt;
&lt;p&gt;本节将重点介绍如何发送你的补丁集的 v2 版。我们将在 v2 版中重新使用我们的 &lt;code&gt;update_gitignore&lt;/code&gt; 分支。在我们做任何改动之前，我们先新建一个名为&lt;code&gt;update_gitignore-v1&lt;/code&gt;的分支，这个分支是我们没有做新的改动的分支。这样在后面我们就可以方便的进行对比差异。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git checkout update_gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git branch update_gitignore-v1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在更新补丁时，我们可能会遇到两种情况，一种是社区的意见只让修改最新的一个提交，一种是修改历史记录中的 commit。我们分别来处理这两种情况。&lt;/p&gt;
&lt;h5 id=&#34;如何修改最新的提交&#34;&gt;如何修改最新的提交&lt;/h5&gt;
&lt;p&gt;比如只需要修改&lt;code&gt;top: filter .cache folder&lt;/code&gt;这个 commit。因为它在我们的修改中是最新的 commit，所以我们可以直接对代码修改。比如我们做一个简单的修改，给修改的内容&lt;code&gt;/.cache&lt;/code&gt;加个注释。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Object files
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.dep
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#Build &amp;amp; install directories
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;build/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;install/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Development friendly files
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tags
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cscope*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Cache file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/.cache/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git add .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;！我们不需要生成新的&lt;code&gt;commit&lt;/code&gt;，所以使用 &lt;code&gt;--amend&lt;/code&gt;参数修改最新的&lt;code&gt;commit message&lt;/code&gt;即可。执行这条命令会弹出编辑窗口，因为修改内容已经很明确，我们不需要在&lt;code&gt;commit message&lt;/code&gt;里再做额外说明，直接保存退出即可。如果修改内容比较大，需要重新编写&lt;code&gt;commit message&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;以上我们就完成了一次更新。&lt;/p&gt;
&lt;h5 id=&#34;如何修改历史记录中的提交&#34;&gt;如何修改历史记录中的提交&lt;/h5&gt;
&lt;p&gt;如果很不巧，社区要求修改的是&lt;code&gt;top: filter .vscode folder&lt;/code&gt;这个提交的内容，那怎么办，因为它不是最新的提交，而是上一个提交，我们无法使用&lt;code&gt;git commit --amend&lt;/code&gt;来直接对他修改，好在 Git 十分强大，不需要我们&lt;code&gt;reset&lt;/code&gt;就可以完成这样的工作。&lt;/p&gt;
&lt;p&gt;同样我们也做一个简单的修改，为&lt;code&gt;/.vscode/&lt;/code&gt;也添加一个注释。首先我们需要使用到&lt;code&gt;git rebase&lt;/code&gt;这个强大的命令。本文只介绍使用到的功能，其他功能需要大家自行摸索。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git rebase -i
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这条命令会弹出编辑窗口，&lt;code&gt;-i&lt;/code&gt;参数表示以交互式方式进行变基（rebase）操作。弹出窗口内容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pick &lt;span class=&#34;m&#34;&gt;7175772&lt;/span&gt; top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pick 52b63f3 top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Rebase 8806855..52b63f3 onto 8806855 (2 commands)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Commands:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# p, pick = use commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# r, reword = use commit, but edit the commit message&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# e, edit = use commit, but stop for amending&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# s, squash = use commit, but meld into previous commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# f, fixup = like &amp;#34;squash&amp;#34;, but discard this commit&amp;#39;s log message&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# x, exec = run command (the rest of the line) using shell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# d, drop = remove commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# These lines can be re-ordered; they are executed from top to bottom.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# If you remove a line here THAT COMMIT WILL BE LOST.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# However, if you remove everything, the rebase will be aborted.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Note that empty commits are commented out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;窗口会显示所有未提交到远程的 commit，下面的注释也告诉了我们该如何使用。我们找到&lt;code&gt;edit&lt;/code&gt;的行，可以看到解释为使用当前的 commit，但是在变基过程中会停下来让我们修改。这正是我们想要的。我们编辑当前的内容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;edit &lt;span class=&#34;m&#34;&gt;7175772&lt;/span&gt; top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pick 52b63f3 top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;表示我们需要编辑历史记录中的&lt;code&gt;top: filter .vscode folder&lt;/code&gt;提交，但是另一个 commit 我们不做改变。保存并退出当前窗口后，会有如下提示。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Stopped at 7175772...  top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;You can amend the commit now, with
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  git commit --amend 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Once you are satisfied with your changes, run
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  git rebase --continue
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据提示，我们可以进行一系列修改了，修改完使用&lt;code&gt;git commit --amend&lt;/code&gt;保存，如果一切符合自己要求了，再使用&lt;code&gt;git rebase --continue&lt;/code&gt;完成变基操作。&lt;/p&gt;
&lt;p&gt;我们先修改代码，可以看到代码已经回到了没有&lt;code&gt;/.cache/&lt;/code&gt;的状态，我们添加一行注释：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Object files
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.a
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.dep
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#Build &amp;amp; install directories
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;build/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;install/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Development friendly files
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tags
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cscope*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# VSCode config file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/.vscode/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git add .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同样弹出窗口后我们直接保存退出，如果修改幅度较大，可以进一步补充说明。然后使用以下命令继续完成变基。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git rebase --continue
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时我们可以看到我们不仅修改了历史记录中的 commit，还保证了最新的 commit 没有丢失或者更改。&lt;/p&gt;
&lt;h5 id=&#34;准备更新版本的补丁集&#34;&gt;准备更新版本的补丁集&lt;/h5&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; $ git format-patch -v2 --cover-letter -o update_gitignore/   master..update_gitignore-v1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update_gitignore/v2-0000-cover-letter.patch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update_gitignore/v2-0001-top-filter-.vscode-folder.patch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update_gitignore/v2-0002-top-filter-.cache-folder.patch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--range-diff  master..update_gitignore-v1&lt;/code&gt; 参数告诉 &lt;code&gt;format-patch&lt;/code&gt; 在 cover letter 中包括 &lt;code&gt;update_gitignore-v1&lt;/code&gt; 和 &lt;code&gt;update_gitignore&lt;/code&gt; 两个分支之间的差异。这有助于告诉评审人你的 v1 和 v2 补丁之间的差异。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-v2&lt;/code&gt; 参数告诉 &lt;code&gt;format-patch&lt;/code&gt; 将你的补丁输出为 &lt;code&gt;v2&lt;/code&gt; 版本。例如，你可能注意到你的 v2 版补丁都被命名为 &lt;code&gt;v2-000n-my-commit-subject.patch&lt;/code&gt;。&lt;code&gt;-v2&lt;/code&gt; 也会将你的补丁格式化，在前面加上&lt;code&gt;[PATCH v2]&lt;/code&gt;，而不是&lt;code&gt;[PATCH]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;运行此命令后，&lt;code&gt;format-patch&lt;/code&gt; 会将补丁输出到 &lt;code&gt;update_gitignore/&lt;/code&gt; 目录，与 v1 版的补丁一起。使用一个目录可以方便在校对 v2 补丁时参考旧的 v1 补丁，但你需要注意只发送 v2 补丁。我们将使用 &lt;code&gt;update_gitignore/v2-.patch&lt;/code&gt;这样的模式（而不是 &lt;code&gt;update_gitignore/.patch&lt;/code&gt;，这将匹配 v1 和 v2 补丁）。&lt;/p&gt;
&lt;p&gt;再次编辑你的 cover letter。现在是一个很好的时间来提及你的上一个版本和现在有什么不同，如果它是重要的东西。你不需要在你的第二封 cover letter 中使用完全相同的内容；重点是向审查人员解释你所做的可能不那么明显的变化。&lt;/p&gt;
&lt;p&gt;我们就简单的写一下添加了注释。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date: Fri, 18 Nov 2022 19:35:06 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subject: [PATCH v2 0/2] Update gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Add a comment for the folder name.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dominic Zhang (2):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; .gitignore | 3 +++
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 1 file changed, 3 insertions(+)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-- 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2.17.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;发送更新版本时你需要将新版本抄送给提出建议的人，你可以在你的 cover letter 中直接添加这些抄送行，在 Subject 行上面写上这样一行。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CC: Name &amp;lt;name@example.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如，把更新的邮件抄送给我自己：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CC: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date: Fri, 18 Nov 2022 19:35:06 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subject: [PATCH v2 0/2] Update gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Add a comment for the folder name.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dominic Zhang (2):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .vscode folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  top: filter .cache folder
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; .gitignore | 3 +++
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 1 file changed, 3 insertions(+)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-- 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2.17.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在再次发送电子邮件，注意你传入的参数。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git send-email --to target@example.com update_gitignore/v2-*.patch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;恭喜你完成了一次补丁版本更新。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;对于一些社区，要求更新的版本需要在同一个 thread 上进行。如下示例这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[PATCH 0/2] Here is what I did...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  [PATCH 1/2] Clean up and tests
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  [PATCH 2/2] Implementation
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  [PATCH v2 0/3] Here is a reroll
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    [PATCH v2 1/3] Clean up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    [PATCH v2 2/3] New tests
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    [PATCH v2 3/3] Implementation
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就是更新的版本需要关联到之前的版本，而不能作为单独的一个列表。&lt;/p&gt;
&lt;p&gt;你还需要去找到你之前的 cover letter 的 Message-Id。你可以在发送第一个补丁系列时，从 &lt;code&gt;git send-email&lt;/code&gt; 的输出中记下它。
例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git send-email --to Dominic Zhang@gmail.com update_gitignore/v2-*.patch 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update_gitignore/v2-0000-cover-letter.patch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update_gitignore/v2-0001-top-filter-.vscode-folder.patch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update_gitignore/v2-0002-top-filter-.cache-folder.patch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;mbox&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Adding cc: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt; from line &lt;span class=&#34;s1&#34;&gt;&amp;#39;From: Dominic Zhang &amp;lt;Dominic Zhang@gmail.com&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;mbox&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; Adding cc: Dominic Zhang &amp;lt;254758318@qq.com&amp;gt; from line &lt;span class=&#34;s1&#34;&gt;&amp;#39;CC: Dominic Zhang &amp;lt;254758318@qq.com&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;From: Dominic Zhang@gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;To: Dominic Zhang@gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Cc: Dominic Zhang &amp;lt;254758318@qq.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subject: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;PATCH v2 0/2&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Update gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Date: Fri, &lt;span class=&#34;m&#34;&gt;18&lt;/span&gt; Nov &lt;span class=&#34;m&#34;&gt;2022&lt;/span&gt; 19:54:54 +0800
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Message-Id: &amp;lt;20221118115456.2242-1-Dominic Zhang@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;X-Mailer: git-send-email 2.17.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你也可以从社区的邮箱列表中找到 Message ID，因为 OpenSBI 不要求在同一个 thread 回复，所以没有相关信息，这里以&lt;a href=&#34;https://lore.kernel.org/git/cover-00.12-00000000000-20221118T112205Z-avarab@gmail.com/T/#t&#34;&gt;Git 社区&lt;/a&gt;的邮箱列表为例。随便点击一个补丁主题，在页面中找到&lt;code&gt;permalink&lt;/code&gt;或者&lt;code&gt;raw&lt;/code&gt;，点击打开即可找到 Message ID 信息。&lt;/p&gt;
&lt;p&gt;它的格式一般如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Message-Id: &amp;lt;foo.12345.author@example.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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/20-11-53-e32c2da586e2b273cd1647e391c5c814-20221118201152-50efbe.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20-11-53-e32c2da586e2b273cd1647e391c5c814-20221118201152-50efbe.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/20-12-36-60538da64fc2af130b05eceb7c38cc52-20221118201235-5e5ace.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20-12-36-60538da64fc2af130b05eceb7c38cc52-20221118201235-5e5ace.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;如果要发送更新版本，那么我们就需要找到上一版本的 Message ID。如发送的是 V3 版本，那么我们需要找到 V2 版本的 Message ID。并且在发送邮件时添加如下参数：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ git send-email --to Dominic Zhang@gmail.com 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 --in-reply-to&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;foo.12345.author@example.com&amp;gt;&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 update_gitignore/v2-*.patch 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;只有一个-patch-的更改&#34;&gt;只有一个 Patch 的更改&lt;/h4&gt;
&lt;p&gt;在某些情况下，你的非常小的变化可能只包括一个补丁。这时，你只需要发送一封邮件。你的提交信息应该已经很有意义了，你只需要生成补丁文件就可以发送了。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git format-patch -o update_gitignore/  HEAD^
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HEAD^&lt;/code&gt;参数表示生成与上一个提交之间的差异。&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h1 id="提交补丁的最佳实践">提交补丁的最佳实践</h1>
<blockquote>
<p>本文翻译自官方教程<a href="https://git-scm.com/docs/MyFirstContribution">Git - MyFirstContribution</a>，原文包含开发到提交的整个周期。但是想要提交的人应该都已经开发完代码了，所以本文用自己的实际例子重新写了一遍，省去了开发代码等流程，重点介绍如何使用 git send-email。</p>
</blockquote>
<h2 id="环境准备">环境准备</h2>
<h3 id="下载-opensbi-仓库">下载 OpenSBI 仓库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/riscv-software-src/opensbi.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> opensbi
</span></span></code></pre></div><h3 id="安装依赖">安装依赖</h3>
<p>要从源代码构建 OpenSBI：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make
</span></span></code></pre></div><blockquote>
<p>注：OpenSBI 的构建是可并行的。上面的命令可以添加<code>-j#</code>参数，如<code>-j12</code>。</p>
</blockquote>
<h3 id="确认要解决的问题">确认要解决的问题</h3>
<p>在本文档中，我们将模拟提交一个简单的 Patch，<code>.gitignore</code>文件可以过滤不必要的文件，现在使用 VSCode 的用户越来越多，使用 VSCode 开发时常常会生成<code>.vscode</code>目录，但是这些文件不该被推送至远程，原仓库中的<code>.gitignore</code>文件中没有过滤该文件，我们给他加上。</p>
<p>为了能够模拟一次发送多个<code>commit</code>的场景，我们将再添加一个<code>.so</code>用来过滤编译过程中生成的<code>.so</code>文件。</p>
<h3 id="建立工作空间">建立工作空间</h3>
<p>让我们先建立一个开发分支来进行我们的修改。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout -b update_gitignore origin/master
</span></span></code></pre></div><p>我们将在这里做一些提交，以演示如何将一个带有多个补丁的主题同时送审。</p>
<h2 id="实现代码">实现代码</h2>
<h3 id="过滤-vscode">过滤 .vscode</h3>
<p>打开文件<code>.gitignore</code>，为该文件添加<code>/.vscode/</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Object files</span>
</span></span><span class="line"><span class="cl">*.o
</span></span><span class="line"><span class="cl">*.a
</span></span><span class="line"><span class="cl">*.dep
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#Build &amp; install directories</span>
</span></span><span class="line"><span class="cl">build/
</span></span><span class="line"><span class="cl">install/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Development friendly files</span>
</span></span><span class="line"><span class="cl">tags
</span></span><span class="line"><span class="cl">cscope*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/.vscode/
</span></span></code></pre></div><p>为以上修改做一次提交：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git status
</span></span><span class="line"><span class="cl">On branch update_gitignore
</span></span><span class="line"><span class="cl">Your branch is up to date with <span class="s1">&#39;origin/master&#39;</span>.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Changes not staged <span class="k">for</span> commit:
</span></span><span class="line"><span class="cl">  <span class="o">(</span>use <span class="s2">&#34;git add &lt;file&gt;...&#34;</span> to update what will be committed<span class="o">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">(</span>use <span class="s2">&#34;git checkout -- &lt;file&gt;...&#34;</span> to discard changes in working directory<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        modified:   .gitignore
</span></span><span class="line"><span class="cl">$ git add .gitignore
</span></span><span class="line"><span class="cl">$ git commit -s
</span></span></code></pre></div><p>执行以上命令后将会弹出编辑框用来编写提交信息。主题行要少于 50 个字符，然后是一个空行（必须），然后是您的提交消息的正文。请记住要明确并提供更改的原因（理由），特别是如果无法从您的差异中轻松理解你的提交内容时。编辑提交消息时，不要删除上面 <code>Signed-off-by</code> 添加的 trailer。（由上面命令<code>-s</code>参数生成）。</p>
<p>其他规范请详细查阅目标社区的提交规范，如OpenSBI要求主题行需要以 <code>lib:</code>， <code>platform:</code>, <code>firmware:</code>, <code>docs:</code>, <code>utils:</code> 或者 <code>top:</code>为前缀，修改<code>.gitignore</code>属于<code>top</code>范畴，所以我们需要将其加在主题行上。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">top:</span> <span class="err">filter</span> <span class="err">.vscode</span> <span class="err">folder</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">Filter</span> <span class="err">the</span> <span class="err">workspace&#39;s</span> <span class="err">&#39;.vscode&#39;</span> <span class="err">directory</span> <span class="err">by</span> <span class="err">adding</span> <span class="err">&#39;/.vscode/&#39;</span> <span class="err">to</span> <span class="err">the.gitignore</span> <span class="err">file.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">Signed-off-by:</span> <span class="err">Dominic</span> <span class="err">Zhang</span> <span class="err">&lt;Dominic</span> <span class="err">Zhang@gmail.com&gt;</span>
</span></span></code></pre></div><p>继续用 <code>git show</code> 检查您的新提交。尤其不要出现不需要在本次提交的内容。通常使用不同的 IDE 都可能会无意间生成一些配置文件等，请注意将其剔除。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">commit 5dc340c29979d4c5d8c4d5a6e881348239714434 (HEAD -&gt; update_gitignore)
</span></span><span class="line"><span class="cl">Author: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date:   Fri Nov 18 16:06:21 2022 +0800
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    top: filter .vscode folder
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    Filter the workspace&#39;s &#39;.vscode&#39; directory by adding &#39;/.vscode/&#39; to the.gitignore file.
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    Signed-off-by: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">diff --git a/.gitignore b/.gitignore
</span></span><span class="line"><span class="cl">index 95692bb..90cf552 100644
</span></span><span class="line"><span class="cl">--- a/.gitignore
</span></span><span class="line"><span class="cl">+++ b/.gitignore
</span></span><span class="line"><span class="cl">@@ -10,3 +10,5 @@ install/
</span></span><span class="line"><span class="cl"> # Development friendly files
</span></span><span class="line"><span class="cl"> tags
</span></span><span class="line"><span class="cl"> cscope*
</span></span><span class="line"><span class="cl">+
</span></span><span class="line"><span class="cl">+/.vscode/
</span></span></code></pre></div><h3 id="过滤-cache">过滤 .cache</h3>
<p>与上一节步骤类似，我们在<code>.gitignore</code>文件中再添加一个<code>/.cache/</code>字段用来过滤<code>.cache</code>文件夹。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Object files</span>
</span></span><span class="line"><span class="cl">*.o
</span></span><span class="line"><span class="cl">*.a
</span></span><span class="line"><span class="cl">*.dep
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#Build &amp; install directories</span>
</span></span><span class="line"><span class="cl">build/
</span></span><span class="line"><span class="cl">install/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Development friendly files</span>
</span></span><span class="line"><span class="cl">tags
</span></span><span class="line"><span class="cl">cscope*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/.vscode/
</span></span><span class="line"><span class="cl">/.cache/
</span></span></code></pre></div><p>添加完我们就即使保存工作进度，新生成一个<code>commit</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add .gitignore
</span></span><span class="line"><span class="cl">git commit -s
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">top: filter .cache folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Filter the workspace&#39;s &#39;.cache&#39; directory by adding &#39;/.cache/&#39; to the.gitignore file.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Signed-off-by: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span></code></pre></div><p>以上我们就已经准备好所有的代码了，在大部分场景下可能修改的是<code>.c</code>或者<code>.h</code>等源文件，这就需要我们能够使代码编译、运行并且测试通过后再提交。</p>
<p>这里为了演示提交流程，就没有涉及这些步骤。接下来我们就要准备提交的补丁文件了。</p>
<h2 id="准备提交补丁">准备提交补丁</h2>
<p>OpenSBI 项目是通过电子邮件发送补丁来进行代码审查的，当补丁准备好并得到社区认可后，维护者就会应用（Apply）这些补丁。OpenSBI 项目不接受来自 Pull Request 的贡献，而且通过电子邮件发送的补丁需要以指定的方式进行审核。</p>
<p>在研究如何将你的提交转化为电子邮件的补丁之前，让我们先分析一下最终的结果，即<strong>补丁系列</strong>（Patch Series）是什么样子。下面是 OpenSBI 邮件列表存档的网页界面上的补丁系列的摘要视图的一个例子。</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/16-32-16-e3a06471e8dc4a8dc2d7c7ca641043e2-20221118163214-5afd66.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-32-16-e3a06471e8dc4a8dc2d7c7ca641043e2-20221118163214-5afd66.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>每次提交都是以单独的邮件形式发送，提交信息的标题为主题，前缀为<code>[PATCH i/n]</code>，代表<code>n</code>个提交系列中的第 <code>i</code> 个提交。</li>
<li>每个补丁都是作为对<code>cover letter</code>的回复，<code>cover letter</code>的前缀为<code>[PATCH 0/n]</code>，序号为 0 的标题。</li>
<li>补丁系列的后续迭代被标记为 <code>PATCH v2</code>、<code>PATCH v3</code>，等等，以代替 <code>PATCH</code>。例如，<code>[PATCH v2 1/3]</code>将是第二次迭代中三个补丁的第一个补丁。每次迭代都有一个新的<code>cover letter</code>（如上面的<code>[PATCH v2 0/3]</code>），本身就是对前一次迭代的<code>cover letter</code>的回复（下面会有更多介绍）。</li>
</ul>
<blockquote>
<p>注：单一补丁的主题是以<code>[PATCH]</code>、<code>[PATCH v2]</code>等发送的，没有 <code>i/n</code> 编号。如上图中的第四个 Patch，就是一个单一补丁。</p>
</blockquote>
<h3 id="什么是-cover-letter">什么是 cover letter</h3>
<p>除了给每个补丁发一封邮件外，OpenSBI 社区还希望你的补丁能附带一封 cover letter。这是修改提交的一个重要组成部分，因为它概括了你想要做什么，以及为什么要这样做，比仅仅看你的补丁更明显。</p>
<p>你的 cover letter 的标题应该是能简洁地涵盖你整个主题分支的目的。就像我们的提交信息标题一样。下面是我们的系列标题。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Update gitignore ---
</span></span></code></pre></div><p>cover letter 的正文是用来给评审员提供额外的背景。一定要解释任何你的补丁自己没有说清楚的东西，但要记住，由于 cover letter 没有记录在提交历史中，任何可能对未来版本库历史的读者有用的东西也应该在你的提交信息中出现。</p>
<p>下文我们将介绍如何生成 cover letter 以及如何填写 cover letter。</p>
<h3 id="用-git-send-email-发送补丁">用 git send-email 发送补丁</h3>
<h4 id="前提条件---设置-git-send-email">前提条件 - 设置 git send-email</h4>
<p>对 <code>send-email</code> 的配置会根据你的操作系统和电子邮件供应商而有所不同，配置可以参考文档<a href="http://lifeislife.cn/2022/09/28/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8git-send-mail%E7%BB%99%E5%BC%80%E6%BA%90%E7%A4%BE%E5%8C%BA%E6%8F%90%E4%BA%A4Patch/">如何使用 git-send-mail 给开源社区提交 Patch - 如云泊</a>。</p>
<h4 id="准备初始补丁集">准备初始补丁集</h4>
<p>在准备邮件本身之前，你需要准备补丁。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git format-patch --cover-letter -o update_gitignore/ --base<span class="o">=</span>auto  update_gitignore@<span class="o">{</span>u<span class="o">}</span>..update_gitignore
</span></span></code></pre></div><ul>
<li><code>--cover-letter</code> 选项告诉 <code>format-patch</code> 为你创建一个 <code>cover letter</code> 模板。在你准备发送之前，你将需要填写该模板。</li>
<li><code>-o update_gitignore/</code> 选项告诉 <code>format-patch</code> 把补丁文件放到目录<code>update_gitignore</code>中。这样发送多个<code>commit</code>时就可以使用命令一次性发送，因为 <code>git send-email</code> 可以接收一个目录并从那里发送所有补丁。</li>
<li><code>--base=auto</code> 选项告诉命令记录”基本提交”，接收者将在此基础上应用补丁系列。自动值将使 <code>format-patch</code> 自动计算基本提交，即远程跟踪分支的最新提交和指定修订范围的合并基数。</li>
<li><code>update_gitignore@{u}..update_gitignore</code> 选项告诉 <code>format-patch</code> 为你在 <code>update_gitignore</code> 分支上创建的提交生成补丁，因为它是从上游分叉出来的。<code>@{u}</code>的意思就是从分叉开始到最新的提交。</li>
</ul>
<p>执行完该命令我们看看生成了哪些内容。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git status
</span></span><span class="line"><span class="cl">On branch update_gitignore
</span></span><span class="line"><span class="cl">Your branch is ahead of <span class="s1">&#39;origin/master&#39;</span> by <span class="m">2</span> commits.
</span></span><span class="line"><span class="cl">  <span class="o">(</span>use <span class="s2">&#34;git push&#34;</span> to publish your <span class="nb">local</span> commits<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Untracked files:
</span></span><span class="line"><span class="cl">  <span class="o">(</span>use <span class="s2">&#34;git add &lt;file&gt;...&#34;</span> to include in what will be committed<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        update_gitignore/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ ls update_gitignore 
</span></span><span class="line"><span class="cl">0000-cover-letter.patch  0001-top-filter-.vscode-folder.patch  0002-top-filter-.cache-folder.patch
</span></span></code></pre></div><p>该命令将为每次提交制作一个补丁文件。运行后，您可以用您喜欢的文本编辑器看一下每个补丁，确保一切正常。可以看到创建了一个<code>-o</code>参数中的<code>update_gitignore</code>文件夹，该文件夹下有三个文件，分别是 cover letter 和上文我们做的两次提交对应的补丁文件。</p>
<p>分别打开他们，结果如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
</span></span><span class="line"><span class="cl">From: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date: Fri, 18 Nov 2022 16:41:32 +0800
</span></span><span class="line"><span class="cl">Subject: [PATCH 0/2] *** SUBJECT HERE ***
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">*** BLURB HERE ***
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Dominic Zhang (2):
</span></span><span class="line"><span class="cl">  top: filter .vscode folder
</span></span><span class="line"><span class="cl">  top: filter .cache folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> .gitignore | 3 +++
</span></span><span class="line"><span class="cl"> 1 file changed, 3 insertions(+)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">base-commit: 880685586dcee950d209088a461443449a1693ce
</span></span><span class="line"><span class="cl">-- 
</span></span><span class="line"><span class="cl">2.17.1
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">From 5dc340c29979d4c5d8c4d5a6e881348239714434 Mon Sep 17 00:00:00 2001
</span></span><span class="line"><span class="cl">From: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date: Fri, 18 Nov 2022 16:06:21 +0800
</span></span><span class="line"><span class="cl">Subject: [PATCH 1/2] top: filter .vscode folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Filter the workspace&#39;s &#39;.vscode&#39; directory by adding &#39;/.vscode/&#39; to the.gitignore file.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Signed-off-by: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl"> .gitignore | 2 ++
</span></span><span class="line"><span class="cl"> 1 file changed, 2 insertions(+)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">diff --git a/.gitignore b/.gitignore
</span></span><span class="line"><span class="cl">index 95692bb..90cf552 100644
</span></span><span class="line"><span class="cl">--- a/.gitignore
</span></span><span class="line"><span class="cl">+++ b/.gitignore
</span></span><span class="line"><span class="cl">@@ -10,3 +10,5 @@ install/
</span></span><span class="line"><span class="cl"> # Development friendly files
</span></span><span class="line"><span class="cl"> tags
</span></span><span class="line"><span class="cl"> cscope*
</span></span><span class="line"><span class="cl">+
</span></span><span class="line"><span class="cl">+/.vscode/
</span></span><span class="line"><span class="cl">\ No newline at end of file
</span></span><span class="line"><span class="cl">-- 
</span></span><span class="line"><span class="cl">2.17.1
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
</span></span><span class="line"><span class="cl">From: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date: Fri, 18 Nov 2022 16:20:37 +0800
</span></span><span class="line"><span class="cl">Subject: [PATCH 2/2] top: filter .cache folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Filter the workspace&#39;s &#39;.cache&#39; directory by adding &#39;/.cache/&#39; to the.gitignore file.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Signed-off-by: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl"> .gitignore | 3 ++-
</span></span><span class="line"><span class="cl"> 1 file changed, 2 insertions(+), 1 deletion(-)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">diff --git a/.gitignore b/.gitignore
</span></span><span class="line"><span class="cl">index 90cf552..bf9d716 100644
</span></span><span class="line"><span class="cl">--- a/.gitignore
</span></span><span class="line"><span class="cl">+++ b/.gitignore
</span></span><span class="line"><span class="cl">@@ -11,4 +11,5 @@ install/
</span></span><span class="line"><span class="cl"> tags
</span></span><span class="line"><span class="cl"> cscope*
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">-/.vscode/
</span></span><span class="line"><span class="cl">\ No newline at end of file
</span></span><span class="line"><span class="cl">+/.vscode/
</span></span><span class="line"><span class="cl">+/.cache/
</span></span><span class="line"><span class="cl">\ No newline at end of file
</span></span><span class="line"><span class="cl">-- 
</span></span><span class="line"><span class="cl">2.17.1
</span></span></code></pre></div><blockquote>
<p>注：另外，你也可以使用 <code>--rfc</code> 参数，在你的补丁主题前加上<code>[RFC PATCH]</code>，而不是<code>[PATCH]</code>。RFC 是”请求评论”的意思，表示虽然你的代码还没有准备好提交，但你想开始代码审查过程。你也可能在列表中看到标有”WIP”的补丁，这意味着他们还没有完成，但希望审查者能看看他们目前的成果。你可以用<code>--subject-prefix=WIP</code>来添加这个标志。</p>
</blockquote>
<p>检查并确保你的补丁和 cover letter 模板存在于你指定的目录中，这就完成所有准备了。</p>
<h4 id="准备邮件">准备邮件</h4>
<p>由于你在调用 <code>format-patch</code> 时使用了<code>--cover-letter</code>，你已经准备好了一个 cover letter 模板。在你喜欢的编辑器中打开它。</p>
<p>你应该看到已经有一些标题存在。检查你的<code>From:</code>标题是否正确。然后修改你的<code>Subject:</code>。</p>
<p>确保保留<code>[PATCH 0/X]</code>的部分；这是向 Git 社区表明这封邮件是一个补丁系列的开始，许多审查者会根据这种类型的标记过滤他们的邮件。</p>
<p>接下来，你必须填写你的 cover letter 的正文。同样，关于应包括哪些内容，见上文。</p>
<p>最后，信中会包括用于生成补丁的 Git 的版本。你可以不用管这个字符串。</p>
<p>完善后的 cover letter 如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
</span></span><span class="line"><span class="cl">From: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date: Fri, 18 Nov 2022 16:41:32 +0800
</span></span><span class="line"><span class="cl">Subject: [PATCH 0/2] Update gitignore
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vscode is a very popular IDE, and it often needs to generate a.vscode. cache directory to hold workspace configuration files that should not be committed to a remote repository, so we made some modifications to the gitignore file to filter such directories.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Dominic Zhang (2):
</span></span><span class="line"><span class="cl">  top: filter .vscode folder
</span></span><span class="line"><span class="cl">  top: filter .cache folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> .gitignore | 3 +++
</span></span><span class="line"><span class="cl"> 1 file changed, 3 insertions(+)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">base-commit: 880685586dcee950d209088a461443449a1693ce
</span></span><span class="line"><span class="cl">-- 
</span></span><span class="line"><span class="cl">2.17.1
</span></span></code></pre></div><h4 id="发送邮件">发送邮件</h4>
<p>到这里，你应该有一个目录 <code>update_gitignore/</code>，里面包含你的补丁和一封 cover letter。是时候把它发出去了！你可以像这样发送。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git send-email --to<span class="o">=</span>target@example.com update_gitignore/*.patch
</span></span></code></pre></div><blockquote>
<p>注：请查看 <code>git help send-email</code> 中的一些其他选项，你可能会发现这些选项很有价值，比如改变回复地址或添加更多的抄送地址或密送地址。</p>
</blockquote>
<blockquote>
<p>注：当你发送一个真正的补丁时，它将被发送到 <code>opensbi@lists.infradead.org</code> - 但请不要把你的补丁集从教程中发送到真正的邮件列表中！现在你可以把它发送给你自己，以确保你了解它的形式。</p>
</blockquote>
<p>在你运行上面的命令后，你会为每个即将发出的补丁看到一个交互提示。这给了你最后一次机会来编辑或放弃发送一些东西（但还是那句话，不要用这种方式编辑代码）。一旦你在这些提示下按下 <code>y</code> 或 <code>a</code>，你的邮件就会被发送出去！Congratulation!</p>
<h4 id="发送补丁的更新版本">发送补丁的更新版本</h4>
<p>本节将重点介绍如何发送你的补丁集的 v2 版。我们将在 v2 版中重新使用我们的 <code>update_gitignore</code> 分支。在我们做任何改动之前，我们先新建一个名为<code>update_gitignore-v1</code>的分支，这个分支是我们没有做新的改动的分支。这样在后面我们就可以方便的进行对比差异。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git checkout update_gitignore
</span></span><span class="line"><span class="cl">git branch update_gitignore-v1
</span></span></code></pre></div><p>在更新补丁时，我们可能会遇到两种情况，一种是社区的意见只让修改最新的一个提交，一种是修改历史记录中的 commit。我们分别来处理这两种情况。</p>
<h5 id="如何修改最新的提交">如何修改最新的提交</h5>
<p>比如只需要修改<code>top: filter .cache folder</code>这个 commit。因为它在我们的修改中是最新的 commit，所以我们可以直接对代码修改。比如我们做一个简单的修改，给修改的内容<code>/.cache</code>加个注释。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"># Object files
</span></span><span class="line"><span class="cl">*.o
</span></span><span class="line"><span class="cl">*.a
</span></span><span class="line"><span class="cl">*.dep
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">#Build &amp; install directories
</span></span><span class="line"><span class="cl">build/
</span></span><span class="line"><span class="cl">install/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Development friendly files
</span></span><span class="line"><span class="cl">tags
</span></span><span class="line"><span class="cl">cscope*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/.vscode/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Cache file
</span></span><span class="line"><span class="cl">/.cache/
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add .gitignore
</span></span><span class="line"><span class="cl">git commit --amend
</span></span></code></pre></div><p><strong>注意</strong>！我们不需要生成新的<code>commit</code>，所以使用 <code>--amend</code>参数修改最新的<code>commit message</code>即可。执行这条命令会弹出编辑窗口，因为修改内容已经很明确，我们不需要在<code>commit message</code>里再做额外说明，直接保存退出即可。如果修改内容比较大，需要重新编写<code>commit message</code>。</p>
<p>以上我们就完成了一次更新。</p>
<h5 id="如何修改历史记录中的提交">如何修改历史记录中的提交</h5>
<p>如果很不巧，社区要求修改的是<code>top: filter .vscode folder</code>这个提交的内容，那怎么办，因为它不是最新的提交，而是上一个提交，我们无法使用<code>git commit --amend</code>来直接对他修改，好在 Git 十分强大，不需要我们<code>reset</code>就可以完成这样的工作。</p>
<p>同样我们也做一个简单的修改，为<code>/.vscode/</code>也添加一个注释。首先我们需要使用到<code>git rebase</code>这个强大的命令。本文只介绍使用到的功能，其他功能需要大家自行摸索。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rebase -i
</span></span></code></pre></div><p>这条命令会弹出编辑窗口，<code>-i</code>参数表示以交互式方式进行变基（rebase）操作。弹出窗口内容如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pick <span class="m">7175772</span> top: filter .vscode folder
</span></span><span class="line"><span class="cl">pick 52b63f3 top: filter .cache folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Rebase 8806855..52b63f3 onto 8806855 (2 commands)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Commands:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># p, pick = use commit</span>
</span></span><span class="line"><span class="cl"><span class="c1"># r, reword = use commit, but edit the commit message</span>
</span></span><span class="line"><span class="cl"><span class="c1"># e, edit = use commit, but stop for amending</span>
</span></span><span class="line"><span class="cl"><span class="c1"># s, squash = use commit, but meld into previous commit</span>
</span></span><span class="line"><span class="cl"><span class="c1"># f, fixup = like &#34;squash&#34;, but discard this commit&#39;s log message</span>
</span></span><span class="line"><span class="cl"><span class="c1"># x, exec = run command (the rest of the line) using shell</span>
</span></span><span class="line"><span class="cl"><span class="c1"># d, drop = remove commit</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># These lines can be re-ordered; they are executed from top to bottom.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># If you remove a line here THAT COMMIT WILL BE LOST.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># However, if you remove everything, the rebase will be aborted.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Note that empty commits are commented out</span>
</span></span></code></pre></div><p>窗口会显示所有未提交到远程的 commit，下面的注释也告诉了我们该如何使用。我们找到<code>edit</code>的行，可以看到解释为使用当前的 commit，但是在变基过程中会停下来让我们修改。这正是我们想要的。我们编辑当前的内容如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">edit <span class="m">7175772</span> top: filter .vscode folder
</span></span><span class="line"><span class="cl">pick 52b63f3 top: filter .cache folder
</span></span></code></pre></div><p>表示我们需要编辑历史记录中的<code>top: filter .vscode folder</code>提交，但是另一个 commit 我们不做改变。保存并退出当前窗口后，会有如下提示。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Stopped at 7175772...  top: filter .vscode folder
</span></span><span class="line"><span class="cl">You can amend the commit now, with
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git commit --amend 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Once you are satisfied with your changes, run
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git rebase --continue
</span></span></code></pre></div><p>根据提示，我们可以进行一系列修改了，修改完使用<code>git commit --amend</code>保存，如果一切符合自己要求了，再使用<code>git rebase --continue</code>完成变基操作。</p>
<p>我们先修改代码，可以看到代码已经回到了没有<code>/.cache/</code>的状态，我们添加一行注释：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"># Object files
</span></span><span class="line"><span class="cl">*.o
</span></span><span class="line"><span class="cl">*.a
</span></span><span class="line"><span class="cl">*.dep
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">#Build &amp; install directories
</span></span><span class="line"><span class="cl">build/
</span></span><span class="line"><span class="cl">install/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Development friendly files
</span></span><span class="line"><span class="cl">tags
</span></span><span class="line"><span class="cl">cscope*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># VSCode config file
</span></span><span class="line"><span class="cl">/.vscode/
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add .gitignore
</span></span><span class="line"><span class="cl">git commit --amend
</span></span></code></pre></div><p>同样弹出窗口后我们直接保存退出，如果修改幅度较大，可以进一步补充说明。然后使用以下命令继续完成变基。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git rebase --continue
</span></span></code></pre></div><p>此时我们可以看到我们不仅修改了历史记录中的 commit，还保证了最新的 commit 没有丢失或者更改。</p>
<h5 id="准备更新版本的补丁集">准备更新版本的补丁集</h5>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> $ git format-patch -v2 --cover-letter -o update_gitignore/   master..update_gitignore-v1
</span></span><span class="line"><span class="cl">update_gitignore/v2-0000-cover-letter.patch
</span></span><span class="line"><span class="cl">update_gitignore/v2-0001-top-filter-.vscode-folder.patch
</span></span><span class="line"><span class="cl">update_gitignore/v2-0002-top-filter-.cache-folder.patch
</span></span></code></pre></div><p><code>--range-diff  master..update_gitignore-v1</code> 参数告诉 <code>format-patch</code> 在 cover letter 中包括 <code>update_gitignore-v1</code> 和 <code>update_gitignore</code> 两个分支之间的差异。这有助于告诉评审人你的 v1 和 v2 补丁之间的差异。</p>
<p><code>-v2</code> 参数告诉 <code>format-patch</code> 将你的补丁输出为 <code>v2</code> 版本。例如，你可能注意到你的 v2 版补丁都被命名为 <code>v2-000n-my-commit-subject.patch</code>。<code>-v2</code> 也会将你的补丁格式化，在前面加上<code>[PATCH v2]</code>，而不是<code>[PATCH]</code>。</p>
<p>运行此命令后，<code>format-patch</code> 会将补丁输出到 <code>update_gitignore/</code> 目录，与 v1 版的补丁一起。使用一个目录可以方便在校对 v2 补丁时参考旧的 v1 补丁，但你需要注意只发送 v2 补丁。我们将使用 <code>update_gitignore/v2-.patch</code>这样的模式（而不是 <code>update_gitignore/.patch</code>，这将匹配 v1 和 v2 补丁）。</p>
<p>再次编辑你的 cover letter。现在是一个很好的时间来提及你的上一个版本和现在有什么不同，如果它是重要的东西。你不需要在你的第二封 cover letter 中使用完全相同的内容；重点是向审查人员解释你所做的可能不那么明显的变化。</p>
<p>我们就简单的写一下添加了注释。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
</span></span><span class="line"><span class="cl">From: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date: Fri, 18 Nov 2022 19:35:06 +0800
</span></span><span class="line"><span class="cl">Subject: [PATCH v2 0/2] Update gitignore
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Add a comment for the folder name.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Dominic Zhang (2):
</span></span><span class="line"><span class="cl">  top: filter .vscode folder
</span></span><span class="line"><span class="cl">  top: filter .cache folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> .gitignore | 3 +++
</span></span><span class="line"><span class="cl"> 1 file changed, 3 insertions(+)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">-- 
</span></span><span class="line"><span class="cl">2.17.1
</span></span></code></pre></div><p>发送更新版本时你需要将新版本抄送给提出建议的人，你可以在你的 cover letter 中直接添加这些抄送行，在 Subject 行上面写上这样一行。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">CC: Name &lt;name@example.com&gt;
</span></span></code></pre></div><p>例如，把更新的邮件抄送给我自己：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">From 30614e5469be4a2f930cca570836627a4e91f1d1 Mon Sep 17 00:00:00 2001
</span></span><span class="line"><span class="cl">From: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">CC: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date: Fri, 18 Nov 2022 19:35:06 +0800
</span></span><span class="line"><span class="cl">Subject: [PATCH v2 0/2] Update gitignore
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Add a comment for the folder name.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Dominic Zhang (2):
</span></span><span class="line"><span class="cl">  top: filter .vscode folder
</span></span><span class="line"><span class="cl">  top: filter .cache folder
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> .gitignore | 3 +++
</span></span><span class="line"><span class="cl"> 1 file changed, 3 insertions(+)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">-- 
</span></span><span class="line"><span class="cl">2.17.1
</span></span></code></pre></div><p>现在再次发送电子邮件，注意你传入的参数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">git send-email --to target@example.com update_gitignore/v2-*.patch
</span></span></code></pre></div><p>恭喜你完成了一次补丁版本更新。</p>
<hr>
<p>对于一些社区，要求更新的版本需要在同一个 thread 上进行。如下示例这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[PATCH 0/2] Here is what I did...
</span></span><span class="line"><span class="cl">  [PATCH 1/2] Clean up and tests
</span></span><span class="line"><span class="cl">  [PATCH 2/2] Implementation
</span></span><span class="line"><span class="cl">  [PATCH v2 0/3] Here is a reroll
</span></span><span class="line"><span class="cl">    [PATCH v2 1/3] Clean up
</span></span><span class="line"><span class="cl">    [PATCH v2 2/3] New tests
</span></span><span class="line"><span class="cl">    [PATCH v2 3/3] Implementation
</span></span></code></pre></div><p>就是更新的版本需要关联到之前的版本，而不能作为单独的一个列表。</p>
<p>你还需要去找到你之前的 cover letter 的 Message-Id。你可以在发送第一个补丁系列时，从 <code>git send-email</code> 的输出中记下它。
例如：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git send-email --to Dominic Zhang@gmail.com update_gitignore/v2-*.patch 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">update_gitignore/v2-0000-cover-letter.patch
</span></span><span class="line"><span class="cl">update_gitignore/v2-0001-top-filter-.vscode-folder.patch
</span></span><span class="line"><span class="cl">update_gitignore/v2-0002-top-filter-.cache-folder.patch
</span></span><span class="line"><span class="cl"><span class="o">(</span>mbox<span class="o">)</span> Adding cc: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt; from line <span class="s1">&#39;From: Dominic Zhang &lt;Dominic Zhang@gmail.com&gt;&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span>mbox<span class="o">)</span> Adding cc: Dominic Zhang &lt;254758318@qq.com&gt; from line <span class="s1">&#39;CC: Dominic Zhang &lt;254758318@qq.com&gt;&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">From: Dominic Zhang@gmail.com
</span></span><span class="line"><span class="cl">To: Dominic Zhang@gmail.com
</span></span><span class="line"><span class="cl">Cc: Dominic Zhang &lt;254758318@qq.com&gt;
</span></span><span class="line"><span class="cl">Subject: <span class="o">[</span>PATCH v2 0/2<span class="o">]</span> Update gitignore
</span></span><span class="line"><span class="cl">Date: Fri, <span class="m">18</span> Nov <span class="m">2022</span> 19:54:54 +0800
</span></span><span class="line"><span class="cl">Message-Id: &lt;20221118115456.2242-1-Dominic Zhang@gmail.com&gt;
</span></span><span class="line"><span class="cl">X-Mailer: git-send-email 2.17.1
</span></span></code></pre></div><p>你也可以从社区的邮箱列表中找到 Message ID，因为 OpenSBI 不要求在同一个 thread 回复，所以没有相关信息，这里以<a href="https://lore.kernel.org/git/cover-00.12-00000000000-20221118T112205Z-avarab@gmail.com/T/#t">Git 社区</a>的邮箱列表为例。随便点击一个补丁主题，在页面中找到<code>permalink</code>或者<code>raw</code>，点击打开即可找到 Message ID 信息。</p>
<p>它的格式一般如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Message-Id: &lt;foo.12345.author@example.com&gt;
</span></span></code></pre></div><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/20-11-53-e32c2da586e2b273cd1647e391c5c814-20221118201152-50efbe.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20-11-53-e32c2da586e2b273cd1647e391c5c814-20221118201152-50efbe.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/20-12-36-60538da64fc2af130b05eceb7c38cc52-20221118201235-5e5ace.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20-12-36-60538da64fc2af130b05eceb7c38cc52-20221118201235-5e5ace.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>如果要发送更新版本，那么我们就需要找到上一版本的 Message ID。如发送的是 V3 版本，那么我们需要找到 V2 版本的 Message ID。并且在发送邮件时添加如下参数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git send-email --to Dominic Zhang@gmail.com 
</span></span><span class="line"><span class="cl">                 --in-reply-to<span class="o">=</span><span class="s2">&#34;&lt;foo.12345.author@example.com&gt;&#34;</span> 
</span></span><span class="line"><span class="cl">                 update_gitignore/v2-*.patch 
</span></span></code></pre></div><h4 id="只有一个-patch-的更改">只有一个 Patch 的更改</h4>
<p>在某些情况下，你的非常小的变化可能只包括一个补丁。这时，你只需要发送一封邮件。你的提交信息应该已经很有意义了，你只需要生成补丁文件就可以发送了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git format-patch -o update_gitignore/  HEAD^
</span></span></code></pre></div><ul>
<li><code>HEAD^</code>参数表示生成与上一个提交之间的差异。</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>更换 Debian 软件更新源</title>
      <link>https://lifeislife.cn/posts/linux%E6%9B%B4%E6%8D%A2debian%E8%BD%AF%E4%BB%B6%E6%9B%B4%E6%96%B0%E6%BA%90/</link>
      <pubDate>Sat, 05 Nov 2022 09:27:52 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/linux%E6%9B%B4%E6%8D%A2debian%E8%BD%AF%E4%BB%B6%E6%9B%B4%E6%96%B0%E6%BA%90/</guid>
      <description>&lt;h2 id=&#34;确认-debian-版本&#34;&gt;确认 Debian 版本&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ cat /etc/os-release               
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PRETTY_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Debian GNU/Linux 10 (buster)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Debian GNU/Linux&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VERSION_ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VERSION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;10 (buster)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VERSION_CODENAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;buster
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;debian
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;HOME_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://www.debian.org/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SUPPORT_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://www.debian.org/support&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BUG_REPORT_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://bugs.debian.org/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;括号里的&lt;code&gt;buster&lt;/code&gt;就是版本信息。&lt;/p&gt;
&lt;h2 id=&#34;获取镜像地址&#34;&gt;获取镜像地址&lt;/h2&gt;
&lt;p&gt;打开&lt;a href=&#34;https://mirrors.tuna.tsinghua.edu.cn/help/debian/&#34;&gt;debian | 清华大学开源软件镜像站&lt;/a&gt;，选择&lt;code&gt;buster&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//2022/11/05/54a39f7d33cf13259607144e03747e5a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/11/05/54a39f7d33cf13259607144e03747e5a.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 class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 默认注释了源码镜像以提高 apt update 速度，如有需要可自行取消注释&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;备份原文件&#34;&gt;备份原文件&lt;/h2&gt;
&lt;p&gt;这也算是系统文件的一部分，还是保险一点，出错了再改回来。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;打开并修改&#34;&gt;打开并修改&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/apt/sources.list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;vim&lt;/code&gt;用的不习惯的估计会和我一样找全选内容怎么操作。教给你了
在命令模式下，就是按一下&lt;code&gt;esc&lt;/code&gt;键，然后输入&lt;code&gt;ggvG&lt;/code&gt;。具体什么含义看&lt;code&gt;VIM 笔记&lt;/code&gt;吧，选择后直接&lt;code&gt;delete&lt;/code&gt;删除，镜像地址粘贴进去。保存退出。&lt;/p&gt;
&lt;h2 id=&#34;更新&#34;&gt;更新&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get dist-upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get upgrade
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h2 id="确认-debian-版本">确认 Debian 版本</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ cat /etc/os-release               
</span></span><span class="line"><span class="cl"><span class="nv">PRETTY_NAME</span><span class="o">=</span><span class="s2">&#34;Debian GNU/Linux 10 (buster)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">NAME</span><span class="o">=</span><span class="s2">&#34;Debian GNU/Linux&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">VERSION_ID</span><span class="o">=</span><span class="s2">&#34;10&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">VERSION</span><span class="o">=</span><span class="s2">&#34;10 (buster)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">VERSION_CODENAME</span><span class="o">=</span>buster
</span></span><span class="line"><span class="cl"><span class="nv">ID</span><span class="o">=</span>debian
</span></span><span class="line"><span class="cl"><span class="nv">HOME_URL</span><span class="o">=</span><span class="s2">&#34;https://www.debian.org/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">SUPPORT_URL</span><span class="o">=</span><span class="s2">&#34;https://www.debian.org/support&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">BUG_REPORT_URL</span><span class="o">=</span><span class="s2">&#34;https://bugs.debian.org/&#34;</span>
</span></span></code></pre></div><p>括号里的<code>buster</code>就是版本信息。</p>
<h2 id="获取镜像地址">获取镜像地址</h2>
<p>打开<a href="https://mirrors.tuna.tsinghua.edu.cn/help/debian/">debian | 清华大学开源软件镜像站</a>，选择<code>buster</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//2022/11/05/54a39f7d33cf13259607144e03747e5a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/11/05/54a39f7d33cf13259607144e03747e5a.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 class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 默认注释了源码镜像以提高 apt update 速度，如有需要可自行取消注释</span>
</span></span><span class="line"><span class="cl">deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free</span>
</span></span><span class="line"><span class="cl">deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free
</span></span><span class="line"><span class="cl"><span class="c1"># deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free</span>
</span></span></code></pre></div><h2 id="备份原文件">备份原文件</h2>
<p>这也算是系统文件的一部分，还是保险一点，出错了再改回来。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup
</span></span></code></pre></div><h2 id="打开并修改">打开并修改</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/apt/sources.list
</span></span></code></pre></div><p><code>vim</code>用的不习惯的估计会和我一样找全选内容怎么操作。教给你了
在命令模式下，就是按一下<code>esc</code>键，然后输入<code>ggvG</code>。具体什么含义看<code>VIM 笔记</code>吧，选择后直接<code>delete</code>删除，镜像地址粘贴进去。保存退出。</p>
<h2 id="更新">更新</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get dist-upgrade
</span></span><span class="line"><span class="cl">sudo apt-get upgrade
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>QEMU&#39;s instance_init() vs. realize()</title>
      <link>https://lifeislife.cn/posts/qemu-s-instance-init-vs-realize/</link>
      <pubDate>Tue, 01 Nov 2022 09:51:28 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu-s-instance-init-vs-realize/</guid>
      <description>&lt;p&gt;转载自&lt;a href=&#34;https://github.com/huth&#34;&gt;huth (Thomas Huth)&lt;/a&gt;的一篇文章，原文已经 404，从网页快照中找回的文章。&lt;/p&gt;
&lt;p&gt;Note that this is a blog post for (new) QEMU developers. If you are just interested in using QEMU, you can certainly skip this text. Otherwise, in case you have ever been in touch with the QEMU device model (&amp;ldquo;qdev&amp;rdquo;), you are likely aware of the basic qdev code boilerplate already:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_realize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DeviceState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Property&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_properties&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;DEFINE_PROP_xxx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myprop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MyDevState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;field&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;DEFINE_PROP_END_OF_LIST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;oc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DeviceClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;DEVICE_CLASS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;oc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;realize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_realize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;desc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;My cool device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;props&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_properties&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_MYDEV&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mydev_state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;type_register_static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mydev_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mydev_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are three different initialization functions involved here, the &lt;strong&gt;class_init&lt;/strong&gt;, the &lt;strong&gt;instance_init&lt;/strong&gt; and the &lt;strong&gt;realize&lt;/strong&gt; function. While it is quite obvious to distinguish the &lt;em&gt;class_init&lt;/em&gt; function from the two others (it is used for initializing the class data, not the data that is used for an instance … this is similar to the object model with classes and instances in C++), I initially always wondered about the difference between the &lt;em&gt;instance_init()&lt;/em&gt; and the &lt;em&gt;realize()&lt;/em&gt; functions. Having fixed quite a lot of related bugs in the past months in the QEMU code base, I now know that a lot of other people are also not properly aware of the difference here, so I think it is now time to write down some information that I&amp;rsquo;m now aware of, to make sure that I don&amp;rsquo;t forget about this again, and maybe help others to avoid related bugs in the future ;-)&lt;/p&gt;
&lt;p&gt;First it is of course always a good idea to have a look at the documentation. While the &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427&#34;&gt;documentation of &lt;/a&gt;&lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427&#34;&gt;&lt;em&gt;TypeInfo&lt;/em&gt;&lt;/a&gt; (where &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l434&#34;&gt;&lt;em&gt;instance_init()&lt;/em&gt;&lt;/a&gt; is defined) is not very helpful to understand the differences, the &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40&#34;&gt;documentation of &lt;/a&gt;&lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40&#34;&gt;&lt;em&gt;DeviceClass&lt;/em&gt;&lt;/a&gt; (where &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l43&#34;&gt;&lt;em&gt;realize()&lt;/em&gt;&lt;/a&gt; is defined) has some more useful information: You can learn here that the object instantiation is done first, before the device is realized, i.e. the &lt;em&gt;instance_init()&lt;/em&gt; function is called first, and the &lt;em&gt;realize()&lt;/em&gt; function is called afterwards. The former must not fail, while the latter can return an error to its caller via a pointer to an &lt;em&gt;“Error”&lt;/em&gt; object pointer.&lt;/p&gt;
&lt;p&gt;So the basic idea here is that device objects are first instantiated, then these objects can be inspected for their interfaces and their creators can set up their properties to configure their settings and wire them up with other devices, before the device finally becomes &amp;ldquo;active&amp;rdquo; by being &lt;em&gt;realized&lt;/em&gt;. It is important here to notice that &lt;strong&gt;devices can be instantiated (and also finalized) &lt;em&gt;&lt;strong&gt;&lt;strong&gt;without&lt;/strong&gt;&lt;/strong&gt;&lt;/em&gt; being realized&lt;/strong&gt;! This happens for example if the device is introspected: If you enter for example &lt;code&gt;device_add xyz,help&lt;/code&gt; at the HMP monitor, or if you send the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command to QEMU to retrieve the device&amp;rsquo;s properties, QEMU creates a temporary instance of the device to query the properties of the object, without realizing it. The object gets destroyed (&amp;ldquo;finalized&amp;rdquo;) immediately afterwards.&lt;/p&gt;
&lt;p&gt;Knowing this, you can avoid a set of bugs which could be found with a couple of devices in the past:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you want your device to provide properties for other parts of the QEMU code or for the users, and you want to add those properties via one of the many object_property_add*() functions of QEMU (instead of using the &lt;em&gt;“props”&lt;/em&gt; field of the &lt;em&gt;DeviceClass&lt;/em&gt;), then you should do this in the &lt;em&gt;instance_init()&lt;/em&gt; and not in the &lt;em&gt;realize()&lt;/em&gt; function. Otherwise the properties won&amp;rsquo;t show up when the user runs &lt;code&gt;--device xyz,help&lt;/code&gt; or the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command to get some information about your device.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;instance_init()&lt;/em&gt; functions must really never fail, i.e. also not call &lt;em&gt;abort()&lt;/em&gt; or &lt;em&gt;exit()&lt;/em&gt;. Otherwise QEMU can terminate unexpectedly when a user simply wanted to have a look at the list of device properties with &lt;code&gt;device_add xyz,help&lt;/code&gt; or the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command. If your device cannot work in certain circumstances, check for the error condition in the &lt;em&gt;realize()&lt;/em&gt; function instead and return with an appropriate error there.&lt;/li&gt;
&lt;li&gt;Never assume that your device is always instantiated only with the machine that it was designed for. It&amp;rsquo;s of course a good idea to set the &lt;em&gt;“user_creatable = false”&lt;/em&gt; flag in the &lt;em&gt;DeviceClass&lt;/em&gt; of your device if your device cannot be plugged in arbitrary machines. But device introspection can still happen at any time, with any machine. So if you wrote a device called &amp;ldquo;mydev-a&amp;rdquo; that only works with &lt;code&gt;--machine A&lt;/code&gt;, the user still can start QEMU with the option &lt;code&gt;--machine B&lt;/code&gt; instead and then run &lt;code&gt;device_add mydev-a,help&lt;/code&gt; or the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command. The &lt;em&gt;instance_init()&lt;/em&gt; function of your device will be called to create a temporary instance of your device, even though the base machine is B and not A here. So you especially should take care to not depend on the availability of certain buses or other devices in the &lt;em&gt;instance_init()&lt;/em&gt; function, nor use things like &lt;em&gt;serial_hd()&lt;/em&gt; or &lt;em&gt;nd_table[]&lt;/em&gt; in your &lt;em&gt;instance_init()&lt;/em&gt; function, since these might (and should) have been used by the machine init function already. If your device needs to be wired up, provide properties as interfaces to the outside and let the creator of your device (e.g. the machine init code) wire your device between the device instantiation and the realize phase instead.&lt;/li&gt;
&lt;li&gt;Make sure that your device leaves a clean state after a temporary instance is destroyed again, i.e. don&amp;rsquo;t assume that there will be only one instance of your device which is created at the beginning right after QEMU has been started and is destroyed at the very end before QEMU terminates. Thus do not assume that the things that you do in your &lt;em&gt;instance_init()&lt;/em&gt; don&amp;rsquo;t need explicit clean-up since the device instance will only be destroyed when QEMU terminates. Device instances can be created and destroyed at any time, so when the device is finalized, you must not leave any dangling pointers or references to your device behind you, e.g. in the QOM tree. When you create other objects in your &lt;em&gt;instance_init()&lt;/em&gt; function, make sure to set proper parents of these objects or use an &lt;em&gt;instance_finalize()&lt;/em&gt; function, so that the created objects get cleaned up correctly again when your device is destroyed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All in all, if you write code for a new QEMU device, it is likely a good idea to use the &lt;em&gt;instance_init()&lt;/em&gt; function only for e.g. creating properties and other things that are required before device realization, and then do the main work in the &lt;em&gt;realize()&lt;/em&gt; function instead.&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>转载自<a href="https://github.com/huth">huth (Thomas Huth)</a>的一篇文章，原文已经 404，从网页快照中找回的文章。</p>
<p>Note that this is a blog post for (new) QEMU developers. If you are just interested in using QEMU, you can certainly skip this text. Otherwise, in case you have ever been in touch with the QEMU device model (&ldquo;qdev&rdquo;), you are likely aware of the basic qdev code boilerplate already:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_realize</span><span class="p">(</span><span class="n">DeviceState</span> <span class="o">*</span><span class="n">dev</span><span class="p">,</span> <span class="n">Error</span> <span class="o">**</span><span class="n">errp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_instance_init</span><span class="p">(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">Property</span> <span class="n">mydev_properties</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">DEFINE_PROP_xxx</span><span class="p">(</span><span class="s">&#34;myprop&#34;</span><span class="p">,</span> <span class="n">MyDevState</span><span class="p">,</span> <span class="n">field</span><span class="p">,</span> <span class="p">...),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">DEFINE_PROP_END_OF_LIST</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_class_init</span><span class="p">(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">oc</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DeviceClass</span> <span class="o">*</span><span class="n">dc</span> <span class="o">=</span> <span class="nf">DEVICE_CLASS</span><span class="p">(</span><span class="n">oc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">realize</span> <span class="o">=</span> <span class="n">mydev_realize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">desc</span> <span class="o">=</span> <span class="s">&#34;My cool device&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">props</span> <span class="o">=</span> <span class="n">mydev_properties</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">mydev_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span>          <span class="o">=</span> <span class="n">TYPE_MYDEV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span>        <span class="o">=</span> <span class="n">TYPE_SYS_BUS_DEVICE</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mydev_state</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">mydev_instance_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_init</span>    <span class="o">=</span> <span class="n">mydev_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_register_types</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">type_register_static</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mydev_info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">type_init</span><span class="p">(</span><span class="n">mydev_register_types</span><span class="p">)</span>
</span></span></code></pre></div><p>There are three different initialization functions involved here, the <strong>class_init</strong>, the <strong>instance_init</strong> and the <strong>realize</strong> function. While it is quite obvious to distinguish the <em>class_init</em> function from the two others (it is used for initializing the class data, not the data that is used for an instance … this is similar to the object model with classes and instances in C++), I initially always wondered about the difference between the <em>instance_init()</em> and the <em>realize()</em> functions. Having fixed quite a lot of related bugs in the past months in the QEMU code base, I now know that a lot of other people are also not properly aware of the difference here, so I think it is now time to write down some information that I&rsquo;m now aware of, to make sure that I don&rsquo;t forget about this again, and maybe help others to avoid related bugs in the future ;-)</p>
<p>First it is of course always a good idea to have a look at the documentation. While the <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427">documentation of </a><a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427"><em>TypeInfo</em></a> (where <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l434"><em>instance_init()</em></a> is defined) is not very helpful to understand the differences, the <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40">documentation of </a><a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40"><em>DeviceClass</em></a> (where <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l43"><em>realize()</em></a> is defined) has some more useful information: You can learn here that the object instantiation is done first, before the device is realized, i.e. the <em>instance_init()</em> function is called first, and the <em>realize()</em> function is called afterwards. The former must not fail, while the latter can return an error to its caller via a pointer to an <em>“Error”</em> object pointer.</p>
<p>So the basic idea here is that device objects are first instantiated, then these objects can be inspected for their interfaces and their creators can set up their properties to configure their settings and wire them up with other devices, before the device finally becomes &ldquo;active&rdquo; by being <em>realized</em>. It is important here to notice that <strong>devices can be instantiated (and also finalized) <em><strong><strong>without</strong></strong></em> being realized</strong>! This happens for example if the device is introspected: If you enter for example <code>device_add xyz,help</code> at the HMP monitor, or if you send the <code>device-list-properties</code> QOM command to QEMU to retrieve the device&rsquo;s properties, QEMU creates a temporary instance of the device to query the properties of the object, without realizing it. The object gets destroyed (&ldquo;finalized&rdquo;) immediately afterwards.</p>
<p>Knowing this, you can avoid a set of bugs which could be found with a couple of devices in the past:</p>
<ul>
<li>If you want your device to provide properties for other parts of the QEMU code or for the users, and you want to add those properties via one of the many object_property_add*() functions of QEMU (instead of using the <em>“props”</em> field of the <em>DeviceClass</em>), then you should do this in the <em>instance_init()</em> and not in the <em>realize()</em> function. Otherwise the properties won&rsquo;t show up when the user runs <code>--device xyz,help</code> or the <code>device-list-properties</code> QOM command to get some information about your device.</li>
<li><em>instance_init()</em> functions must really never fail, i.e. also not call <em>abort()</em> or <em>exit()</em>. Otherwise QEMU can terminate unexpectedly when a user simply wanted to have a look at the list of device properties with <code>device_add xyz,help</code> or the <code>device-list-properties</code> QOM command. If your device cannot work in certain circumstances, check for the error condition in the <em>realize()</em> function instead and return with an appropriate error there.</li>
<li>Never assume that your device is always instantiated only with the machine that it was designed for. It&rsquo;s of course a good idea to set the <em>“user_creatable = false”</em> flag in the <em>DeviceClass</em> of your device if your device cannot be plugged in arbitrary machines. But device introspection can still happen at any time, with any machine. So if you wrote a device called &ldquo;mydev-a&rdquo; that only works with <code>--machine A</code>, the user still can start QEMU with the option <code>--machine B</code> instead and then run <code>device_add mydev-a,help</code> or the <code>device-list-properties</code> QOM command. The <em>instance_init()</em> function of your device will be called to create a temporary instance of your device, even though the base machine is B and not A here. So you especially should take care to not depend on the availability of certain buses or other devices in the <em>instance_init()</em> function, nor use things like <em>serial_hd()</em> or <em>nd_table[]</em> in your <em>instance_init()</em> function, since these might (and should) have been used by the machine init function already. If your device needs to be wired up, provide properties as interfaces to the outside and let the creator of your device (e.g. the machine init code) wire your device between the device instantiation and the realize phase instead.</li>
<li>Make sure that your device leaves a clean state after a temporary instance is destroyed again, i.e. don&rsquo;t assume that there will be only one instance of your device which is created at the beginning right after QEMU has been started and is destroyed at the very end before QEMU terminates. Thus do not assume that the things that you do in your <em>instance_init()</em> don&rsquo;t need explicit clean-up since the device instance will only be destroyed when QEMU terminates. Device instances can be created and destroyed at any time, so when the device is finalized, you must not leave any dangling pointers or references to your device behind you, e.g. in the QOM tree. When you create other objects in your <em>instance_init()</em> function, make sure to set proper parents of these objects or use an <em>instance_finalize()</em> function, so that the created objects get cleaned up correctly again when your device is destroyed.</li>
</ul>
<p>All in all, if you write code for a new QEMU device, it is likely a good idea to use the <em>instance_init()</em> function only for e.g. creating properties and other things that are required before device realization, and then do the main work in the <em>realize()</em> function instead.</p>
]]></content:encoded>
    </item>
    <item>
      <title>ZH-UEFI 规范 -1-引言</title>
      <link>https://lifeislife.cn/posts/zh-uefi%E8%A7%84%E8%8C%83-1-%E5%BC%95%E8%A8%80/</link>
      <pubDate>Tue, 18 Oct 2022 20:07:10 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/zh-uefi%E8%A7%84%E8%8C%83-1-%E5%BC%95%E8%A8%80/</guid>
      <description>&lt;h1 id=&#34;引言&#34;&gt;引言&lt;/h1&gt;
&lt;p&gt;统一可扩展固件接口 (UEFI) 规范描述了操作系统和平台固件之间的接口。UEFI 之前是可扩展固件接口规范 1.10 (EFI)。因此，一些代码和某些协议名称保留了 EFI 名称。除非另有说明，本规范中的 EFI 名称可假定为 UEFI 的一部分。&lt;/p&gt;
&lt;p&gt;该接口采用数据表的形式，其中包含与平台相关的信息，以及可供 OS 加载程序和 OS 使用的引导和运行时服务调用。它们共同提供了一个引导操作系统的标准环境。本规范是作为一个纯粹的接口规范设计的。因此，&lt;strong&gt;该规范定义了平台固件必须实现的接口和结构集&lt;/strong&gt;。类似地，该规范定义了操作系统在引导时可能使用的一组接口和结构。无论是固件开发者选择如何实现所需的元素，还是操作系统开发者选择如何利用这些接口和结构，都由开发者自己决定。&lt;/p&gt;
&lt;p&gt;该规范的目的是定义一种方法，使操作系统和平台固件仅通信支持操作系统引导过程所必需的信息。这是通过平台和固件提供给操作系统的软件可见接口的正式和完整的抽象规范来实现的。&lt;/p&gt;
&lt;p&gt;本规范的目的是为操作系统和平台固件定义一种方式，以仅传递支持操作系统启动过程所需的信息。这是通过平台和固件呈现给操作系统的软件可见接口的抽象规范来实现的。&lt;/p&gt;
&lt;p&gt;使用这一正式定义，旨在运行在与受支持的处理器规范兼容的平台上的收缩包装操作系统将能够在各种系统设计上启动，而无需进一步的平台或操作系统定制。该定义还允许平台创新引入新特性和功能，以增强平台的能力，而不需要按照操作系统的引导顺序编写新代码。&lt;/p&gt;
&lt;p&gt;此外，抽象规范开辟了一条替代遗留设备和固件代码的路径。新的设备类型和相关代码可以通过相同定义的抽象接口提供同等的功能，同样不会影响 OS 引导支持代码。&lt;/p&gt;
&lt;p&gt;该规范适用于从移动系统到服务器的所有硬件平台。该规范提供了一组核心服务以及一组协议接口。协议接口的选择可以随着时间的推移而发展，并针对不同的平台市场细分进行优化。与此同时，该规范允许 oem 提供最大限度的可扩展性和定制能力，以实现差异化。在这方面，UEFI 的目的是定义一个从传统的“PC-AT”风格的引导世界到一个没有遗留 API 的环境的进化路径。&lt;/p&gt;
&lt;h2 id=&#34;uefi-驱动模型扩展&#34;&gt;UEFI 驱动模型扩展&lt;/h2&gt;
&lt;p&gt;对启动设备的访问是通过一系列的协议接口提供的。UEFI 驱动模型的一个目的是为 &amp;ldquo;PC-AT&amp;quot;式的 Option ROM（TODO）提供一个替代品。需要指出的是，写在 UEFI 驱动模型上的驱动，被设计为在预启动环境中访问启动设备。它们并不是为了取代高性能的、针对操作系统的驱动程序。&lt;/p&gt;
&lt;p&gt;UEFI 驱动模型被设计为支持执行模块化的代码，也被称为驱动，在预启动环境中运行。这些驱动程序可以管理或控制平台上的硬件总线和设备，也可以提供一些软件衍生的、平台特定的服务。&lt;/p&gt;
&lt;p&gt;UEFI 驱动模型还包含了 UEFI 驱动编写者所需的信息，以设计和实现平台启动 UEFI 兼容的操作系统可能需要的任何总线驱动和设备驱动的组合。&lt;/p&gt;
&lt;p&gt;UEFI 驱动模型被设计为通用的，可以适应任何类型的总线或设备。UEFI 规范描述了如何编写 PCI 总线驱动程序、PCI 设备驱动程序、USB 总线驱动程序、USB 设备驱动程序和 SCSI 驱动程序。提供了允许将 UEFI 驱动程序存储在 PCI Option ROM 中的其他详细信息，同时保持了与旧 Option ROM 镜像的兼容性。&lt;/p&gt;
&lt;p&gt;UEFI 规范的一个设计目标是使驱动镜像尽可能的小。然而，如果一个驱动程序需要支持多个处理器架构，那么也需要为每个支持的处理器架构提供一个驱动程序对象文件。为了解决这个空间问题，本规范还定义了 EFI 字节代码虚拟机（EFI Byte Code Virtual Machine）。一个 UEFI 驱动可以被编译成一个 EFI 字节代码对象文件。UEFI Specification-complaint（TODO）的固件必须包含一个 EFI 字节代码解释器。这使得支持多种处理器架构的单一 EFI 字节代码对象文件可以被运出。另一种节省空间的技术是使用压缩。该规范定义了压缩和解压算法，可用于减少 UEFI 驱动程序的大小，从而减少 UEFI 驱动程序存储在 ROM 设备中时的开销。&lt;/p&gt;
&lt;p&gt;OSV、IHV、OEM 和固件供应商可以使用 UEFI 规范中包含的信息来设计和实现符合本规范的固件、生成标准协议接口的驱动程序以及可用于引导 UEFI 兼容的操作系统加载程序操作系统。&lt;/p&gt;
&lt;h2 id=&#34;章节安排&#34;&gt;章节安排&lt;/h2&gt;
&lt;p&gt;本规范的章节组织如下：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;章节名&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;内容&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;引言/概述&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;介绍 UEFI 规范，并描述 UEFI 的主要组件。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;启动管理器&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;管理器用于加载写入此规范的驱动程序和应用程序。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;EFI 系统表和分区&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;描述了一个 EFI 系统表，它被传递给每个兼容的驱动程序和应用程序，并定义了一个基于 GUID 的分区方案。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;块转换表&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;用于执行块 I/O 的布局和规则集，可提供单个块的断电写入原子性。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;引导服务&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;包含在引导操作系统之前存在于 UEFI 兼容系统中的基本服务的定义。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;运行时服务&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;包含在操作系统启动之前和之后存在于兼容系统中的基本服务的定义。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;协议&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;EFI 加载图像协议描述已加载到内存的 UEFI 镜像。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;设备路径协议提供了在 UEFI 环境中构建和管理设备路径所需的信息。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;UEFI 驱动模型描述了一组服务和协议，适用于每个总线和设备类型。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;控制台支持协议定义了I/O协议，处理系统用户在启动服务环境中执行的基于文本的信息的输入和输出。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;媒介访问协议定义了加载文件协议，文件系统格式和媒介格式处理可移动媒介。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;PCI 总线支持协议定义 PCI 总线驱动程序，PCI 设备驱动程序和 PCI Option ROM 布局。所描述的协议包括 PCI 根桥 I/O 协议和 PCI I/O协议。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;SCSI 驱动程序模型和总线支持定义了 SCSI I/O协议和扩展SCSI Pass Thru 协议，用于抽象访问由 SCSI 主机控制器产生的 SCSI 通道。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;iSCSI协议定义了通过TCP/IP传输SCSI数据。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;USB 支持协议定义了 USB 总线驱动程序和 USB 设备驱动程序。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;调试器支持协议描述了一组可选的协议，提供所需的服务，以实现一个源级调试器的 UEFI 环境。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;压缩算法规范详细描述了压缩/解压缩算法，外加一个标准的EFI解压缩接口，用于启动时使用。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;ACPI 协议可用于从平台上安装或删除 ACPI 表。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;字符串服务：Unicode 排序协议允许在启动服务环境中运行的代码对给定语言的 Unicode 字符串执行词法比较函数;正则表达式协议用于根据正则表达式模式匹配 Unicode 字符串。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;EFI 字节码虚拟机&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;定义 EFI 字节码虚拟处理器及其指令集。它还定义了如何将 EBC 对象文件加载到内存中，以及从本机代码到 EBC 代码再转换到本机代码的机制。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;固件更新和报告&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;为设备提供一个抽象，以提供固件管理支持。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;网络协议&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;SNP、PXE、BIS 和 HTTP 启动协议定义了在 UEFI 启动服务环境中执行时提供对网络设备访问的协议。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;受管网络协议定义了 EFI 受管网络协议，它提供原始 (未格式化) 异步网络数据包 I/O 服务和托管网络服务绑定协议，用于定位 MNP 驱动支持的通信设备。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;VLAN、EAP、Wi-Fi 和 Supplicant 协议定义了一个协议，为 VLAN 配置提供可管理性接口。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;蓝牙协议定义。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;TCP、IP、PIPsec、FTP、GTLS 和 Configurations 协议定义了 EFI TCPv4 (Transmission Control Protocol version 4) 协议和 EFI IPv4 (Internet Protocol version 4) 协议。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;ARP、DHCP、DNS、HTTP 和 REST 协议定义了 ARP (Address Resolution Protocol) 协议接口和 EFI DHCPv4 协议。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;UDP 和 MTFTP 协议定义了 EFI UDPv4 (User Datagram Protocol version 4) 协议，该协议在 EFI IPv4 协议上接口，并定义了 EFI MTFTPv4 协议接口，该接口建立在 EFI UDPv4 协议之上。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;安全引导和驱动程序签名&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;介绍 Secure Boot 和生成 UEFI 数字签名的方法。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;人机界面基础设施 (HII)&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;定义实现人机接口基础设施 (HII) 所需的核心代码和服务，包括管理用户输入和相关协议的代码定义的基本机制。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;描述用于管理系统配置的数据和 api:描述旋钮和设置的实际数据。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;用户标识&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;描述描述平台当前用户的服务。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;安全技术&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;描述用于利用安全技术的协议，包括加密散列和密钥管理。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;杂项协议&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Timestamp 协议提供了一个独立于平台的接口来检索高分辨率的时间戳计数器。当调用 ResetSystem 时，重置通知协议提供注册通知的服务。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;附录&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;GUID 和时间格式。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;基于基本文本的控制台要求，符合 efi 系统需要提供通信能力。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;设备路径使用数据结构的例子，定义各种硬件设备的引导服务。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;状态代码列出了 UEFI 接口返回的成功、错误和警告代码。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;通用网络驱动程序接口定义了32/64位硬件和软件通用网络驱动程序接口(UNDIs)。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;使用简单指针协议。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;使用 EFI 扩展 SCISI 直通协议。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;压缩源代码的一个压缩算法的实现。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;一个 EFI 解压缩算法的实现的解压源代码。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;EFI 字节码虚拟机操作码列表提供了相应指令集的摘要。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;字母功能列表按字母顺序标识所有 UEFI 接口功能。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;EFI 1.10 协议变更和折旧清单标识了协议、GUID、修订标识符名称变更以及与 EFI 1.10 规范相比已弃用的协议。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;平台错误记录描述了用于表示平台硬件错误的常见平台错误记录格式。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;UEFI ACPI Data Table 定义了 UEFI ACPI 表格式。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;硬件错误记录持久性使用。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;引用&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;术语表&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;索引&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;提供规范中关键术语和概念的索引。&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;目标&#34;&gt;目标&lt;/h2&gt;
&lt;p&gt;“PC-AT”启动环境对行业内的创新提出了重大挑战。每一个新的平台功能或硬件创新都要求固件开发人员设计越来越复杂的解决方案，并且通常要求操作系统开发人员修改引导代码，然后客户才能从创新中受益。这可能是一个耗时的过程，需要大量的资源投资。&lt;/p&gt;
&lt;p&gt;UEFI 规范的主要目标是定义一个替代引导环境，可以减轻这些考虑。在这个目标中，该规范类似于其他现有的引导规范。本规范的主要属性可以概括为以下属性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;一致的、可扩展的平台环境&lt;/strong&gt;。该规范为固件定义了一个完整的解决方案，以描述所有平台特性和 OS 的 surface platform(TODO) 功能在引导过程中。这些定义非常丰富，足以涵盖一系列当代处理器设计。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;从固件中抽象操作系统&lt;/strong&gt;。该规范定义了平台功能的接口。通过使用抽象接口，该规范允许在构建 OS 加载器时，而无需了解作为这些接口基础的平台和固件。这些接口代表了底层平台和固件实现与操作系统加载程序之间定义良好的稳定边界。这样的边界允许底层固件和操作系统加载程序更改，前提是两者都将交互限制在定义的接口上。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;合理的设备抽象，不需要遗留接口&lt;/strong&gt;。“PC-AT”BIOS 接口要求操作系统加载程序对某些硬件设备的工作有特定的了解。该规范为 OS 加载器开发人员提供了一些不同的东西：抽象接口使得可以构建在一系列底层硬件设备上工作的代码，而无需明确了解该范围内每个设备的细节。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;从固件中提取 Option ROM&lt;/strong&gt;。该规范定义了平台功能的接口，包括 PCI、USB 和 SCSI 等标准总线类型。支持的总线类型可能会随着时间的推移而增加，因此包括了一种扩展到未来总线类型的机制。这些定义的接口以及扩展到未来总线类型的能力是 UEFI 驱动程序模型的组件。UEFI 驱动模型的一个目的是解决现有“PC-AT”Option ROM 中存在的广泛问题。与 OS 加载程序一样，驱动程序使用抽象接口，因此可以构建设备驱动程序和总线驱动程序，而无需了解作为这些接口基础的平台和固件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;架构上可共享的系统分区&lt;/strong&gt;。扩展平台功能和添加新设备的计划通常需要软件支持。在许多情况下，当这些平台创新（TODO）在操作系统控制平台之前被激活时，它们必须由特定于平台而不是客户选择的操作系统的代码支持。解决这个问题的传统方法是在制造过程中将代码嵌入平台中（例如，在闪存设备中）。对这种持久存储的需求正在快速增长。该规范定义了大型海量存储媒介类型上的持久存储，以供平台支持代码扩展使用，以补充传统方法。规范中明确了其工作原理的定义，以确保固件开发商、OEM、操作系统供应商甚至第三方可以安全地共享空间，同时增加平台功能。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以通过多种方式定义提供这些属性的引导环境。实际上，在编写本规范时，已经存在几种替代方案，从学术角度来看可能是可行的。然而，考虑到当前围绕支持的处理器平台的基础设施能力，这些替代方案通常会带来很高的门槛。本规范旨在提供上面列出的属性，同时也认识到行业的独特需求，该行业在兼容性方面进行了大量投资，并且拥有大量无法立即放弃的系统安装基础。这些需求推动了对本规范中体现的附加属性的要求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;进化性的，而不是革命性的&lt;/strong&gt;。规范中的接口和结构旨在尽可能地减少初始实现的负担。虽然已经小注意保在接口本身中维护适当的抽象，但该设计还确保可以重用 BIOS 代码来实现接口，而只需要最少的额外编码工作。换句话说，在 PC-AT 平台上，规范最初可以作为基于现有代码的底层实现之上的薄接口（thin Interface TODO）层来实现。同时，抽象接口的引入提供了将来从遗留代码的迁移。一旦抽象被确立为固件和操作系统加载程序在引导期间交互的手段，开发人员就可以随意替换抽象接口下的遗留代码。类似的硬件遗留迁移也是可能的。由于抽象隐藏了设备的细节，因此可以移除底层硬件，并用提供改进功能、降低成本或两者兼而有之的新硬件替换它。显然，这需要编写新的平台固件来支持设备并通过抽象接口将其呈现给 OS 加载器。但是，如果没有接口抽象，则可能根本无法移除旧设备。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设计上的兼容性&lt;/strong&gt;。系统分区结构的设计还保留了当前在“PC-AT”引导环境中使用的所有结构。因此，构建一个能够从同一磁盘引导传统操作系统或 EFI-aware 操作系统的单一系统是一件简单的事情。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;简化了操作系统中立的平台增值的添加&lt;/strong&gt;。该规范定义了一个开放的、可扩展的接口，它有助于创建平台“驱动程序”。这些可能类似于操作系统驱动程序，在引导过程中为新设备类型提供支持，或者它们可能用于实现增强的平台功能，例如容错或安全性。此外，这种扩展平台能力的能力从一开始就被设计到规范中。这旨在帮助开发人员避免在尝试将新代码挤入传统 BIOS 环境时所固有的许多挫败感。由于包含用于添加新协议的接口，OEM 或固件开发人员拥有以模块化方式向平台添加功能的基础设施。由于规范中定义的调用约定和环境，此类驱动程序可能会使用高级编码语言来实现。这反过来可能有助于降低创新的难度和成本。系统分区选项为此类扩展提供了非易失性存储器存储的替代方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;建立在现有投入的基础上&lt;/strong&gt;。在可能的情况下，规范避免在现有行业规范提供足够覆盖的领域重新定义接口和结构。例如，ACPI 规范为操作系统提供了发现和配置平台资源所需的所有信息。同样，规范设计的这种哲学选择旨在尽可能降低采用该规范的障碍。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;目标受众&#34;&gt;目标受众&lt;/h2&gt;
&lt;p&gt;本文档主要适用于以下读者：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将实现 UEFI 驱动程序的 IHV 和 OEM。&lt;/li&gt;
&lt;li&gt;将创建支持的处理器平台的 OEM 厂商，旨在启动 shrink-wrap（TODO）的操作系统。&lt;/li&gt;
&lt;li&gt;BIOS 开发人员，无论是创建通用 BIOS 和其他固件产品的人员，还是修改这些产品的支持人员。&lt;/li&gt;
&lt;li&gt;操作系统开发人员将调整他们的 shrink-wrap（TODO）操作系统产品，用来在支持的基于处理器的平台上运行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;uefi-设计概述&#34;&gt;UEFI 设计概述&lt;/h2&gt;
&lt;p&gt;UEFI 的设计基于以下基本要素：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;重用现有的基于表格的接口&lt;/strong&gt;。为了保持对现有基础支持代码（包括操作系统和固件）的投资，必须在希望符合 UEFI 规范的平台上，实现通常在与支持的处理器规范兼容的平台上，实现的许多现有规范。 （有关更多信息，请参阅附录 Q：参考资料。）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系统分区&lt;/strong&gt;。系统分区定义了一个分区和文件系统，可允许多个供应商之间安全共享，并用于不同目的。包含单独的、可共享的系统分区的能力提供了增加平台附加值的机会，而不会显著增加对非易失性平台存储器的需求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引导服务&lt;/strong&gt;。引导服务为可在引导期间使用的设备和系统功能提供接口。设备访问是通过“句柄”（handles）和“协议”(protocols) 抽象出来的。这有利于重用现有 BIOS 代码，将基本实现要求保持在规范之外，而不会给访问设备的消费者带来负担。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时服务&lt;/strong&gt;。提供了一组最小的运行时服务，以确保对基础平台的硬件资源进行适当的抽象，这些资源可能是操作系统在正常运行时需要的。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&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;../pic/1-1.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;../pic/1-1.jpg&#34; alt=&#34;UEFI 概念概述&#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;hr&gt;
&lt;p&gt;图 1-1 描述了用于完成平台和操作系统引导的符合 UEFI 规范的系统的各种组件的交互。&lt;/p&gt;
&lt;p&gt;平台固件能够从系统分区中检索操作系统加载器镜像。该规范提供了各种大容量存储设备类型，包括磁盘、CD-ROM 和 DVD，以及通过网络的远程启动。通过可扩展的协议接口，有可能增加其他的引导媒介类型，尽管如果这些媒介需要使用本文件中定义的协议以外的协议，可能需要修改操作系统加载器。&lt;/p&gt;
&lt;p&gt;一旦启动，操作系统加载程序将继续引导整个操作系统。为此，它可以使用本规范或其他所需规范定义的 EFI 引导服务和接口来探测、解析和初始化各种平台组件和管理它们的操作系统软件。在引导阶段，EFI 运行时服务也可供 OS 加载器使用。&lt;/p&gt;
&lt;h2 id=&#34;uefi-驱动模型&#34;&gt;UEFI 驱动模型&lt;/h2&gt;
&lt;p&gt;本节描述了符合本规范的固件的驱动模型的目标。目标是让这个驱动模型为所有类型的总线和设备提供一个实现总线驱动和设备驱动的机制。在撰写本文时，支持的总线类型包括 PCI、USB 等。&lt;/p&gt;
&lt;p&gt;随着硬件架构的不断发展，平台中存在的总线数量和类型也在不断增加。这种趋势在高端服务器中尤为明显。然而，更多样化的总线类型被设计到桌面和移动系统，甚至一些嵌入式系统中。这种日益增长的复杂性，意味着在预启动环境中，需要一种简单的方法来描述和管理平台中的所有总线和设备。UEFI 驱动模型以协议、服务和启动服务的形式提供了这种简单的方法。&lt;/p&gt;
&lt;h3 id=&#34;uefi-驱动程序模型目标&#34;&gt;UEFI 驱动程序模型目标&lt;/h3&gt;
&lt;p&gt;UEFI 驱动模型有以下目标：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;兼容&lt;/strong&gt; – 符合此规范的驱动程序必须保持与 EFI 1.10 规范和 UEFI 规范的兼容性。这意味着 UEFI 驱动程序模型利用 UEFI 2. 0 规范中的可扩展性机制来添加所需的功能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;简单&lt;/strong&gt; - 符合本规范的驱动程序必须易于实现，易于维护。UEFI 驱动模型必须允许驱动编写者专注于正在开发的特定设备的驱动。驱动程序不应关注平台策略或平台管理问题。这些考虑应该留给系统固件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性&lt;/strong&gt; - UEFI 驱动模型必须能够适应所有类型的平台。这些平台包括嵌入式系统、移动和桌面系统，以及工作站和服务器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;灵活&lt;/strong&gt; - UEFI 驱动模型必须支持枚举所有设备的能力，或者只枚举启动所需操作系统的那些设备。最小的设备枚举提供了对更快速的启动能力的支持，而完整的设备媒体提供了在系统中存在的任何启动设备上执行操作系统安装、系统维护或系统诊断的能力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性&lt;/strong&gt; - UEFI 驱动模型必须能够扩展到未来定义的总线类型。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可移植性&lt;/strong&gt; - 根据 UEFI 驱动模型编写的驱动，必须在不同平台和支持的处理器架构之间可移植。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可互操作性&lt;/strong&gt; - 驱动程序必须与其他驱动程序和系统固件共存，并且必须在不产生资源冲突的情况下进行操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;描述复杂的总线层次结构&lt;/strong&gt; - UEFI 驱动模型必须能够描述各种总线拓扑结构，从非常简单的单总线平台到包含许多不同类型总线的非常复杂的平台。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;驱动占用空间小&lt;/strong&gt; - 由 UEFI 驱动程序模型产生的可执行文件的大小必须最小化，以减少整体平台成本。虽然灵活性和可扩展性是目标，但支持这些所需的额外开销必须保持在最低水平，以防止固件组件的大小变得无法管理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解决遗留 Option ROM 的问题&lt;/strong&gt; - UEFI 驱动模型必须直接解决遗留 Option ROM 的约束和限制。具体来说，必须能够建立同时支持 UEFI 驱动和传统 Option ROM 的插件卡，这种卡可以在传统 BIOS 系统和符合 UEFI 的平台上执行，而无需修改卡上的代码。该解决方案必须提供一个从传统 Option ROM 驱动程序迁移到 UEFI 驱动程序的进化路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;遗留-option-rom-问题&#34;&gt;遗留 Option ROM 问题&lt;/h3&gt;
&lt;p&gt;这个支持驱动模型的想法来自于对 UEFI 规范的反馈，它提供了一个明确的、由市场驱动的对传统选项 ROM（有时也被称为扩展 ROM）的替代要求。人们认为，UEFI 规范的出现代表了一个机会，通过用一种在 UEFI 规范框架内工作的替代机制来取代传统选项 ROM 镜像的构建和操作，从而摆脱隐含的限制。&lt;/p&gt;
&lt;h2 id=&#34;迁移要求&#34;&gt;迁移要求&lt;/h2&gt;
&lt;p&gt;迁移要求涵盖了从最初实施本规范到未来所有平台和操作系统都实施本规范的过渡时期。在这一时期，有两个主要的兼容性考虑是很重要的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;能够继续启动传统的操作系统；&lt;/li&gt;
&lt;li&gt;能够在现有的平台上实现 UEFI，尽可能多地复用现有的固件代码，将开发资源和时间要求降到最低。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;旧版操作系统支持&#34;&gt;旧版操作系统支持&lt;/h3&gt;
&lt;p&gt;UEFI 规范代表了收缩式操作系统和固件在启动过程中进行通信的首选方式。然而，选择制作一个符合该规范的平台，并不排除该平台，也支持不了解 UEFI 规范的，现有传统操作系统二进制文件。&lt;/p&gt;
&lt;p&gt;UEFI 规范并不限制平台设计者，选择同时支持 UEFI 规范和更传统的 &amp;ldquo;PC-AT &amp;ldquo;启动基础架构。如果要实现这样的传统基础架构，应该按照现有的行业惯例来开发，这些惯例是在本规范范围之外定义的。在任何给定的平台上，支持的传统操作系统的选项是由该平台的制造商决定的。&lt;/p&gt;
&lt;h3 id=&#34;在旧平台上支持-uefi-规范&#34;&gt;在旧平台上支持 UEFI 规范&lt;/h3&gt;
&lt;p&gt;UEFI 规范经过精心设计，允许以最少的开发工作扩展现有系统以支持它。特别是 UEFI 规范中定义的抽象结构和服务，都可以在遗留平台上得到支持&lt;/p&gt;
&lt;p&gt;例如，要在现有且受支持的基于 32 位的平台上实现此类支持，该平台使用传统 BIOS 来支持操作系统启动，需要提供额外的固件代码层。需要这些额外的代码来将服务和设备的现有接口转换为对本规范中定义的抽象的支持。&lt;/p&gt;
&lt;h2 id=&#34;本文档中使用的约定&#34;&gt;本文档中使用的约定&lt;/h2&gt;
&lt;h3 id=&#34;数据结构描述&#34;&gt;数据结构描述&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;支持的处理器是“小端”机器&lt;/strong&gt;。这种区别意味着内存中多字节数据项的低位字节位于最低地址，而高位字节位于最高地址。一些受支持的 64 位处理器可以配置为“小端”和“大端”操作。所有旨在符合本规范的实现都使用“小端”操作。&lt;/p&gt;
&lt;p&gt;在某些内存布局描述中，某些字段被标记为保留。软件必须将这些字段初始化为零并在读取时忽略它们。在更新操作中，软件必须保留任何保留字段。&lt;/p&gt;
&lt;h3 id=&#34;协议描述&#34;&gt;协议描述&lt;/h3&gt;
&lt;p&gt;协议描述一般有以下格式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;协议名称&lt;/strong&gt;：协议接口的正式名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;摘要&lt;/strong&gt;：协议接口的简要描述。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GUID&lt;/strong&gt;：协议接口的 128 位 GUID (Globally Unique Identifier)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协议接口结构&lt;/strong&gt;：一种“c 风格”的数据结构定义，包含由该协议接口产生的过程和数据字段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：协议接口结构中各字段的简要说明。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;描述&lt;/strong&gt;：对接口提供的功能的描述，包括调用者应该知道的任何限制和警告。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;相关定义&lt;/strong&gt;：协议接口结构或其任何过程中使用的类型声明和常量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;过程描述&#34;&gt;过程描述&lt;/h3&gt;
&lt;p&gt;过程描述通常具有以下格式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;过程名称&lt;/strong&gt;：过程的正式名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;摘要&lt;/strong&gt;：过程的简要说明。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：定义调用序列的“C 风格”过程标头。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：对程序原型中每个字段的简要描述。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;描述&lt;/strong&gt;：对接口所提供的功能的描述，包括调用者应该注意的任何限制和注意事项。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;相关定义&lt;/strong&gt;：仅由该过程使用的类型声明和常量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回的状态代码&lt;/strong&gt;：对接口所返回的任何代码的描述。该过程需要实现本表中列出的任何状态代码。可以返回更多的错误代码，但是它们不会被标准的符合性测试所测试，而且任何使用该程序的软件，都不能依赖于实现可能提供的任何扩展错误代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;指令描述&#34;&gt;指令描述&lt;/h3&gt;
&lt;p&gt;EBC 指令的指令描述一般有以下格式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;指令名称&lt;/strong&gt;：指令的正式名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;语法&lt;/strong&gt;：指令的简要描述。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;描述&lt;/strong&gt;：对指令所提供的功能的描述，并附有指令编码的详细表格。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;操作&lt;/strong&gt;：详细说明对操作数进行的操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行为和限制&lt;/strong&gt;：逐项描述指令中涉及的每个操作数的行为，以及适用于操作数或指令的任何限制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;伪代码约定&#34;&gt;伪代码约定&lt;/h3&gt;
&lt;p&gt;提出伪代码是为了以更简洁的形式描述算法。本文件中的所有算法都不打算直接进行编译。代码是在与周围文本相对应的水平上呈现的。&lt;/p&gt;
&lt;p&gt;在描述变量时，列表是一个无序的同质对象的集合。一个队列是一个同质对象的有序列表。除非另有说明，否则假设排序为先进先出。&lt;/p&gt;
&lt;p&gt;伪代码以类似于 C 的格式呈现，在适当的地方使用 C 约定。编码风格，特别是缩进风格，是为了可读性，不一定符合 UEFI 规范的实现。&lt;/p&gt;
&lt;h3 id=&#34;排版约定&#34;&gt;排版约定&lt;/h3&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;li&gt;斜体：在文本中，斜体字可以用作强调，以引入一个新的术语或表示手册或规范的名称。&lt;/li&gt;
&lt;li&gt;加粗等宽（暗红色）：计算机代码、示例代码段和所有原型代码段使用 BOLD Monospace 字体，颜色为暗红色。这些代码列表通常出现在一个或多个独立的段落中，尽管单词或片段也可以嵌入到一个正常的文本段落中。&lt;/li&gt;
&lt;li&gt;加粗等宽（蓝色）：用粗体单色字体的字，下划线和蓝色的字，表示该功能或类型定义的代码定义的活动超链接。点击该词，即可进入超链接。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：出于管理和文件大小的考虑，每一页上只有第一次出现的参考文献是一个主动链接。同一页上的后续参考文献不会被主动链接到定义上，而是使用标准的、无下划线的 BOLD Monospace 字体。在页面上找到该名称的第一个实例（使用下划线的 BOLD Monospace 字体），点击该词即可跳转到该功能或类型的定义。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;斜体等宽：在代码或文本中，斜体字表示必须提供的变量信息的占位符名称（即参数）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;数字格式&#34;&gt;数字格式&lt;/h3&gt;
&lt;p&gt;在本标准中，二进制数字是由仅由西方阿拉伯数字 0 和 1 组成的任何数字序列表示的，后面紧跟一个小写的 b（例如，0101b）。在二进制数字表示中的字符之间可以包含下划线或空格，以增加可读性或划分领域边界（例如，0 0101 1010b 或 0_0101_1010b）。&lt;/p&gt;
&lt;h4 id=&#34;十六进制&#34;&gt;十六进制&lt;/h4&gt;
&lt;p&gt;十六进制数字在本标准中用 0x 表示，前面是仅由西阿拉伯数字 0 至 9 和/或大写英文字母 A 至 F 组成的任何数字序列（例如，0xFA23）。十六进制数字表示中的字符之间可以包含下划线或空格，以增加可读性或划定字段边界（例如，0xB FD8C FA23 或 0xB_FD8C_FA23）。&lt;/p&gt;
&lt;h4 id=&#34;十进制&#34;&gt;十进制&lt;/h4&gt;
&lt;p&gt;在本标准中，小数是由仅由阿拉伯数字 0 到 9 组成的任何数字序列来表示的，后面不紧跟小写的 b 或小写的 h（例如，25）。本标准使用以下惯例来表示小数：&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;h3 id=&#34;二进制前缀&#34;&gt;二进制前缀&lt;/h3&gt;
&lt;p&gt;本标准使用国际单位制（SI）中定义的前缀来表示 10 的幂值。见 &amp;ldquo;SI 二进制前缀 &amp;ldquo;标题下的 &amp;ldquo;UEFI 相关文件链接&amp;rdquo;（&lt;a href=&#34;http://uefi.org/uefi&#34;&gt;http://uefi.org/uefi&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;../pic/table1-1.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;../pic/table1-1.jpg&#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;本标准使用ISO/IEC 80000-13《数量和单位&amp;ndash;第 13 部分：信息科学和技术》和 IEEE 1514《二进制倍数前缀标准》中定义的二进制前缀，用于表示 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;../pic/table1-2.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;../pic/table1-2.jpg&#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;例如，4 KB 意味着 4000 个字节，4 KiB 意味着 4096 个字节。&lt;/p&gt;
&lt;h3 id=&#34;修订号&#34;&gt;修订号&lt;/h3&gt;
&lt;p&gt;对 UEFI 规范的更新被认为是新的修订或勘误表，如下所述：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当有实质性的新内容或可能修改现有行为的变化时，就会产生一个新的修订。新的修订版由一个主要的。次要的版本号来指定（例如：xx.yy）。在变化特别小的情况下，我们可能有一个 major.minor.minor 的命名惯例（例如 xx.yy.zz）。&lt;/li&gt;
&lt;li&gt;当批准的规范更新不包括任何重要的新材料或修改现有行为时，就会产生勘误的版本。勘误的指定方法是在版本号后面加上一个大写字母，如 xx.yy 勘误 A。&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h1 id="引言">引言</h1>
<p>统一可扩展固件接口 (UEFI) 规范描述了操作系统和平台固件之间的接口。UEFI 之前是可扩展固件接口规范 1.10 (EFI)。因此，一些代码和某些协议名称保留了 EFI 名称。除非另有说明，本规范中的 EFI 名称可假定为 UEFI 的一部分。</p>
<p>该接口采用数据表的形式，其中包含与平台相关的信息，以及可供 OS 加载程序和 OS 使用的引导和运行时服务调用。它们共同提供了一个引导操作系统的标准环境。本规范是作为一个纯粹的接口规范设计的。因此，<strong>该规范定义了平台固件必须实现的接口和结构集</strong>。类似地，该规范定义了操作系统在引导时可能使用的一组接口和结构。无论是固件开发者选择如何实现所需的元素，还是操作系统开发者选择如何利用这些接口和结构，都由开发者自己决定。</p>
<p>该规范的目的是定义一种方法，使操作系统和平台固件仅通信支持操作系统引导过程所必需的信息。这是通过平台和固件提供给操作系统的软件可见接口的正式和完整的抽象规范来实现的。</p>
<p>本规范的目的是为操作系统和平台固件定义一种方式，以仅传递支持操作系统启动过程所需的信息。这是通过平台和固件呈现给操作系统的软件可见接口的抽象规范来实现的。</p>
<p>使用这一正式定义，旨在运行在与受支持的处理器规范兼容的平台上的收缩包装操作系统将能够在各种系统设计上启动，而无需进一步的平台或操作系统定制。该定义还允许平台创新引入新特性和功能，以增强平台的能力，而不需要按照操作系统的引导顺序编写新代码。</p>
<p>此外，抽象规范开辟了一条替代遗留设备和固件代码的路径。新的设备类型和相关代码可以通过相同定义的抽象接口提供同等的功能，同样不会影响 OS 引导支持代码。</p>
<p>该规范适用于从移动系统到服务器的所有硬件平台。该规范提供了一组核心服务以及一组协议接口。协议接口的选择可以随着时间的推移而发展，并针对不同的平台市场细分进行优化。与此同时，该规范允许 oem 提供最大限度的可扩展性和定制能力，以实现差异化。在这方面，UEFI 的目的是定义一个从传统的“PC-AT”风格的引导世界到一个没有遗留 API 的环境的进化路径。</p>
<h2 id="uefi-驱动模型扩展">UEFI 驱动模型扩展</h2>
<p>对启动设备的访问是通过一系列的协议接口提供的。UEFI 驱动模型的一个目的是为 &ldquo;PC-AT&quot;式的 Option ROM（TODO）提供一个替代品。需要指出的是，写在 UEFI 驱动模型上的驱动，被设计为在预启动环境中访问启动设备。它们并不是为了取代高性能的、针对操作系统的驱动程序。</p>
<p>UEFI 驱动模型被设计为支持执行模块化的代码，也被称为驱动，在预启动环境中运行。这些驱动程序可以管理或控制平台上的硬件总线和设备，也可以提供一些软件衍生的、平台特定的服务。</p>
<p>UEFI 驱动模型还包含了 UEFI 驱动编写者所需的信息，以设计和实现平台启动 UEFI 兼容的操作系统可能需要的任何总线驱动和设备驱动的组合。</p>
<p>UEFI 驱动模型被设计为通用的，可以适应任何类型的总线或设备。UEFI 规范描述了如何编写 PCI 总线驱动程序、PCI 设备驱动程序、USB 总线驱动程序、USB 设备驱动程序和 SCSI 驱动程序。提供了允许将 UEFI 驱动程序存储在 PCI Option ROM 中的其他详细信息，同时保持了与旧 Option ROM 镜像的兼容性。</p>
<p>UEFI 规范的一个设计目标是使驱动镜像尽可能的小。然而，如果一个驱动程序需要支持多个处理器架构，那么也需要为每个支持的处理器架构提供一个驱动程序对象文件。为了解决这个空间问题，本规范还定义了 EFI 字节代码虚拟机（EFI Byte Code Virtual Machine）。一个 UEFI 驱动可以被编译成一个 EFI 字节代码对象文件。UEFI Specification-complaint（TODO）的固件必须包含一个 EFI 字节代码解释器。这使得支持多种处理器架构的单一 EFI 字节代码对象文件可以被运出。另一种节省空间的技术是使用压缩。该规范定义了压缩和解压算法，可用于减少 UEFI 驱动程序的大小，从而减少 UEFI 驱动程序存储在 ROM 设备中时的开销。</p>
<p>OSV、IHV、OEM 和固件供应商可以使用 UEFI 规范中包含的信息来设计和实现符合本规范的固件、生成标准协议接口的驱动程序以及可用于引导 UEFI 兼容的操作系统加载程序操作系统。</p>
<h2 id="章节安排">章节安排</h2>
<p>本规范的章节组织如下：</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">章节名</th>
          <th style="text-align: left">内容</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left">引言/概述</td>
          <td style="text-align: left">介绍 UEFI 规范，并描述 UEFI 的主要组件。</td>
      </tr>
      <tr>
          <td style="text-align: left">启动管理器</td>
          <td style="text-align: left">管理器用于加载写入此规范的驱动程序和应用程序。</td>
      </tr>
      <tr>
          <td style="text-align: left">EFI 系统表和分区</td>
          <td style="text-align: left">描述了一个 EFI 系统表，它被传递给每个兼容的驱动程序和应用程序，并定义了一个基于 GUID 的分区方案。</td>
      </tr>
      <tr>
          <td style="text-align: left">块转换表</td>
          <td style="text-align: left">用于执行块 I/O 的布局和规则集，可提供单个块的断电写入原子性。</td>
      </tr>
      <tr>
          <td style="text-align: left">引导服务</td>
          <td style="text-align: left">包含在引导操作系统之前存在于 UEFI 兼容系统中的基本服务的定义。</td>
      </tr>
      <tr>
          <td style="text-align: left">运行时服务</td>
          <td style="text-align: left">包含在操作系统启动之前和之后存在于兼容系统中的基本服务的定义。</td>
      </tr>
      <tr>
          <td style="text-align: left">协议</td>
          <td style="text-align: left">EFI 加载图像协议描述已加载到内存的 UEFI 镜像。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">设备路径协议提供了在 UEFI 环境中构建和管理设备路径所需的信息。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">UEFI 驱动模型描述了一组服务和协议，适用于每个总线和设备类型。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">控制台支持协议定义了I/O协议，处理系统用户在启动服务环境中执行的基于文本的信息的输入和输出。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">媒介访问协议定义了加载文件协议，文件系统格式和媒介格式处理可移动媒介。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">PCI 总线支持协议定义 PCI 总线驱动程序，PCI 设备驱动程序和 PCI Option ROM 布局。所描述的协议包括 PCI 根桥 I/O 协议和 PCI I/O协议。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">SCSI 驱动程序模型和总线支持定义了 SCSI I/O协议和扩展SCSI Pass Thru 协议，用于抽象访问由 SCSI 主机控制器产生的 SCSI 通道。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">iSCSI协议定义了通过TCP/IP传输SCSI数据。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">USB 支持协议定义了 USB 总线驱动程序和 USB 设备驱动程序。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">调试器支持协议描述了一组可选的协议，提供所需的服务，以实现一个源级调试器的 UEFI 环境。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">压缩算法规范详细描述了压缩/解压缩算法，外加一个标准的EFI解压缩接口，用于启动时使用。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">ACPI 协议可用于从平台上安装或删除 ACPI 表。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">字符串服务：Unicode 排序协议允许在启动服务环境中运行的代码对给定语言的 Unicode 字符串执行词法比较函数;正则表达式协议用于根据正则表达式模式匹配 Unicode 字符串。</td>
      </tr>
      <tr>
          <td style="text-align: left">EFI 字节码虚拟机</td>
          <td style="text-align: left">定义 EFI 字节码虚拟处理器及其指令集。它还定义了如何将 EBC 对象文件加载到内存中，以及从本机代码到 EBC 代码再转换到本机代码的机制。</td>
      </tr>
      <tr>
          <td style="text-align: left">固件更新和报告</td>
          <td style="text-align: left">为设备提供一个抽象，以提供固件管理支持。</td>
      </tr>
      <tr>
          <td style="text-align: left">网络协议</td>
          <td style="text-align: left">SNP、PXE、BIS 和 HTTP 启动协议定义了在 UEFI 启动服务环境中执行时提供对网络设备访问的协议。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">受管网络协议定义了 EFI 受管网络协议，它提供原始 (未格式化) 异步网络数据包 I/O 服务和托管网络服务绑定协议，用于定位 MNP 驱动支持的通信设备。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">VLAN、EAP、Wi-Fi 和 Supplicant 协议定义了一个协议，为 VLAN 配置提供可管理性接口。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">蓝牙协议定义。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">TCP、IP、PIPsec、FTP、GTLS 和 Configurations 协议定义了 EFI TCPv4 (Transmission Control Protocol version 4) 协议和 EFI IPv4 (Internet Protocol version 4) 协议。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">ARP、DHCP、DNS、HTTP 和 REST 协议定义了 ARP (Address Resolution Protocol) 协议接口和 EFI DHCPv4 协议。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">UDP 和 MTFTP 协议定义了 EFI UDPv4 (User Datagram Protocol version 4) 协议，该协议在 EFI IPv4 协议上接口，并定义了 EFI MTFTPv4 协议接口，该接口建立在 EFI UDPv4 协议之上。</td>
      </tr>
      <tr>
          <td style="text-align: left">安全引导和驱动程序签名</td>
          <td style="text-align: left">介绍 Secure Boot 和生成 UEFI 数字签名的方法。</td>
      </tr>
      <tr>
          <td style="text-align: left">人机界面基础设施 (HII)</td>
          <td style="text-align: left">定义实现人机接口基础设施 (HII) 所需的核心代码和服务，包括管理用户输入和相关协议的代码定义的基本机制。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">描述用于管理系统配置的数据和 api:描述旋钮和设置的实际数据。</td>
      </tr>
      <tr>
          <td style="text-align: left">用户标识</td>
          <td style="text-align: left">描述描述平台当前用户的服务。</td>
      </tr>
      <tr>
          <td style="text-align: left">安全技术</td>
          <td style="text-align: left">描述用于利用安全技术的协议，包括加密散列和密钥管理。</td>
      </tr>
      <tr>
          <td style="text-align: left">杂项协议</td>
          <td style="text-align: left">Timestamp 协议提供了一个独立于平台的接口来检索高分辨率的时间戳计数器。当调用 ResetSystem 时，重置通知协议提供注册通知的服务。</td>
      </tr>
      <tr>
          <td style="text-align: left">附录</td>
          <td style="text-align: left">GUID 和时间格式。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">基于基本文本的控制台要求，符合 efi 系统需要提供通信能力。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">设备路径使用数据结构的例子，定义各种硬件设备的引导服务。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">状态代码列出了 UEFI 接口返回的成功、错误和警告代码。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">通用网络驱动程序接口定义了32/64位硬件和软件通用网络驱动程序接口(UNDIs)。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">使用简单指针协议。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">使用 EFI 扩展 SCISI 直通协议。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">压缩源代码的一个压缩算法的实现。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">一个 EFI 解压缩算法的实现的解压源代码。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">EFI 字节码虚拟机操作码列表提供了相应指令集的摘要。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">字母功能列表按字母顺序标识所有 UEFI 接口功能。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">EFI 1.10 协议变更和折旧清单标识了协议、GUID、修订标识符名称变更以及与 EFI 1.10 规范相比已弃用的协议。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">平台错误记录描述了用于表示平台硬件错误的常见平台错误记录格式。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">UEFI ACPI Data Table 定义了 UEFI ACPI 表格式。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">硬件错误记录持久性使用。</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">引用</td>
      </tr>
      <tr>
          <td style="text-align: left"></td>
          <td style="text-align: left">术语表</td>
      </tr>
      <tr>
          <td style="text-align: left">索引</td>
          <td style="text-align: left">提供规范中关键术语和概念的索引。</td>
      </tr>
  </tbody>
</table>
<h2 id="目标">目标</h2>
<p>“PC-AT”启动环境对行业内的创新提出了重大挑战。每一个新的平台功能或硬件创新都要求固件开发人员设计越来越复杂的解决方案，并且通常要求操作系统开发人员修改引导代码，然后客户才能从创新中受益。这可能是一个耗时的过程，需要大量的资源投资。</p>
<p>UEFI 规范的主要目标是定义一个替代引导环境，可以减轻这些考虑。在这个目标中，该规范类似于其他现有的引导规范。本规范的主要属性可以概括为以下属性：</p>
<ul>
<li>
<p><strong>一致的、可扩展的平台环境</strong>。该规范为固件定义了一个完整的解决方案，以描述所有平台特性和 OS 的 surface platform(TODO) 功能在引导过程中。这些定义非常丰富，足以涵盖一系列当代处理器设计。</p>
</li>
<li>
<p><strong>从固件中抽象操作系统</strong>。该规范定义了平台功能的接口。通过使用抽象接口，该规范允许在构建 OS 加载器时，而无需了解作为这些接口基础的平台和固件。这些接口代表了底层平台和固件实现与操作系统加载程序之间定义良好的稳定边界。这样的边界允许底层固件和操作系统加载程序更改，前提是两者都将交互限制在定义的接口上。</p>
</li>
<li>
<p><strong>合理的设备抽象，不需要遗留接口</strong>。“PC-AT”BIOS 接口要求操作系统加载程序对某些硬件设备的工作有特定的了解。该规范为 OS 加载器开发人员提供了一些不同的东西：抽象接口使得可以构建在一系列底层硬件设备上工作的代码，而无需明确了解该范围内每个设备的细节。</p>
</li>
<li>
<p><strong>从固件中提取 Option ROM</strong>。该规范定义了平台功能的接口，包括 PCI、USB 和 SCSI 等标准总线类型。支持的总线类型可能会随着时间的推移而增加，因此包括了一种扩展到未来总线类型的机制。这些定义的接口以及扩展到未来总线类型的能力是 UEFI 驱动程序模型的组件。UEFI 驱动模型的一个目的是解决现有“PC-AT”Option ROM 中存在的广泛问题。与 OS 加载程序一样，驱动程序使用抽象接口，因此可以构建设备驱动程序和总线驱动程序，而无需了解作为这些接口基础的平台和固件。</p>
</li>
<li>
<p><strong>架构上可共享的系统分区</strong>。扩展平台功能和添加新设备的计划通常需要软件支持。在许多情况下，当这些平台创新（TODO）在操作系统控制平台之前被激活时，它们必须由特定于平台而不是客户选择的操作系统的代码支持。解决这个问题的传统方法是在制造过程中将代码嵌入平台中（例如，在闪存设备中）。对这种持久存储的需求正在快速增长。该规范定义了大型海量存储媒介类型上的持久存储，以供平台支持代码扩展使用，以补充传统方法。规范中明确了其工作原理的定义，以确保固件开发商、OEM、操作系统供应商甚至第三方可以安全地共享空间，同时增加平台功能。</p>
</li>
</ul>
<p>可以通过多种方式定义提供这些属性的引导环境。实际上，在编写本规范时，已经存在几种替代方案，从学术角度来看可能是可行的。然而，考虑到当前围绕支持的处理器平台的基础设施能力，这些替代方案通常会带来很高的门槛。本规范旨在提供上面列出的属性，同时也认识到行业的独特需求，该行业在兼容性方面进行了大量投资，并且拥有大量无法立即放弃的系统安装基础。这些需求推动了对本规范中体现的附加属性的要求：</p>
<ul>
<li><strong>进化性的，而不是革命性的</strong>。规范中的接口和结构旨在尽可能地减少初始实现的负担。虽然已经小注意保在接口本身中维护适当的抽象，但该设计还确保可以重用 BIOS 代码来实现接口，而只需要最少的额外编码工作。换句话说，在 PC-AT 平台上，规范最初可以作为基于现有代码的底层实现之上的薄接口（thin Interface TODO）层来实现。同时，抽象接口的引入提供了将来从遗留代码的迁移。一旦抽象被确立为固件和操作系统加载程序在引导期间交互的手段，开发人员就可以随意替换抽象接口下的遗留代码。类似的硬件遗留迁移也是可能的。由于抽象隐藏了设备的细节，因此可以移除底层硬件，并用提供改进功能、降低成本或两者兼而有之的新硬件替换它。显然，这需要编写新的平台固件来支持设备并通过抽象接口将其呈现给 OS 加载器。但是，如果没有接口抽象，则可能根本无法移除旧设备。</li>
<li><strong>设计上的兼容性</strong>。系统分区结构的设计还保留了当前在“PC-AT”引导环境中使用的所有结构。因此，构建一个能够从同一磁盘引导传统操作系统或 EFI-aware 操作系统的单一系统是一件简单的事情。</li>
<li><strong>简化了操作系统中立的平台增值的添加</strong>。该规范定义了一个开放的、可扩展的接口，它有助于创建平台“驱动程序”。这些可能类似于操作系统驱动程序，在引导过程中为新设备类型提供支持，或者它们可能用于实现增强的平台功能，例如容错或安全性。此外，这种扩展平台能力的能力从一开始就被设计到规范中。这旨在帮助开发人员避免在尝试将新代码挤入传统 BIOS 环境时所固有的许多挫败感。由于包含用于添加新协议的接口，OEM 或固件开发人员拥有以模块化方式向平台添加功能的基础设施。由于规范中定义的调用约定和环境，此类驱动程序可能会使用高级编码语言来实现。这反过来可能有助于降低创新的难度和成本。系统分区选项为此类扩展提供了非易失性存储器存储的替代方案。</li>
<li><strong>建立在现有投入的基础上</strong>。在可能的情况下，规范避免在现有行业规范提供足够覆盖的领域重新定义接口和结构。例如，ACPI 规范为操作系统提供了发现和配置平台资源所需的所有信息。同样，规范设计的这种哲学选择旨在尽可能降低采用该规范的障碍。</li>
</ul>
<h2 id="目标受众">目标受众</h2>
<p>本文档主要适用于以下读者：</p>
<ul>
<li>将实现 UEFI 驱动程序的 IHV 和 OEM。</li>
<li>将创建支持的处理器平台的 OEM 厂商，旨在启动 shrink-wrap（TODO）的操作系统。</li>
<li>BIOS 开发人员，无论是创建通用 BIOS 和其他固件产品的人员，还是修改这些产品的支持人员。</li>
<li>操作系统开发人员将调整他们的 shrink-wrap（TODO）操作系统产品，用来在支持的基于处理器的平台上运行。</li>
</ul>
<h2 id="uefi-设计概述">UEFI 设计概述</h2>
<p>UEFI 的设计基于以下基本要素：</p>
<ul>
<li><strong>重用现有的基于表格的接口</strong>。为了保持对现有基础支持代码（包括操作系统和固件）的投资，必须在希望符合 UEFI 规范的平台上，实现通常在与支持的处理器规范兼容的平台上，实现的许多现有规范。 （有关更多信息，请参阅附录 Q：参考资料。）</li>
<li><strong>系统分区</strong>。系统分区定义了一个分区和文件系统，可允许多个供应商之间安全共享，并用于不同目的。包含单独的、可共享的系统分区的能力提供了增加平台附加值的机会，而不会显著增加对非易失性平台存储器的需求。</li>
<li><strong>引导服务</strong>。引导服务为可在引导期间使用的设备和系统功能提供接口。设备访问是通过“句柄”（handles）和“协议”(protocols) 抽象出来的。这有利于重用现有 BIOS 代码，将基本实现要求保持在规范之外，而不会给访问设备的消费者带来负担。</li>
<li><strong>运行时服务</strong>。提供了一组最小的运行时服务，以确保对基础平台的硬件资源进行适当的抽象，这些资源可能是操作系统在正常运行时需要的。</li>
</ul>
<hr>
<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="../pic/1-1.jpg">
            <img class="responsive-image" src="../pic/1-1.jpg" alt="UEFI 概念概述"  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>
<hr>
<p>图 1-1 描述了用于完成平台和操作系统引导的符合 UEFI 规范的系统的各种组件的交互。</p>
<p>平台固件能够从系统分区中检索操作系统加载器镜像。该规范提供了各种大容量存储设备类型，包括磁盘、CD-ROM 和 DVD，以及通过网络的远程启动。通过可扩展的协议接口，有可能增加其他的引导媒介类型，尽管如果这些媒介需要使用本文件中定义的协议以外的协议，可能需要修改操作系统加载器。</p>
<p>一旦启动，操作系统加载程序将继续引导整个操作系统。为此，它可以使用本规范或其他所需规范定义的 EFI 引导服务和接口来探测、解析和初始化各种平台组件和管理它们的操作系统软件。在引导阶段，EFI 运行时服务也可供 OS 加载器使用。</p>
<h2 id="uefi-驱动模型">UEFI 驱动模型</h2>
<p>本节描述了符合本规范的固件的驱动模型的目标。目标是让这个驱动模型为所有类型的总线和设备提供一个实现总线驱动和设备驱动的机制。在撰写本文时，支持的总线类型包括 PCI、USB 等。</p>
<p>随着硬件架构的不断发展，平台中存在的总线数量和类型也在不断增加。这种趋势在高端服务器中尤为明显。然而，更多样化的总线类型被设计到桌面和移动系统，甚至一些嵌入式系统中。这种日益增长的复杂性，意味着在预启动环境中，需要一种简单的方法来描述和管理平台中的所有总线和设备。UEFI 驱动模型以协议、服务和启动服务的形式提供了这种简单的方法。</p>
<h3 id="uefi-驱动程序模型目标">UEFI 驱动程序模型目标</h3>
<p>UEFI 驱动模型有以下目标：</p>
<ul>
<li><strong>兼容</strong> – 符合此规范的驱动程序必须保持与 EFI 1.10 规范和 UEFI 规范的兼容性。这意味着 UEFI 驱动程序模型利用 UEFI 2. 0 规范中的可扩展性机制来添加所需的功能。</li>
<li><strong>简单</strong> - 符合本规范的驱动程序必须易于实现，易于维护。UEFI 驱动模型必须允许驱动编写者专注于正在开发的特定设备的驱动。驱动程序不应关注平台策略或平台管理问题。这些考虑应该留给系统固件。</li>
<li><strong>可扩展性</strong> - UEFI 驱动模型必须能够适应所有类型的平台。这些平台包括嵌入式系统、移动和桌面系统，以及工作站和服务器。</li>
<li><strong>灵活</strong> - UEFI 驱动模型必须支持枚举所有设备的能力，或者只枚举启动所需操作系统的那些设备。最小的设备枚举提供了对更快速的启动能力的支持，而完整的设备媒体提供了在系统中存在的任何启动设备上执行操作系统安装、系统维护或系统诊断的能力。</li>
<li><strong>可扩展性</strong> - UEFI 驱动模型必须能够扩展到未来定义的总线类型。</li>
<li><strong>可移植性</strong> - 根据 UEFI 驱动模型编写的驱动，必须在不同平台和支持的处理器架构之间可移植。</li>
<li><strong>可互操作性</strong> - 驱动程序必须与其他驱动程序和系统固件共存，并且必须在不产生资源冲突的情况下进行操作。</li>
<li><strong>描述复杂的总线层次结构</strong> - UEFI 驱动模型必须能够描述各种总线拓扑结构，从非常简单的单总线平台到包含许多不同类型总线的非常复杂的平台。</li>
<li><strong>驱动占用空间小</strong> - 由 UEFI 驱动程序模型产生的可执行文件的大小必须最小化，以减少整体平台成本。虽然灵活性和可扩展性是目标，但支持这些所需的额外开销必须保持在最低水平，以防止固件组件的大小变得无法管理。</li>
<li><strong>解决遗留 Option ROM 的问题</strong> - UEFI 驱动模型必须直接解决遗留 Option ROM 的约束和限制。具体来说，必须能够建立同时支持 UEFI 驱动和传统 Option ROM 的插件卡，这种卡可以在传统 BIOS 系统和符合 UEFI 的平台上执行，而无需修改卡上的代码。该解决方案必须提供一个从传统 Option ROM 驱动程序迁移到 UEFI 驱动程序的进化路径。</li>
</ul>
<h3 id="遗留-option-rom-问题">遗留 Option ROM 问题</h3>
<p>这个支持驱动模型的想法来自于对 UEFI 规范的反馈，它提供了一个明确的、由市场驱动的对传统选项 ROM（有时也被称为扩展 ROM）的替代要求。人们认为，UEFI 规范的出现代表了一个机会，通过用一种在 UEFI 规范框架内工作的替代机制来取代传统选项 ROM 镜像的构建和操作，从而摆脱隐含的限制。</p>
<h2 id="迁移要求">迁移要求</h2>
<p>迁移要求涵盖了从最初实施本规范到未来所有平台和操作系统都实施本规范的过渡时期。在这一时期，有两个主要的兼容性考虑是很重要的。</p>
<ul>
<li>能够继续启动传统的操作系统；</li>
<li>能够在现有的平台上实现 UEFI，尽可能多地复用现有的固件代码，将开发资源和时间要求降到最低。</li>
</ul>
<h3 id="旧版操作系统支持">旧版操作系统支持</h3>
<p>UEFI 规范代表了收缩式操作系统和固件在启动过程中进行通信的首选方式。然而，选择制作一个符合该规范的平台，并不排除该平台，也支持不了解 UEFI 规范的，现有传统操作系统二进制文件。</p>
<p>UEFI 规范并不限制平台设计者，选择同时支持 UEFI 规范和更传统的 &ldquo;PC-AT &ldquo;启动基础架构。如果要实现这样的传统基础架构，应该按照现有的行业惯例来开发，这些惯例是在本规范范围之外定义的。在任何给定的平台上，支持的传统操作系统的选项是由该平台的制造商决定的。</p>
<h3 id="在旧平台上支持-uefi-规范">在旧平台上支持 UEFI 规范</h3>
<p>UEFI 规范经过精心设计，允许以最少的开发工作扩展现有系统以支持它。特别是 UEFI 规范中定义的抽象结构和服务，都可以在遗留平台上得到支持</p>
<p>例如，要在现有且受支持的基于 32 位的平台上实现此类支持，该平台使用传统 BIOS 来支持操作系统启动，需要提供额外的固件代码层。需要这些额外的代码来将服务和设备的现有接口转换为对本规范中定义的抽象的支持。</p>
<h2 id="本文档中使用的约定">本文档中使用的约定</h2>
<h3 id="数据结构描述">数据结构描述</h3>
<p><strong>支持的处理器是“小端”机器</strong>。这种区别意味着内存中多字节数据项的低位字节位于最低地址，而高位字节位于最高地址。一些受支持的 64 位处理器可以配置为“小端”和“大端”操作。所有旨在符合本规范的实现都使用“小端”操作。</p>
<p>在某些内存布局描述中，某些字段被标记为保留。软件必须将这些字段初始化为零并在读取时忽略它们。在更新操作中，软件必须保留任何保留字段。</p>
<h3 id="协议描述">协议描述</h3>
<p>协议描述一般有以下格式：</p>
<ul>
<li><strong>协议名称</strong>：协议接口的正式名称。</li>
<li><strong>摘要</strong>：协议接口的简要描述。</li>
<li><strong>GUID</strong>：协议接口的 128 位 GUID (Globally Unique Identifier)。</li>
<li><strong>协议接口结构</strong>：一种“c 风格”的数据结构定义，包含由该协议接口产生的过程和数据字段。</li>
<li><strong>参数</strong>：协议接口结构中各字段的简要说明。</li>
<li><strong>描述</strong>：对接口提供的功能的描述，包括调用者应该知道的任何限制和警告。</li>
<li><strong>相关定义</strong>：协议接口结构或其任何过程中使用的类型声明和常量。</li>
</ul>
<h3 id="过程描述">过程描述</h3>
<p>过程描述通常具有以下格式：</p>
<ul>
<li><strong>过程名称</strong>：过程的正式名称。</li>
<li><strong>摘要</strong>：过程的简要说明。</li>
<li><strong>原型</strong>：定义调用序列的“C 风格”过程标头。</li>
<li><strong>参数</strong>：对程序原型中每个字段的简要描述。</li>
<li><strong>描述</strong>：对接口所提供的功能的描述，包括调用者应该注意的任何限制和注意事项。</li>
<li><strong>相关定义</strong>：仅由该过程使用的类型声明和常量。</li>
<li><strong>返回的状态代码</strong>：对接口所返回的任何代码的描述。该过程需要实现本表中列出的任何状态代码。可以返回更多的错误代码，但是它们不会被标准的符合性测试所测试，而且任何使用该程序的软件，都不能依赖于实现可能提供的任何扩展错误代码。</li>
</ul>
<h3 id="指令描述">指令描述</h3>
<p>EBC 指令的指令描述一般有以下格式：</p>
<ul>
<li><strong>指令名称</strong>：指令的正式名称。</li>
<li><strong>语法</strong>：指令的简要描述。</li>
<li><strong>描述</strong>：对指令所提供的功能的描述，并附有指令编码的详细表格。</li>
<li><strong>操作</strong>：详细说明对操作数进行的操作。</li>
<li><strong>行为和限制</strong>：逐项描述指令中涉及的每个操作数的行为，以及适用于操作数或指令的任何限制。</li>
</ul>
<h3 id="伪代码约定">伪代码约定</h3>
<p>提出伪代码是为了以更简洁的形式描述算法。本文件中的所有算法都不打算直接进行编译。代码是在与周围文本相对应的水平上呈现的。</p>
<p>在描述变量时，列表是一个无序的同质对象的集合。一个队列是一个同质对象的有序列表。除非另有说明，否则假设排序为先进先出。</p>
<p>伪代码以类似于 C 的格式呈现，在适当的地方使用 C 约定。编码风格，特别是缩进风格，是为了可读性，不一定符合 UEFI 规范的实现。</p>
<h3 id="排版约定">排版约定</h3>
<p>本文件采用了以下描述的排版和说明性惯例。</p>
<ul>
<li>纯文本：规范中的绝大部分描述性文本都使用普通文本字体。</li>
<li>纯文本（蓝色）：任何有下划线和蓝色的纯文本都表示与交叉参考资料的活动链接。点击该词，就可以跟踪超链接。</li>
<li>加粗：在文本中，粗体字标识了一个处理器寄存器的名称。在其他情况下，黑体字可以作为段落中的标题。</li>
<li>斜体：在文本中，斜体字可以用作强调，以引入一个新的术语或表示手册或规范的名称。</li>
<li>加粗等宽（暗红色）：计算机代码、示例代码段和所有原型代码段使用 BOLD Monospace 字体，颜色为暗红色。这些代码列表通常出现在一个或多个独立的段落中，尽管单词或片段也可以嵌入到一个正常的文本段落中。</li>
<li>加粗等宽（蓝色）：用粗体单色字体的字，下划线和蓝色的字，表示该功能或类型定义的代码定义的活动超链接。点击该词，即可进入超链接。</li>
</ul>
<p><strong>注意</strong>：出于管理和文件大小的考虑，每一页上只有第一次出现的参考文献是一个主动链接。同一页上的后续参考文献不会被主动链接到定义上，而是使用标准的、无下划线的 BOLD Monospace 字体。在页面上找到该名称的第一个实例（使用下划线的 BOLD Monospace 字体），点击该词即可跳转到该功能或类型的定义。</p>
<ul>
<li>斜体等宽：在代码或文本中，斜体字表示必须提供的变量信息的占位符名称（即参数）。</li>
</ul>
<h3 id="数字格式">数字格式</h3>
<p>在本标准中，二进制数字是由仅由西方阿拉伯数字 0 和 1 组成的任何数字序列表示的，后面紧跟一个小写的 b（例如，0101b）。在二进制数字表示中的字符之间可以包含下划线或空格，以增加可读性或划分领域边界（例如，0 0101 1010b 或 0_0101_1010b）。</p>
<h4 id="十六进制">十六进制</h4>
<p>十六进制数字在本标准中用 0x 表示，前面是仅由西阿拉伯数字 0 至 9 和/或大写英文字母 A 至 F 组成的任何数字序列（例如，0xFA23）。十六进制数字表示中的字符之间可以包含下划线或空格，以增加可读性或划定字段边界（例如，0xB FD8C FA23 或 0xB_FD8C_FA23）。</p>
<h4 id="十进制">十进制</h4>
<p>在本标准中，小数是由仅由阿拉伯数字 0 到 9 组成的任何数字序列来表示的，后面不紧跟小写的 b 或小写的 h（例如，25）。本标准使用以下惯例来表示小数：</p>
<ul>
<li>小数点分隔符（即分隔数字的整数部分和小数部分）是一个句号；</li>
<li>千位数分隔符（即分隔数字部分的三位数组）是一个逗号；</li>
<li>千位数分隔符用于数字的整数部分，不用于数字的小数部分。</li>
</ul>
<h3 id="二进制前缀">二进制前缀</h3>
<p>本标准使用国际单位制（SI）中定义的前缀来表示 10 的幂值。见 &ldquo;SI 二进制前缀 &ldquo;标题下的 &ldquo;UEFI 相关文件链接&rdquo;（<a href="http://uefi.org/uefi">http://uefi.org/uefi</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="../pic/table1-1.jpg">
            <img class="responsive-image" src="../pic/table1-1.jpg" 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>本标准使用ISO/IEC 80000-13《数量和单位&ndash;第 13 部分：信息科学和技术》和 IEEE 1514《二进制倍数前缀标准》中定义的二进制前缀，用于表示 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="../pic/table1-2.jpg">
            <img class="responsive-image" src="../pic/table1-2.jpg" 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>例如，4 KB 意味着 4000 个字节，4 KiB 意味着 4096 个字节。</p>
<h3 id="修订号">修订号</h3>
<p>对 UEFI 规范的更新被认为是新的修订或勘误表，如下所述：</p>
<ul>
<li>当有实质性的新内容或可能修改现有行为的变化时，就会产生一个新的修订。新的修订版由一个主要的。次要的版本号来指定（例如：xx.yy）。在变化特别小的情况下，我们可能有一个 major.minor.minor 的命名惯例（例如 xx.yy.zz）。</li>
<li>当批准的规范更新不包括任何重要的新材料或修改现有行为时，就会产生勘误的版本。勘误的指定方法是在版本号后面加上一个大写字母，如 xx.yy 勘误 A。</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>解决 VSCode 远程登录失败 Error: WebSocket close with status code 1006</title>
      <link>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3vscode%E8%BF%9C%E7%A8%8B%E7%99%BB%E5%BD%95%E5%A4%B1%E8%B4%A5error-websocket-close-with-status-code-1006/</link>
      <pubDate>Sat, 15 Oct 2022 18:53:20 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%A7%A3%E5%86%B3vscode%E8%BF%9C%E7%A8%8B%E7%99%BB%E5%BD%95%E5%A4%B1%E8%B4%A5error-websocket-close-with-status-code-1006/</guid>
      <description>&lt;h2 id=&#34;保留现场&#34;&gt;保留现场&lt;/h2&gt;
&lt;p&gt;使用 VSCode 远程登录失败，报错：Failed to connect to the remote extension host server (Error: WebSocket close with status code 1006)。&lt;/p&gt;
&lt;h2 id=&#34;解决方法&#34;&gt;解决方法&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim /etc/ssh/sshd_config
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AllowTcpForwarding no
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AllowAgentForwarding no
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 替换为&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AllowTcpForwarding yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AllowAgentForwarding yes
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;保存后重启 &lt;code&gt;sshd&lt;/code&gt; 服务：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart sshd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<h2 id="保留现场">保留现场</h2>
<p>使用 VSCode 远程登录失败，报错：Failed to connect to the remote extension host server (Error: WebSocket close with status code 1006)。</p>
<h2 id="解决方法">解决方法</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim /etc/ssh/sshd_config
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">AllowTcpForwarding no
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">AllowAgentForwarding no
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 替换为</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">AllowTcpForwarding yes
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">AllowAgentForwarding yes
</span></span></code></pre></div><p>保存后重启 <code>sshd</code> 服务：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl restart sshd
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>如何使用 GitHub Actions</title>
      <link>https://lifeislife.cn/posts/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8github-actions/</link>
      <pubDate>Fri, 14 Oct 2022 22:08:54 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8github-actions/</guid>
      <description>&lt;h1 id=&#34;简介&#34;&gt;简介&lt;/h1&gt;
&lt;p&gt;GitHub Actions 是 GitHub 在 2018 年推出的&lt;a href=&#34;http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html&#34;&gt;持续集成服务&lt;/a&gt;。它可以自动完成一些开发周期内的任务，如 Push 代码时自动编译，Pull 代码时自动执行测试脚本等等。&lt;/p&gt;
&lt;p&gt;我了解 GitHub Actions 的契机是，我在 GitHub 上保存了一些 Markdown 文档，我希望每次更新文档后自动使用 Pandoc 转换成 PDF 文档。接下来我们一起学习如何通过 GitHub Actions 实现这样的需求。&lt;/p&gt;
&lt;p&gt;首先我们先直观的了解一下它在 GitHub 的位置，如果打开一个仓库，它有图中绿色对号√，或者红色叉号×，说明这个项目配置了 GitHub Actions，绿色表示自动化的流程运行成功了，红色表示失败了。&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//2022/10/15/a3a273415c7250d26bb50e293378bf5e.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/10/15/a3a273415c7250d26bb50e293378bf5e.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;code&gt;Actions&lt;/code&gt;按钮就可以查看具体的任务详情。下面我们先学习如何配置一个简单的 GitHub Actions。&lt;/p&gt;
&lt;h1 id=&#34;配置-github-actions&#34;&gt;配置 GitHub Actions&lt;/h1&gt;
&lt;p&gt;GitHub Actions 可以简单理解为一些自动化脚本，工具，目的就是为了减少重复工作，所以这些工具都可以做成普适性的工具。而 GitHub 官方就开放了一个这类工具的&lt;a href=&#34;https://github.com/marketplace&#34;&gt;市场&lt;/a&gt;，我们可以在上面搜索自己想要的工具。因为初学 GitHub Actions 所以也不知道怎么写配置文件，我们可以直接搜索一个并应用它，看看别人是怎么写的。&lt;/p&gt;
&lt;p&gt;我们进入一个自己的仓库，点击&lt;code&gt;Actions&lt;/code&gt;，搜索框中搜索&lt;code&gt;PDF&lt;/code&gt;，在搜索结果中找到&lt;a href=&#34;https://github.com/marketplace/actions/create-pdf&#34;&gt;Create PDF · Actions&lt;/a&gt;这个工具。如果搜索到点击&lt;code&gt;Configure&lt;/code&gt;。如果显示未找到，则点击&lt;code&gt;set up a workflow yourself&lt;/code&gt;，同样搜索&lt;code&gt;PDF&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//2022/10/15/f6baead7ab50e1db5ce3611a0525f833.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/10/15/f6baead7ab50e1db5ce3611a0525f833.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;a href=&#34;https://github.com/marketplace/actions/create-pdf&#34;&gt;详情页面&lt;/a&gt;，拉到底，将&lt;code&gt;Example usage&lt;/code&gt;。里的内容复制到编辑框中。点击右上角&lt;code&gt;Start commit&lt;/code&gt;将会把我们新建的&lt;code&gt;main.yml&lt;/code&gt;提交到仓库中。这就相当于创建了一个生成 PDF 的 GitHub Actions。当然每个 Actions 都有一些使用要求，比如这里还要根据介绍，创建几个文件夹，比如从哪个文件夹获取源文件，生成后的 PDF 又会放到哪个文件夹等。这里就不再介绍，我们先了解如何创建一个 Actions。&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//2022/10/16/0a24d8bcecc9d210b4e6b75b2fccae0b.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/10/16/0a24d8bcecc9d210b4e6b75b2fccae0b.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//2022/10/16/69370e917f9c35963a2343176ecf0eea.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img//2022/10/16/69370e917f9c35963a2343176ecf0eea.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;h1 id=&#34;workflow-配置&#34;&gt;Workflow 配置&lt;/h1&gt;
&lt;p&gt;GitHub Actions 的配置文件叫做 workflow 文件，存放在代码仓库的.github/workflows 目录。&lt;/p&gt;
&lt;p&gt;workflow 文件采用 YAML 格式，文件名可以任意取，但是后缀名统一为.yml or .yaml，比如 foo.yml or foo.yaml。一个库可以有多个 workflow 文件。GitHub 只要发现.github/workflows 目录里面有.yml or .yaml 文件，就会自动运行该文件（并行）。&lt;/p&gt;
&lt;p&gt;接下来我们逐个参数来解释都有哪些功能。&lt;/p&gt;
&lt;h2 id=&#34;on&#34;&gt;on&lt;/h2&gt;
&lt;p&gt;触发 workflow 的 GitHub 事件的名称。比如&lt;code&gt;push&lt;/code&gt;代码时触发，其他人&lt;code&gt;fork&lt;/code&gt;代码仓时触发等等。&lt;/p&gt;
&lt;p&gt;可以只有一个事件触发，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也可有多个事件触发，使用列表列举，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;push, fork]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所有支持的事件列表，请查看&lt;a href=&#34;https://docs.github.com/cn/actions/using-workflows/events-that-trigger-workflows&#34;&gt;官方文档&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;onpushforktagsbranches&#34;&gt;on.[push|fork].[tags|branches]&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：从这里开始就会出现一个字段下有子字段，每个点号&lt;code&gt;.&lt;/code&gt;分割一个子字段。如&lt;code&gt;push&lt;/code&gt;或者&lt;code&gt;fork&lt;/code&gt;可以作为&lt;code&gt;on&lt;/code&gt;的子字段，&lt;code&gt;tags&lt;/code&gt;或者&lt;code&gt;branches&lt;/code&gt;可以作为&lt;code&gt;push&lt;/code&gt;或者&lt;code&gt;fork&lt;/code&gt;的子字段。在&lt;code&gt;yaml&lt;/code&gt;文件中，缩进很重要，每个缩进都表示是从属关系，表示是该字段的子字段。千万要注意缩进关系，如果缩进出错，那么将无法解析&lt;code&gt;yaml&lt;/code&gt;文件。&lt;/p&gt;
&lt;p&gt;指定触发事件时，可以限定分支或标签。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;master&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面代码指定，只有 master 分支发生 push 事件时，才会触发 workflow。&lt;/p&gt;
&lt;h2 id=&#34;name&#34;&gt;name&lt;/h2&gt;
&lt;p&gt;工作流程的名称。GitHub 在仓库的操作页面上显示工作流程的名称。如果省略 name，GitHub 将其设置为相对于仓库根目录的工作流程文件路径。&lt;/p&gt;
&lt;h2 id=&#34;jobs&#34;&gt;jobs&lt;/h2&gt;
&lt;p&gt;workflow 运行包括一项或多项 jobs。jobs 默认是并行运行。要按顺序运行作业，可以使用 &lt;code&gt;[job_id].needs&lt;/code&gt; 关键词在其他 job 上定义依赖项。&lt;/p&gt;
&lt;p&gt;每个作业在 &lt;code&gt;runs-on&lt;/code&gt; 指定的运行器环境中运行。&lt;/p&gt;
&lt;h3 id=&#34;jobsjob_id&#34;&gt;jobs.[job_id]&lt;/h3&gt;
&lt;p&gt;jobs 中的每个任务都有一个&lt;code&gt;[job_id]&lt;/code&gt; ，且其必须为 jobs 对象中&lt;strong&gt;唯一的字符串键值&lt;/strong&gt;。&lt;code&gt;[job_id]&lt;/code&gt;必须以字母或&lt;code&gt;_&lt;/code&gt;开头，并且只能包含字母数字字符、&lt;code&gt;-&lt;/code&gt;或&lt;code&gt;_&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;first_job&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# [job_id]，任务 id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;My first job&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;second_job&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;My second job&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;jobsjob_idruns-on&#34;&gt;jobs.[job_id].[runs-on]&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;runs-on&lt;/code&gt; 字段指定运行所需要的虚拟机环境。它是必填字段。目前可用的虚拟机如下。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;ubuntu-latest，ubuntu-18.04或ubuntu-16.04&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;windows-latest，windows-2019或windows-2016&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;macOS-latest或macOS-10.14&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面代码指定虚拟机环境为 ubuntu-18.04。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-18.04&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;jobsjob_idname&#34;&gt;jobs.[job_id].name&lt;/h3&gt;
&lt;p&gt;workflow 文件的主体是 jobs 字段，表示要执行的一项或多项任务。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;job_id&lt;/code&gt; 里面的 &lt;code&gt;name&lt;/code&gt; 字段是任务的说明。它可以在网页端的 UI 上显示。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;first_job&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;My first job &lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# [job_name]，任务名称&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;second_job&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;My second job&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;jobsjob_idneeds&#34;&gt;jobs.[job_id].needs&lt;/h3&gt;
&lt;p&gt;needs 字段指定当前任务的依赖关系，即运行顺序。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;job1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;job2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;needs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;job1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;job3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;needs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;job1, job2]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面代码中，job1 必须先于 job2 完成，而 job3 等待 job1 和 job2 的完成才能运行。因此，这个 workflow 的运行顺序依次为：job1、job2、job3。&lt;/p&gt;
&lt;h3 id=&#34;jobsjob_idsteps&#34;&gt;jobs.[job_id].steps&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;steps&lt;/code&gt; 字段指定每个 Job 的运行步骤，可以包含一个或多个步骤。每个步骤都可以指定以下三个字段。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;jobs.[job_id].steps.name：步骤名称。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;jobs.[job_id].steps.run：该步骤运行的命令或者 action。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;l&#34;&gt;jobs.[job_id].steps.env：该步骤所需的环境变量。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面是一个完整的 workflow 文件的范例。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Greeting from Mona&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;my-job&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;My Job&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Print a greeting&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;MY_VAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Hi there! My name is&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;FIRST_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Mona&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;MIDDLE_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;The&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;LAST_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Octocat&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;          echo $MY_VAR $FIRST_NAME $MIDDLE_NAME $LAST_NAME.&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面代码中，steps 字段只包括一个步骤。该步骤先注入四个环境变量，然后执行一条 Bash 命令。&lt;/p&gt;
&lt;h3 id=&#34;jobsjob_idstepsuses&#34;&gt;jobs.[job_id].steps[*].uses&lt;/h3&gt;
&lt;p&gt;选择一个 action，可以理解为若干 steps.run，有利于代码复用。这也是 github action 最主要的功能。&lt;/p&gt;
&lt;p&gt;比如最常用的，下载本仓库的代码到工作区，就是使用的一个 action 完成的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Check out Git repository&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;actions/checkout@v2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;注：&lt;code&gt;@v2&lt;/code&gt; 什么意思？
表示 Action 的版本。我们如果不带版本号的话，就是默认使用最新版本。Github 官方强烈要求我们带上版本号。这样子的话，我们就不会出现：写好一个 Workflow，但是由于某个 Action 的作者一更新，我们的 Workflow 就崩了的问题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;jobsjob_idstepsrun&#34;&gt;jobs.[job_id].steps.run&lt;/h3&gt;
&lt;p&gt;在 shell 中执行的命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;actions/checkout@v2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;create dir&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;            mkdir output  # create output dir&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上配置是在下载完本仓库的代码后，在仓库根目录新建一个&lt;code&gt;output&lt;/code&gt;文件夹。注意&lt;code&gt;run:&lt;/code&gt;后的&lt;code&gt;|&lt;/code&gt;表示可以多行命令。如果没有&lt;code&gt;|&lt;/code&gt;表示只能执行一条命令。&lt;/p&gt;
&lt;h3 id=&#34;jobsjob_idstepsworking-directory&#34;&gt;jobs.[job_id].steps.working-directory&lt;/h3&gt;
&lt;p&gt;用来指定在&lt;code&gt;run&lt;/code&gt;命令在哪执行。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Create dir&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mkdir output&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;working-directory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;./build&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;jobsjob_idstepsshell&#34;&gt;jobs.[job_id].steps.shell&lt;/h3&gt;
&lt;p&gt;用来指定 shell 类型，如 Python，bash，powershell 等。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line