<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://blog.yokumi.cn/</id>
  <title type="text">YokumiのBlog</title>
  <subtitle type="text">Here&apos;s Yokumi...</subtitle>
  <updated>2026-05-29T12:49:00.000Z</updated>
  <author><name>Yokumi</name></author>
  <link rel="alternate" href="https://blog.yokumi.cn/"/>
  <link rel="self" href="https://blog.yokumi.cn/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://blog.yokumi.cn/posts/bupt-computer-science-survival-guide/</id>
      <title type="text">BUPT计科专业生存不完全指北</title>
      <published>2024-06-26T09:06:00.000Z</published>
      <updated>2024-06-26T09:06:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/bupt-computer-science-survival-guide/"/>
      <summary type="text">关于自己为什么要写这篇博客的一些碎碎念。首先在个人进行课程学习以及期末复习的过程中，历届学长的博客都起到了非常大的帮助。主要是 19 级和 21 级，22 级似乎断代了，没有找到什么学长的博客。总之，既然自己已经整理了一些内容，也就顺便写了。至于有没有帮助，那只能说仅供大家参考，毕竟楼主的水平在计科属实蒟蒻。</summary>
      <content type="html"><![CDATA[<section><h2>写在前面<a href="#写在前面"><span>#</span></a></h2><p>关于自己为什么要写这篇博客的一些碎碎念。首先在个人进行课程学习以及期末复习的过程中，历届学长的博客都起到了非常大的帮助。主要是 19 级和 21 级，22 级似乎断代了，没有找到什么学长的博客。总之，既然自己已经整理了一些内容，也就顺便写了。至于有没有帮助，那只能说仅供大家参考，毕竟楼主的水平在计科属实蒟蒻。也欢迎后来者在评论区补充。</p><p>楼主还整理了一些有关<a href="#%E4%B8%93%E4%B8%9A%E5%88%86%E6%B5%81">专业分流</a>和<a href="#%E8%BD%AC%E4%B8%93%E4%B8%9A">转专业</a>的相关情况。</p><p>楼主的所有课程笔记在我的 <a href="https://buptwiki.yokumi.cn/" target="_blank">Wiki 网站</a> 上，博客上也散落了一些，欢迎访问。</p><div><div><div></div><div>Warning</div></div><div><p>纯主观感受，课程情况可能与实际存在差距，请以你自己的培养方案为准，楼主参考的是 <a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/blob/main/Additional-Information/Cultivation/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%AD%A6%E9%99%A2%EF%BC%88%E5%9B%BD%E5%AE%B6%E7%A4%BA%E8%8C%83%E6%80%A7%E8%BD%AF%E4%BB%B6%E5%AD%A6%E9%99%A2%EF%BC%892023%E7%BA%A7%E6%9C%AC%E7%A7%91%E4%B8%93%E4%B8%9A%E5%9F%B9%E5%85%BB%E6%96%B9%E6%A1%88-0831.pdf" target="_blank">23 级计科培养方案</a>。（听说 24 级培养方案大改，因此笔者这篇文章可能也没啥用了）。</p></div></div></section>
<section><h2>大一上学期<a href="#大一上学期"><span>#</span></a></h2><p>不完全按照学分顺序。</p><section><h3>数学分析（上）<a href="#数学分析上"><span>#</span></a></h3><p>23 级数分和高数还是二选一，24 级据我所知是只有数分了，不过话又说回来，工科数学分析的难度其实和高数没差多少。学就完了，无论对考研党还是保研党（毕竟 6 + 5 = 11 学分）都很重要。好好学之后还可以参加<strong>全国大学生数学竞赛</strong>有保研分加（当然也有很多考研党在考研前去参加，屠新手村了属于是）。</p></section><section><h3>线性代数<a href="#线性代数"><span>#</span></a></h3><p>对工科来说也叫较水，做做往年题就行。但是其实挺重要的。</p></section><section><h3>计算导论与程序设计<a href="#计算导论与程序设计"><span>#</span></a></h3><p>基本都是计算机导论，但夹杂着少部分的 C 语言教学，高达 4.5 的学分，甚至大于 408 的专业课。平时会在 PTA （浙大的sb OJ 平台） 布置 OJ 题，不过似乎不占平时分（可能和具体老师有关），不过刷刷还是有帮助的。3 次上机考试和期中期末考试也通过 PTA 的 OMS 监考系统进行。我们当时第二次机考和期中安排在一起，考了整整一上午。</p><p>机考一般是 5 道大题（共 500 分），有时候会比较恶心；<strong>期中期末有选择和填空和简答，以及根据题目要求编写程序（不用过测试样例）</strong>，得背背概念。总之，学分高事也多，挺烦的，不过对大一如果之前没怎么接触过计算机的确实得好好打基础。</p></section><section><h3>创新创业实践课<a href="#创新创业实践课"><span>#</span></a></h3><p>培养方案里标的是<strong>指选</strong>，但事实上是必修。不同教学班的任务不太一样，笔者当时好像是一个大数据一个移动 APP 开发（可以看看笔者的<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term1/Innovation-and-Entrepreneurship-Practice/Mobile-App-Development" target="_blank">轮子</a>）。水。</p></section><section><h3>习近平新时代中国特色社会主义思想概论<a href="#习近平新时代中国特色社会主义思想概论"><span>#</span></a></h3><p>印象里当时布置了一个模拟政协提案报告。期末的话，我只能说珍惜开卷时光吧。</p></section><section><h3>军事理论<a href="#军事理论"><span>#</span></a></h3><p>水。期末有题库。好像还有篇小论文。</p></section></section>
<section><h2>大一下学期<a href="#大一下学期"><span>#</span></a></h2><section><h3>数学分析（下）<a href="#数学分析下"><span>#</span></a></h3><p>已经在 <a href="#%E6%95%B0%E5%AD%A6%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8A%EF%BC%89">数学分析（上）</a> 部分说了，数分下会难一些。</p></section><section><h3>大学物理 C<a href="#大学物理-c"><span>#</span></a></h3><p>计院的大学物理压缩在一学期，从力学一直到光学，内容比较多，但讲的都不是很深。你说的对，但是我们那年期末考了一堆光学，服了。期中的话好像是 4 道大题，另外老师会隔几周安排小测（具体视老师而定）。这里有一些<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term2/College-Physics-C" target="_blank">往年题</a>；还是得花点时间，毕竟到大一下高中的物理水平已经退化的差不多了。</p></section><section><h3>离散数学（上）<a href="#离散数学上"><span>#</span></a></h3><p>人已经学的离散了。总之学的很杂，楼主已经基本忘记上册学了什么了。不过<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term2/Discrete-Mathematics-I" target="_blank">期中期末</a>也基本都是套路题。</p><p>注意是英文教学（指 <strong>PPT 、教材、作业题和考试是英文</strong>），需要能基本知道基本概念的英文名词，能看懂。</p></section><section><h3>电路与电子学基础<a href="#电路与电子学基础"><span>#</span></a></h3><p>首先，如果你和楼主是老乡且高中选了技术，我只能说可以少花很多精力。主要就是一些模拟电路的内容，比如二极管、三极管，电容、电感和一些元器件以及模拟电路中的数值计算。2 学分，做做作业和往年题就能学会，这里有一些<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term2/Electronic-Circuit-Analysis" target="_blank">参考资料</a>。</p></section><section><h3>二选一<a href="#二选一"><span>#</span></a></h3><p>没什么很强烈的推荐倾向，如果你有算法竞赛基础，那推荐后者。否则的话选前者锻炼一下语言和开发基础也不错。</p><section><h4>计算导论与程序设计课程设计<a href="#计算导论与程序设计课程设计"><span>#</span></a></h4><div><div><div></div><div>Warning</div></div><div><p>注意，这门课在 24 级改名了，在前面加了个什么“项目制课程设计阶段一”。</p></div></div><p>我们这届做的是<strong>模拟麦当劳点餐系统</strong>，工作量还是比较大的。<strong>分为 OJ 版本和 GUI 版本</strong>。</p><p>OJ 版本强制 C 语言，并要求你的程序有一定的优化，否则通不过全部测试点。GUI 版本是组队完成（当然，你觉得一个人足够也可以一个人干，不过并不推荐，因为那时候已经临近期末了），老师一般推荐 C 自带的 EasyX 等库，楼主一个人库库干了很久并推翻了之前写的全部 C 怒转 Cpp 用 Qt 卷了个还可以的 <a href="https://www.bilibili.com/video/BV15i421e7XQ/?spm_id_from=333.1387.homepage.video_card.click&amp;vd_source=e3ae0c6236d84247af331a2b674dd6ca" target="_blank">GUI 版本</a>。</p><p>最后 GUI 版本在线下由老师和助教一起验收，一般会看功能实现，问用了什么平台或库开发，分工，代码行数等。</p><p>24 级做<strong>俄罗斯方块</strong>，只能说很难评。甚至还有逆天助教爆了个大的。</p><p>另外，课上还是教一些 C 语言剩余的链表、文件操作等内容，也有一些 OJ 题，教的和考的，关系不大我只能说。</p></section><section><h4>程序设计竞赛基础<a href="#程序设计竞赛基础"><span>#</span></a></h4><p>没选。校队✌️似乎是直接不用上给满，哎哎。<strong>期末是老师提前给一些题目，考试随机抽几道</strong>。（类似于背板子）</p><p>另外，<strong>24 级这门课安排到了大一下结束的小学期（6月底7月初）</strong>。</p></section></section><section><h3>物理实验 A<a href="#物理实验-a"><span>#</span></a></h3><p>每个教学班具体安排的实验不太一样，一共好像是 6 个，每个一份报告。</p></section><section><h3>中国近代史纲要<a href="#中国近代史纲要"><span>#</span></a></h3><p>哎思政课，记得签到我只能说，另外即使签到了也谨防某些老师背刺。期末开卷，且行且珍惜。</p></section><section><h3>四史<a href="#四史"><span>#</span></a></h3><p>选哪个都无所谓，差不太多。记得之前论坛看到有人推荐一个老师的，具体忘了。</p></section><section><h3>军训<a href="#军训"><span>#</span></a></h3><div><div><div></div><div>Warning</div></div><div><p>注意，<strong>24 级的军训安排在入学前（8月下旬）</strong>。之后不知道还会变吗。</p></div></div><p>23 级的军训安排在大一下结束（7月5号 ～ 19号）。夏天的北京一晒一个不吱声。</p><p>如果你参加国旗护卫队，军训成绩直接给满。不过评价是真没必要，没事给自己找罪受。</p></section></section>
<section><h2>大二上学期<a href="#大二上学期"><span>#</span></a></h2><p>这学期期中长达 1 个月，考完就差不多期末了，太 TM 逆天了。</p><section><h3>数据结构<a href="#数据结构"><span>#</span></a></h3><p>408 之一。不过还是挺简单的，OJ 题 + 3 次<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/Data-Structure/Labs" target="_blank">实验</a>，不过每年实验似乎安排的不太一样。另外，期中期末都是纸质版考试，要<strong>手写代码</strong>，可以参考<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/Data-Structure/Past-Exams" target="_blank">往年题</a>。</p></section><section><h3>数字逻辑与数字系统<a href="#数字逻辑与数字系统"><span>#</span></a></h3><p>也就是数电，东西是真的多，不过<strong>方维老师</strong>真的很好。平常作业和期中考试都是在 PTA 上布置，后期还会有一些 Verilog 的内容，<strong>期末也需要手写 Verilog 代码</strong>，所以也需要好好学，对下学期的课程设计也有帮助。</p><p>同时，有 6 次实验，后面 2 次需要用到 Verilog 编程实现。</p><p>个人的一些<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/Digital-Logic-and-Digital-Systems" target="_blank">参考资料</a>。</p></section><section><h3>离散数学（下）<a href="#离散数学下"><span>#</span></a></h3><p>主要是<strong>群论、图论</strong>的内容。群论看着比较难，但是考试题型固定，所以会做作业题就行。其他注意事项见 <a href="#%E7%A6%BB%E6%95%A3%E6%95%B0%E5%AD%A6%EF%BC%88%E4%B8%8A%EF%BC%89">离散数学（上）</a>。</p><p><a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/Discrete-Mathematics-II" target="_blank">往年题</a>。</p></section><section><h3>计算机系统基础<a href="#计算机系统基础"><span>#</span></a></h3><p>也就是大名鼎鼎的 CMU 的 CSAPP 课程，不过北邮做了很大程度的压缩（同时也体现在 PPT 上，对原版的 PPT 做了删减，导致看起来十分奇怪），同时压缩到了 4 个实验（2 个是照搬的 CMU 的，剩下 2 个挺简单）。</p><p>搬的 CMU 的两个实验，炸弹和缓冲区溢出攻击还是挺有意思的，可以做做，对熟悉汇编语言也有帮助。</p><p>知识点较为重要，不过如果你遇上一节课翻三位数 PPT 并且听不太懂在讲什么的老师，建议看书或者看网课自学。</p><p>个人的一些<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/CSAPP/Homeworks" target="_blank">习题解答</a>。</p></section><section><h3>概率论二选一<a href="#概率论二选一"><span>#</span></a></h3><p>如果是考研党，那无脑选前者吧。</p><section><h4>概率论与数理统计<a href="#概率论与数理统计"><span>#</span></a></h4><p>需要花点时间背公式（特别是<strong>假设检验</strong>部分，那真是一大坨），再做做<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/Probability-Theory-and-Mathematical-Statistics" target="_blank">往年题</a>，一般也没什么问题。</p></section><section><h4>概率论与随机过程<a href="#概率论与随机过程"><span>#</span></a></h4><p>没选，身边选的人也很少，好像整个年级选的人就一个班的人数。会讲一些马尔可夫随机过程部分的内容，对以后科研兴许有帮助？</p></section></section><section><h3>四选一<a href="#四选一"><span>#</span></a></h3><p><strong>推荐《矩阵理论与方法》或者《数学建模与模拟》</strong>。更有甚者为了刷分，四选四或者四选三。</p><section><h4>矩阵理论与方法<a href="#矩阵理论与方法"><span>#</span></a></h4><p><strong>李昊辰老师，不和分数过不去的无脑冲即可</strong>。每节课前有签到，作业一般是手抄一些定理或者完成一些代码（老师会提供参考程序）。期末是一篇<strong>大论文</strong>，老师会给出一个模版和目录，根据目录往里面填东西即可，他会要求放一些平时作业中手抄的定理等内容。</p><p>参考资料在<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/Matrix-Theory-and-Methods" target="_blank">这里</a>。</p></section><section><h4>数学建模与模拟<a href="#数学建模与模拟"><span>#</span></a></h4><p>听人说期末好像是一周时间完成一份卷子？总之给分也挺高。</p></section><section><h4>运筹学<a href="#运筹学"><span>#</span></a></h4><p>不太清楚，不过好像平时作业挺多，期末是<strong>随堂开卷</strong>。</p></section><section><h4>组合数学<a href="#组合数学"><span>#</span></a></h4><p>几乎没人选，更不清楚了。</p></section></section><section><h3>马克思主义基本原理<a href="#马克思主义基本原理"><span>#</span></a></h3><p>闭卷喽，一些<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term3/Fundamentals-of-Marxism" target="_blank">背诵资料</a>。</p></section></section>
<section><h2>大二下学期<a href="#大二下学期"><span>#</span></a></h2><p><strong>我 TM 实在是懒得喷了，根据楼主自己的情况和“身边统计学”，计科大概率是被狠狠压分了（不一定完全准确，只能说大概率是。至于实际情况自己看吧，反正楼主已经在前面说明了，这篇博客纯主观）。就 byd 这个绩点计算规则你还压分，你存心不想让人保外了是吧。</strong></p><section><h3>计算机网络<a href="#计算机网络"><span>#</span></a></h3><p>网络这块知识点特别细和多。计院使用的教材是 <strong>“自下往上”</strong> 的方法，并且可能是由于北邮通信的特色，又花了较多的笔墨讲<strong>物理层</strong>，不过大头还是在<strong>网络层和传输层</strong>，即 TCP/IP 部分，应用层似乎每年都是因为课时来不及直接让看的网课，不过本身考试也不涉及多少。一般期中的范围是物理层 + 数据链路层，由任课老师自行命题，卷面一般有英文以及对应中文翻译，<strong>期末是所有内容，并且卷面全英文，作答可用中文，且题量较大，题型可参考样题</strong>。是需要花不少精力学的一门课。</p><p>作业的话，每年都差不多，可以找到参考答案，不过最好得先自己完成。另外有 2 个实验，第一个<a href="https://github.com/Yokumii/BuptNetworkLab_Datelink" target="_blank">数据链路层实验</a>一般在 4 月中旬布置，5 月中旬<strong>由助教线下验收，一般会问你 GBN/SR 原理，你的具体代码实现并记录一个你当场跑的协议效率</strong>；第二个网络抓包实验只需要提交报告，无需验收。好好做，对课程理解还是有一定的帮助的。</p><p>张冬梅老师讲的还是非常不错的，并且线下、线上答疑都非常及时和耐心。至于最后给分，楼主只能说可能是整个课程组对计科都压分了，老师人还是很好的。</p><div><div><div></div><div>Warning</div></div><div><p>你猜我什么只说计科呢？按网工舍友的身边统计学，有高分，不过存在一定的方差（93 94 95 80-85 75）。我请问计科怎么你了呢？<strong>那我不得不劝人都去小专业了</strong>。</p></div></div></section><section><h3>计算机组成原理<a href="#计算机组成原理"><span>#</span></a></h3><p>同样是 4 学分的课，前置课程有数字逻辑与数字系统（数电）和计算机系统基础（CSAPP），虽然楼主不是很懂为什么计科要把计算机组成原理分成 4 + 2 + 4 学分的课上。楼主对于硬件并不是很擅长，CSAPP 学的更是一坨，计组学的也是没好到哪里去，关键是这期末还出一堆硬件。总之还是有难度并且需要花精力好好复习的一门课。</p><p>除作业外，类似数电，有 6 个线下<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term4/Computer-Organization/Labs" target="_blank">实验</a>，最后 2 个比较恶心。不过做完实验对课程知识的理解有较大的帮助。</p><p>周锋老师讲得也不错，平常有签到。至于最后给分，也是一言难尽，按照身边统计学，3 个班没有问到上 90 的，大部分都是 70 多。</p></section><section><h3>形式语言与自动机<a href="#形式语言与自动机"><span>#</span></a></h3><p>看着有些难但实际教的和考的都不算太难？感觉期末多刷刷<a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/tree/main/Term4/Formal-Languages-and-Automata/Past-Exams" target="_blank">往年题</a>就能基本掌握。</p><p>有 2 次实验，组队完成，不难，推荐用 Python ，做图形化比较方便，内置库的数据结构比较好用。</p><p>石川老师，属于是你院学术顶梁柱，治学严谨，不过课上有时候会点名。给分还行。</p></section><section><h3>三选二<a href="#三选二"><span>#</span></a></h3><section><h4>计算机网络课程设计<a href="#计算机网络课程设计"><span>#</span></a></h4><p>用 C 语言做个 DNS 中继服务器，已经是不知道迭代多少届的传统了，有往届代码可以直接复用，不过验收老师会问的非常细，并提了一些奇怪的问题，不过并没有说的现场让你改 BUG 那么夸张。</p><p>至于给分，懒得喷了已经。我们小组哈希、LRU 缓存、多线程高并发都做了，测试 QPS 6000+ ，验收和报告也没什么问题，结果给分都没上 90 ，纯恶心人。</p></section><section><h4>数据结构课程设计<a href="#数据结构课程设计"><span>#</span></a></h4><p>3 人组队。23 级的题目是<strong>个性化旅游系统</strong>，布置完就不上课自己做了，和上学期的数据结构课程也一点关系没有。最多就是一些排序和图算法。评价是<strong>纯卷 GUI 就行</strong>，有中期验收和期末验收，助教验收，全是不问你具体实现，纯看你前端功能有没有实现，最多问你一下用到了什么数据结构和算法，你乱说都没关系，完全不看代码。</p><p>要写的文档也是又臭又长，<strong>每周要提交周报，除此之外还有总报告、需求分析报告等将近 10 个文档</strong>。</p><p><strong>做完了也是浪费时间，很难有收获，并且没有正常人队友不推荐选。</strong></p><p>最后给分还行。</p></section><section><h4>数据库系统原理课程设计（大三下）<a href="#数据库系统原理课程设计大三下"><span>#</span></a></h4><p>在大三下学期开设，问了 2022 级的题目如下：</p><ul>
<li>题目1：基于 MiniOB 的数据库管理系统设计与实现
<ul>
<li>基于开源数据库内核 MiniOB ；</li>
<li>源于 OceanBase 数据库；</li>
<li>侧重于“造数据库”，了解 DBMS 内部查询处理、事务管理机制；</li>
</ul>
</li>
<li>题目2：TPC Benchmark 电商数据管理系统
<ul>
<li>侧重于“使用数据库”，基于现有 DBMS 平台，开发数据库应用系统（原型）</li>
<li>目标：初步了解、掌握数据库应用系统开发；熟悉 TPC-H、TPC-C 数据库测试基准；</li>
</ul>
</li>
</ul><p>学长的评价是不用 AI 就能写出来的起码有 icpc 金牌和两年以上数据库开发经验，验收结束，某老师发的要求原话如下：</p><p>@所有人 今天验收已结束。大家写报告的时候增加一个“ AI 使用说明”的章节。强调下课设任务完成过程中是否使用 AI 大模型辅助完成，如何使用的。强调清楚 AI 的贡献和大家各自的贡献。没有使用 AI 的也显式说明没有使用。</p><p>哈哈，这下真 AI 也来抢贡献度了。</p></section></section><section><h3>二选一<a href="#二选一-1"><span>#</span></a></h3><p>差不多，体感没什么太大区别，也有不少人为了卷绩点全选的。</p><section><h4>面向对象程序设计实践（Java）<a href="#面向对象程序设计实践java"><span>#</span></a></h4><p>老师是吴起凡老师，课很抽象，而且教的还是 Java8 的内容，基本没人听，到后面基本上全跑了，签过一两次到，不过似乎签不签都无所谓，上课有几次抽人回答问题过。</p><p>有 2 次 OJ 和 2 次小作业，OJ 上的题大模型基本都能速通，2 次小作业一般安排在临近期末由助教验收（助教一般会抽几题问你具体实现，看看你代码）。</p><p>大作业是典中典<strong>多人聊天室</strong>，4 ～ 6 人一组，这届又新加了一些小组聊天、语音通话等功能，由老师验收，一般是你简单演示功能，然后他会问一下具体的问题，包括用了设计模式诸如此类问题，还会重点关注你文档中的时序图、UML 图等。</p><p>最后给分还不错，没有计网课设给分如此抽象。</p></section><section><h4>面向对象程序设计实践（C++）<a href="#面向对象程序设计实践c"><span>#</span></a></h4><p>好像是做个电商平台？另外要求好像也不少，不过同样不考勤，水。</p></section></section><section><h3>二选一<a href="#二选一-2"><span>#</span></a></h3><section><h4>数字逻辑与数字系统课程设计<a href="#数字逻辑与数字系统课程设计"><span>#</span></a></h4><p>基础任务是电子钟 + 药片装瓶系统，往届代码一堆，VHDL/Verilog 实现。除基本的硬件平台（就是数电和计组实验用的那个箱子）之外，还提供一些别的企业的开发板。如果选择拓展的板子，优点是体积小巧，不需要来实验室调试，可以带回去；缺点同样明显，这些板子缺乏参考资料，并且与之适配的开发平台同样缺少参考资料，在连接和调试的过程中，遇到玄学甚至无法解决的问题肯定是在所难免的。至于选拓展板子有无隐式加分，这个没有调研过，不是很清楚，只能说想卷的可以卷。</p><p>有中期验收和期末验收，中期验收无所谓，主要是看一下进度。期末验收除了演示功能的实现外，老师还会问你一些设计思路等问题，杨秦老师还是很友善的。</p><p>除完成 2 个基础任务外，你可以自己选择拓展任务做，像贪吃蛇、俄罗斯方块这种。能卷出一个不错的分数，但即使累死累活调板子一学期，最后的给分也和网工只做基础的没区别。</p><blockquote><p>这届出分是真的晚，6.3 验收，7.16 才出成绩（怀疑是和计组课设一起出的）。至此，随着最后一门课出完，楼主的大二生涯也正式结束了。</p></blockquote></section><section><h4>计算机组成原理课程设计<a href="#计算机组成原理课程设计"><span>#</span></a></h4><p>没选，这门课安排在小学期，并且由于 2023 级刚好赶上教二装修，实验室搬到沙河去了，所以得乘班车去沙河上课。</p></section></section><section><h3>毛泽东思想和中国特色社会主义理论体系概论<a href="#毛泽东思想和中国特色社会主义理论体系概论"><span>#</span></a></h3><p>这可不敢乱评价（）。个人感觉比马原难背，不过楼主就花了一天时间背，因为这学期排考太逆天了。</p><p>但是事情太多了，有期中微电影，小组展示和一个实践部分的调查报告，难评。</p></section></section>
<section><h2>大三上学期<a href="#大三上学期"><span>#</span></a></h2><div><div><div></div><div>Warning</div></div><div><p>大三上是为数不多总体给分都很高（对大部分人） 的学期了。</p></div></div><section><h3>操作系统<a href="#操作系统"><span>#</span></a></h3><p>408的最后一块拼图，但是考的内容不难，作业题和往年题会做就行，考研题没啥作用。</p><p>孟xw教的，根据之前学长的描述早闻大名，“幸”得一见。实际上完也没有说的那么夸张，除了每节课基本都要小测以及及其复古的PPT，最后给分也还行。另外只有 2 次实验，只需提交报告。</p></section><section><h3>数据库系统原理<a href="#数据库系统原理"><span>#</span></a></h3><p>内容多，且考试英文题面（但还是操作系统的英文更抽象），是需要花较多时间复习的一门课。不过考试的题型也比较公式。有 7 次实验，需要使用华为云的 GaussDB，也是纯屎。</p><p>邓芳老师，讲得非常不错，顺便推荐一下疫情期间她的网课 <a href="https://www.bilibili.com/video/BV1mM411873E" target="_blank">数据库系统原理 北京邮电大学 2022 邓芳</a>。</p></section><section><h3>编译原理与技术<a href="#编译原理与技术"><span>#</span></a></h3><p>课程内容及其抽象，并且个人感觉用处不大。好在考试依旧比较公式，不会考非常阴间的点，和往年题差不太多。有 4 次实验，包含词法分析和语法分析，对理解和做题有一定的帮助，可以好好做做。</p></section><section><h3>算法设计与分析<a href="#算法设计与分析"><span>#</span></a></h3><p>内容本身对打算法竞赛意外以外的是比较有难度的，但是期末考的也越来越简单了，划定范围后甚至直接背那几个算法都行，有些甚至都不考代码。有 4 次实验。</p></section><section><h3>专业选修课<a href="#专业选修课"><span>#</span></a></h3><section><h4>计算机网络技术实践<a href="#计算机网络技术实践"><span>#</span></a></h4><p>推荐，不过事比较多。给分可以，具体内容可以参考<a href="/posts/setting-up-a-cisco-lab-on-linux-using-dynamips-and-dynagen/">博客</a>。不过每个老师用的仿真平台都不一样。</p></section><section><h4>程序设计实践<a href="#程序设计实践"><span>#</span></a></h4><p>推荐。做一个 DSL 脚本解释器实现的客服机器人，放在大模型时代更是及其水，想办法卷一个好看的功能齐全的前端应该就能有个不错的分数。并且提早验收有加分。</p></section><section><h4>Python程序设计<a href="#python程序设计"><span>#</span></a></h4><p>推荐。有几次 OJ 作业和实验。大作业是爬链家数据并分析，这个如果自己实打实爬会比较费力，这么多届都是爬的链家已经把现在的网站反爬养蛊了，有些固定风控基本没法避开。</p></section><section><h4>网络存储技术<a href="#网络存储技术"><span>#</span></a></h4><p>推荐。5篇小论文 + 1篇大论文，下次还填非常简单。</p></section><section><h4>人工智能原理<a href="#人工智能原理"><span>#</span></a></h4><p>期末开卷考试。</p></section></section></section>
<section><h2>大三下学期<a href="#大三下学期"><span>#</span></a></h2><section><h3>计算机系统结构<a href="#计算机系统结构"><span>#</span></a></h3><p>体感上并没有前人传的那么恐怖，内容确实比较多，知识点比较细。不过事实证明面向 PPT 和作业题复习基本没什么问题，往年题感觉过于古早了参考价值不如作业题。今年似乎也没有刻意压分的情况。至于 4 次小实验没什么意思和帮助，依旧用古早的 Win XP 软件仿真软件。</p></section><section><h3>现代交换原理<a href="#现代交换原理"><span>#</span></a></h3><p><strong>古代</strong>交换原理确实名不虚传，即使每年考纲确实都在变化，像今年基本大题完全没有 2G/3G 了，以 4G/5G 为主，甚至在实验里还加了 AI for network 的内容，不过依旧不改变只是表面工程。首先依旧很多内容还是老古董，而且完全搞不懂这门课对计算机科学与技术专业的意义是什么；其次即使强行违和地添加了也已经是四五年前的时间序列预测实验复现，但也只是个实验，课堂内容就是发个视频自学。</p><p>不过往年题确实没什么参考价值了，建议面向作业题复习，题型也是比较固定的。</p></section><section><h3>软件工程<a href="#软件工程"><span>#</span></a></h3><p>纯纯的文科课，软工也许有用但以考试的形式出现绝对没用，大作业依旧典中典充电桩系统，好在期中期末终于都争取到开卷了，因此只需要对概念有基本印象，然后掌握那几个图怎么画就行了（其实也没几个，面向对象明确不考了）。</p></section><section><h3>二选一<a href="#二选一-3"><span>#</span></a></h3><section><h4>编译原理与技术课程设计<a href="#编译原理与技术课程设计"><span>#</span></a></h4><p>依旧 Pascal-S 编译器，to C 或者 to 汇编，真是一场酣畅淋漓的调库啊。</p><p>wwf 老师给分也是顶级啊。</p></section><section><h4>操作系统课程设计<a href="#操作系统课程设计"><span>#</span></a></h4><p>没选，不清楚。</p></section></section><section><h3>专业选修课<a href="#专业选修课-1"><span>#</span></a></h3><section><h4>机器学习<a href="#机器学习"><span>#</span></a></h4><p>2 次云平台上布置的课前选择题 + 1 次线下计算题（最好去线下），1 次学术论文阅读笔记，然后 1 个大作业（两个赛道，分类/时间序列，感觉都是非常的水啊，满分不难）。水，推荐。</p></section><section><h4>下一代 Internet 技术与协议<a href="#下一代-internet-技术与协议"><span>#</span></a></h4><p>1 次实验 + 1 次报告，有时候会签到（会提前通知）。水，推荐。</p></section><section><h4>信息与知识获取<a href="#信息与知识获取"><span>#</span></a></h4><p>1 次报告 + 2 次大作业（信息检索/抽取系统），大作业一起验收。有时候会突然课后纸质签到，要注意。总体水，但老师比较难评。</p></section><section><h4>大数据技术基础<a href="#大数据技术基础"><span>#</span></a></h4><p>第一节课去了，看到一堆小组作业直接劝退了，而且今年又强行搞一堆所谓与时俱进的操作，什么部署🦞，实验指导书都一眼 AI 生成，事不少反正。</p><p>不得不提的是 ehh 在某方面也是声名远扬（你自己理解，不要仅凭给本科生上课被骗了）。</p></section><section><h4>无线传感器网络（高新标杆课）<a href="#无线传感器网络高新标杆课"><span>#</span></a></h4><p>水确实水，就 1 次要验收的实验和一个个人的报告，平常也没签到。不过给分很难评，并且年年容易引发争议且老师官回让有异议去申请查卷。</p></section><section><h4>Linux 开发环境及应用<a href="#linux-开发环境及应用"><span>#</span></a></h4><p>要考试，没选。</p></section></section><section><h3>专业实习<a href="#专业实习"><span>#</span></a></h3><p>最出生的课，把你拉到校外没空调的小教室坐牢三周，交通吃饭自行解决，占用周末时间，内容也是计算机网络技术实践的完全复刻，纯赤石。</p></section></section>
<section><h2>其他必修/选修课<a href="#其他必修选修课"><span>#</span></a></h2><section><h3>院级双创课程<a href="#院级双创课程"><span>#</span></a></h3><div><div><div></div><div>Warning</div></div><div><p><strong>注意仔细研读自己的培养方案，区分校级和院级双创学分，区分课程和实践学分，明确要求，年年有人错。</strong></p></div></div><p>按照培养方案的要求选 2 门即可（当然也可以通过科研训练等形式拿到学分），<strong>需要抢，建议尽早修满吧</strong>，但是会保证给即将毕业的学生选上。</p><section><h4>智能车实践训练<a href="#智能车实践训练"><span>#</span></a></h4><p>刁婷老师，人很好。任务是让智能车避障/寻线等，自行选择，难度不大，不过需要废点时间去实验室调车子，还有一篇调研报告。给分不错，<strong>推荐！</strong></p><p>另外似乎有祖传代码，不过笔者当时是和组员慢慢调的。</p></section><section><h4>智能机器人实践训练<a href="#智能机器人实践训练"><span>#</span></a></h4><p>李晶老师，任务是用机械臂去抓指定颜色的方块，难度也不大。实验室和智能车在一起（外训楼301）。</p></section><section><h4>大数据技术实践训练<a href="#大数据技术实践训练"><span>#</span></a></h4><p>用 nm 中国移动的梧桐大数据开发平台，纯狗屎，不超过 5 分钟用户 Token 就过期一次，并且跑得很慢（甚至怀疑偷了本机的算力）。最后验收是线下 PPT 答辩。给分还不错。</p></section><section><h4>移动应用开发实践训练<a href="#移动应用开发实践训练"><span>#</span></a></h4><p>没选，不清楚。</p></section><section><h4>机器学习实践训练<a href="#机器学习实践训练"><span>#</span></a></h4><p>同样没选，不清楚。</p></section></section><section><h3>大学英语<a href="#大学英语"><span>#</span></a></h3><section><h4>第三学期（视听说英语）<a href="#第三学期视听说英语"><span>#</span></a></h4><p>如果你是 B 班或者 C 班，第三学期是视听说英语，<strong>期末有口语考试</strong>，使用 iTEST 平台，对哑巴英语来说简直灾难，建议提前练练。</p></section><section><h4>第四学期<a href="#第四学期"><span>#</span></a></h4><p><strong>第四学期的选课需要抢</strong>，推荐以下课程：</p><ul>
<li>英美文化概况（<strong>推荐，如果是张爱阳老师，那推荐++</strong>）；
<ul>
<li>平时课上会互动，会请人发言，不过你回答不出来也没事，老师很和蔼；</li>
<li>云平台和灯塔的一些阅读作业 + 小组展示（表演一个英美影视片段）+ 短视频（介绍一个西方哲学家）+ 期末考试（2 篇阅读 + 3 篇作文）；</li>
<li><strong>期末考试开卷</strong>，可以准备些作文素材，但作文太阴间了，楼主水平也是稀烂，最后给分也不奢求了；</li>
</ul>
</li>
<li>实用英汉翻译（韩凌老师，听舍友评价也不错）；
<ul>
<li>期末考的翻译较难；</li>
</ul>
</li>
<li>思辨阅读与写作（穆婕老师，似乎也不错，给分不知）；</li>
</ul></section></section><section><h3>体育课<a href="#体育课"><span>#</span></a></h3><p>体育课除了大一，后面都是自选，但每学期不能重复，楼主运动能力属于还过得去，最后给分都是 90+，因此可能不太具有参考价值，1 学分的课也无所谓，因此建议选点喜欢的就可以。</p><section><h4>体育基础（太极拳）<a href="#体育基础太极拳"><span>#</span></a></h4><p>北邮经典 16 式太极拳，能打下来就行，有空多打打哈哈哈哈。</p></section><section><h4>田径<a href="#田径"><span>#</span></a></h4><p><strong>刘利老师</strong>，人很好，别看是田径但课上强度并不大，<strong>期末考 50m + 立定跳</strong>，平时课上还能刷一点校园跑。给分是如实给分，并且会在最后一节课左右全班公示，不过最后大家对分数都挺高的。（总之对楼主这种体育渣渣非常友好）</p></section><section><h4>篮球<a href="#篮球"><span>#</span></a></h4><p>考核是 4次罚篮 + 2 次三步上篮共 10 分 + 技评 90 分，最后按 50 分折算计入总成绩，还有个折返跑。</p></section><section><h4>羽毛球<a href="#羽毛球"><span>#</span></a></h4><p>应该也许是最难抢的。期末考核是对角线发高远球（两边各 5 次） + 击打高远球，分别占 30% 和 20%，击打到后场指定区域的成功率占一定比例，不过大头是技评。剩下的是 50m + 校园跑 + 课堂表现。</p></section></section></section>
<section><h1>专业分流<a href="#专业分流"><span>#</span></a></h1><div><div><div></div><div>Warning</div></div><div><p>24 级在大一下学期（五一左右）已经进行了分流。</p></div></div><p>计算机类会在大二上学期的中期，大概是 10 ～ 11 月份，进行专业分流。人数一般为计算机科学与技术 ： 网络工程 ： 数据科学与大数据技术 = 400 ： 60 ： 60 。网工和大数据一般会有一些高年级的留级下来，同时也有本年级的留下去。</p><p>关于建议，楼主只能说，论坛基本都是烟雾弹，年年都说博弈，但年年头部都在计科，所以小专业基本都稳赚不赔。不过具体选网工和大数据，这个确实有点博弈。</p><p>23级的分流情况是前20都在计科，网工第一21名，大数据第一39名。（没记错的话）</p><p>不过到24级网工和大数据又对调了，并且非常离谱。网工第一120名，大数据第一9名。</p><p>总之，保研基本不看你专业名称，所以<strong>想保外校大可以冲小专业</strong>。</p><div><div><div></div><div>Warning</div></div><div><p>笔者的建议仅供参考，请以实际情况和自身情况为准，笔者对任何后果不负责。</p></div></div></section>
<section><h1>转专业<a href="#转专业"><span>#</span></a></h1><p>北邮有 2 次转专业的机会。分别在大一上结束和大一下结束。一般大一上结束名额多并且更容易。</p><p>转计院和 AI 院都需要机试和面试，具体可参考 <a href="https://github.com/Yokumii/BUPT-SCS-Courses-Shared/blob/main/Additional-Information/Change-Major/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B1%BB%E8%BD%AC%E4%B8%93%E4%B8%9A%E6%8C%87%E5%AF%BC.pdf" target="_blank">计算机类转专业指导</a>。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/2026-5-live-repo/</id>
      <title type="text">2026.5 Live Repo</title>
      <published>2026-05-29T12:49:00.000Z</published>
      <updated>2026-05-29T12:49:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/2026-5-live-repo/"/>
      <summary type="text">翻了翻相册才发现上一次已经是去年 9 月的羊文学了，哎gszm坏事做尽。 这回的出行方式是经典京沪高铁二等座，去年锅贴得意之作绿皮 D9 真给我坐麻了，这次果断 G 系列。不过被课表背刺，本来还可以买早一班（），结果就是快 0 点才到虹桥。好像是第一次这么晚到沪国。</summary>
      <content type="html"><![CDATA[<section><h2>5月小结<a href="#5月小结"><span>#</span></a></h2><ul>
<li>5.1 上海梅赛德斯-奔驰文化中心 2026明日方舟音律联觉-昔时我见</li>
<li>5.24 北京 蛙厂RMMF·北京798店 ChiliChill乐团「混入人类计划II：方街」巡演-北京站</li>
</ul><p>翻了翻相册才发现上一次已经是去年 9 月的羊文学了，哎gszm坏事做尽。</p></section>
<section><h2>音律联觉<a href="#音律联觉"><span>#</span></a></h2><blockquote><p>依旧每年五一保留节目，这次还好二开抢到了。</p></blockquote><section><h3>音律前: 北京 to 上海<a href="#音律前-北京-to-上海"><span>#</span></a></h3><p>这回的出行方式是经典京沪高铁二等座，去年锅贴得意之作绿皮 D9 真给我坐麻了，这次果断 G 系列。不过被课表背刺，本来还可以买早一班（），结果就是快 0 点才到虹桥。好像是第一次这么晚到沪国。</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530131934826.jpeg" alt="" /></p><p>晚上六点多出发，地铁上已经挤的挂门了，夸张哦。不过快速进展厅是好文明啊😋，虽然人流也爆了。</p><p>一上车就听见了经典的沪上雅言，路上无话，不过因为节假日抢票时没法指定静音车厢，二等座还有销售在谈生意（</p><p>幽默锅贴，这下彻底不用赶最后一班 2 号线了：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530132635444.png" alt="" /></p><p>其实截图的时候还没停下，依旧偷偷摸摸装自己正点，懒得喷。</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530132803876.jpeg" alt="" /></p><p>从来没有这么晚到虹桥，没想到迎接我的是附近 200+ 打车，实际感觉远超，具体可见下图：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530132838027.jpeg" alt="" /></p><p>不过好在等了 10 分钟也是终于给我滴上了（高德打车屁用没有），在司机的指示下通过过街天桥到了对面的马路边，也是解锁新上车点了，依旧全是打车的，甚至马路边，交警也拦不住只能引导。甚至有乡毋宁电瓶车拉客的，真绷不住了。</p><p>另外等的时候已经看到不少背痛包的🪳了（笑）。</p><p>附一张从外面看虹桥枢纽：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530133216974.jpeg" alt="" /></p><p>到酒店进去前面一个人背的就是新能痛包，神了兄弟。</p></section><section><h3>音律入场前<a href="#音律入场前"><span>#</span></a></h3><p>没有提前到很久（经典地图了属于是），卡着点进的，拍了点外围：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530133359694.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530133607427.jpeg" alt="" /></p><p>来点 C 挡 522 区直拍，因为有点高且有点侧导致大屏上方会被阵列音箱挡住，PV 经常看不到人头（</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530133823243.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530133851317.jpeg" alt="" /></p></section><section><h3>音律中<a href="#音律中"><span>#</span></a></h3><p>由于众所周知的原因，演出肯定是 no japanese 的了，导致一些经典曲目都被拿下了，不过这回有很多多曲混编、或者改编，还是比较惊喜的，4 首新曲也很不错，个人比较喜欢戏中身：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>乱↑成线↓</span></div></div><div><div><div>2</div></div><div><span>不过山茶花落地→</span></div></div><div><div><div>3</div></div><div><span>三～菩～↓提↑</span></div></div><div><div><div>4</div></div><div><span>无意识而无依…如是→</span></div></div><div><div><div>5</div></div><div><span>我闻↑过…如是→</span></div></div><div><div><div>6</div></div><div><span>一花一木 皆成↑因↓</span></div></div><div><div><div>7</div></div><div><span>梦化成现实困住我↑↓</span></div></div><div><div><div>8</div></div><div><span>无所从 又无所去躲～</span></div></div><div><div><div>9</div></div><div><span>梦化！成现实困住我，</span></div></div><div><div><div>10</div></div><div><span>万生皆因果↓</span></div></div><div><div><div>11</div></div><div><span>惦↑朝↑↑霞↑↑↑</span></div></div><div><div><div>12</div></div><div><span>乱↑↑成↓麻～～</span></div></div><div><div><div>13</div></div><div><span>非以身普渡 又何渡泞路？</span></div></div><div><div><div>14</div></div><div><span>兀自聊以束 亦是空和无。</span></div></div><div><div><div>15</div></div><div><span>不知何求 与执念为谋，</span></div></div><div><div><div>16</div></div><div><span>贪和念无谓是色，即空——</span></div></div><div><div><div>17</div></div><div><span>非 空！！——</span></div></div><div><div><div>18</div></div><div><span>无知所故 与执念为伍，</span></div></div><div><div><div>19</div></div><div><span>淘尽灰↑色↓泥泞路——！</span></div></div><div><div><div>20</div></div><div><span>固执…（一段优雅的吟唱）</span></div></div><div><div><div>21</div></div><div><span>（一长段激烈的吟唱）</span></div></div><div><div><div>22</div></div><div><span>虔～诚如一↑</span></div></div><div><div><div>23</div></div><div><span>将往事送菩提！</span></div></div><div><div><div>24</div></div><div><span>无所依 无往 无妨且听着哭啼</span></div></div><div><div><div>25</div></div><div><span>走～进雾↑里↑</span></div></div><div><div><div>26</div></div><div><span>穿到界外断了人↑间↓雨～</span></div></div><div><div><div>27</div></div><div><span>我～盼↑如一 往事能送菩提</span></div></div><div><div><div>28</div></div><div><span>无所依 无往 无妨且走一遭</span></div></div><div><div><div>29</div></div><div><span>让那褪去的 如泡影般惨白的</span></div></div><div><div><div>30</div></div><div><span>化成儿时听说，</span></div></div><div><div><div>31</div></div><div><span>随↑花骨朵↓而～掉落——</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>（转载的），国风带点电就是🧊啊，加上 PV 质量依旧无敌。</p><p>哭狼也是超大杯，念诗那段也很震撼。Mili 稳定发挥。</p><p>另外利刃行动合约曲返场，也算是一个惊喜，希望之后也能像去年这种形式保留一定数量的经典曲目返场吧，以及小剧场环节。</p><p>来点可拍摄部分，看来塔子姐是要上岛了：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530134732453.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530134743950.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530134805807.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530134832412.jpeg" alt="" /></p><p>音箱挡脸真的红温（</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530134912016.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530134934779.jpeg" alt="" /></p><p>🥺已经在戒断了</p></section><section><h3>音律后<a href="#音律后"><span>#</span></a></h3><p>散场又是经典的大批人流涌入中华艺术宫，依旧多走点到耀华路回去取行李然后赶高铁，好在这回并不是很极限。</p><p>事已至此，先吃饭吧😋：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530135150525.jpeg" alt="" /></p><p>确实是很美味且性价比在联动产品里算高的了，爽吃，不过上海没得是真的快。</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530135256676.png" alt="" /></p></section></section>
<section><h2>ChiliChill「混入人类计划II：方街」<a href="#chilichill混入人类计划ii方街"><span>#</span></a></h2><section><h3>混入前<a href="#混入前"><span>#</span></a></h3><p>还是第一次来看 ChiliChill，之前错估了抢票难度导致等想起来的时候已经售罄了（很🔥，老师</p><p>798 这块也是很早就听说但第一次来，不得不说确实充满艺术气息（</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530135940141.jpeg" alt="" /></p><p>另外人真的多，按取号情况来看，应该 1200+，疯狂的厚嘴唇星人（</p><p>另外，蛙厂在我看过的所有 livehouse 里感觉能排到夯（综合音响/舞美/灯光/场地大小），个人感觉是 &gt; 疆进酒 &gt; 东三 &gt; 福浪。</p><p>线上取号是好文明啊，不用提前来排队，这回也是拼手速拿下：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530140105930.jpeg" alt="" /></p><p>不过秀动这个取号需要先刷新再取号，多次 UI 交互一言难尽，一坨屎简直。</p><p>最后选择了第三排靠右狙击于老师，等演出开始发现看小伍会有点挡（</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530140524235.jpeg" alt="" /></p><p>来点群友无料：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530140553716.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530140616553.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530140637556.jpeg" alt="" /></p><p>笑飞了，群友创意这一块，导播也是会配合的。</p></section><section><h3>混入中<a href="#混入中"><span>#</span></a></h3><p>开场真的难绷，方街设备故障，然后爽听 2 遍开场曲《混入人类计划》😋</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530140740432.jpeg" alt="" /></p><p>这辈子也是见到环卫工唱歌，空姐弹琴，医生弹吉他，建筑工人打鼓和女仆弹贝斯了。</p><p>女仆小伍无敌，扫地僧小夏真有活人感吧：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530141153586.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530141225301.jpeg" alt="" /></p><p>下等马，最喜欢的一集，荣登去年年度歌单，一！身！素！青！纱！</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530141325735.jpeg" alt="" /></p><p>后面就不一点点放了，终于给我听上 Live 版的《衡山路宛平路》了，泪目</p><p>真夫妻就是好磕啊：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530141435109.jpeg" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530141831331.jpeg" alt="" /></p><p>以及于老师又又又唱少男了，而且这回没有少男转少女，吃的真好罢😋</p><p>夏老师小巧思环节：</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530141911027.jpeg" alt="" /></p><p>这期神了，恍然大悟啊（笑）</p><p>安可飞鸟说，好耶</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530142019084.jpeg" alt="" /></p><p>金色的雨（bushi）</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530142048053.jpeg" alt="" /></p><blockquote><p>回去发现袋子里也喷进一堆</p></blockquote></section><section><h3>混入后<a href="#混入后"><span>#</span></a></h3><p>感谢七里秋收容不安灵魂！今天过得也很愉快👏👏👏</p><p><img src="https://webp-pic.yokumi.cn/2026/05/20260530142129363.jpeg" alt="" /></p><p>又要当个人继续和生活对线了，好想一起继续当厚嘴唇星人混入人类啊。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/notes-about-anthropic-mcp-skill/</id>
      <title type="text">MCP vs. Skill 旧瓶装新酒这一块</title>
      <published>2026-01-10T13:03:00.000Z</published>
      <updated>2026-01-10T13:03:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/notes-about-anthropic-mcp-skill/"/>
      <summary type="text">我在之前对 MCP 的学习中简单提到过当时 MCP 的一些痛点，不过发展到现在 MCP 这套通信协议已经被 Anthropic 搞得相当完善，MCP Server 也能快速适配到不同 Client。 从本质上来说，MCP 的功能侧重于包装了 Function Calling（函数调用），用户不需要具体 Tool 的逻…</summary>
      <content type="html"><![CDATA[<section><h2>MCP<a href="#mcp"><span>#</span></a></h2><p>我在之前对 MCP 的学习中简单提到过当时 MCP 的一些痛点，不过发展到现在 MCP 这套通信协议已经被 Anthropic 搞得相当完善，MCP Server 也能快速适配到不同 Client。</p><p>从本质上来说，MCP 的功能侧重于包装了 <strong>Function Calling（函数调用）</strong>，用户不需要具体 Tool 的逻辑。比如 Context7，封装了抓取网页、读文档等工具，LLM 在其加持下，引入并使用这些工具。</p><p>值得注意的是，作为 Agent 重点关注的 Context，MCP 的做法是将所有工具都放入上下文，</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260112213759677.png" alt="" /></p><p>上图是通过 Claude Code 的 <code>/context</code> 得到的上下文占用情况，可以看到，MCP tools 会固定占用一定的空间，即，LLM 每次都会携带完整的工具，包括具体的调用参数、方法等。LLM 正是通过这种方式，能根据用户输入判断是否需要进行工具调用，以及如何调用，并直接调用 MCP Tools，如下图所示：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260112215755049.png" alt="" /></p></section>
<section><h2>Skill<a href="#skill"><span>#</span></a></h2><p>Anthropic 可能是看到了 MCP 占用大量 Context 的问题，又提出了一个新的概念“<strong>Progressive Disclosure</strong>”（<strong>渐进式披露</strong>）以及 Agent Skill，如下图：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260112224100544.png" alt="" /></p><p>简单来说， 技能就是打包好的程序性知识，本质上就是文件夹。例如，Claude Code 中，每个 Skill 有一个 Skill.md：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260112232827652.png" alt="" /></p><p>它包含了提示词（Prompts）、脚本（Scripts）和说明文件等。看起来和 MCP 差不多，但两者真正的区别在运行时机制上。</p><ol>
<li>Skill 在运行时，一开始只暴露元数据（Metadate），对应到图中的 YAML 中前置配置，用于提供简要的工具信息，同时避免工作时占用过多 Context。</li>
<li>接下来，当用户指令或 LLM 发现请求与 Skill 的描述部分匹配时，就会触发并加载对应 Skill 的 Skill.md，此时包含详细信息的工具说明才进入上下文窗口。</li>
<li>如果文件目录下还有其他资源和代码，在调用工具时也会进行按需加载；</li>
</ol><p>因此，从本质上来说，Skill 就是按需暴露工具，从而不一次性并持久的占用上下文空间，从而节省了上下文窗口。</p></section>
<section><h2>End<a href="#end"><span>#</span></a></h2><p>之所以个人认为它是旧瓶装新酒，是因为本质上只是对 Agent 运行时使用和加载工具的时机进行了对 Context 进行优化，从功能上看没什么区别。并且，直接将 MCP 按照渐进式披露的思路进行转换，说不定更好，例如有一个类似的 MCP 工具 <code>mcp__pdf</code>，我们同样不加载一些具体的 Tool，一开始只告诉 LLM 有这个工具及其功能描述，当需要使用时，在通过类似 <code>mcp_pdf --help</code> 自行查看和加载需要的方法。甚至避免了在脚本上操作读取存在的安全性问题。</p><p>不过脚本由于其轻量且易于共享，因此个人认为 Skill 的出现还是属于进步的。把 Skill 认为是渐进加载的 MCP 也好，多个 MCP Tool 的整合或包装也好，现在都只需要写个通用提示词就能实现工具的选择和调用了。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/notes-about-syntax-directed-translation/</id>
      <title type="text">浅谈编译原理——语法制导翻译篇</title>
      <published>2025-12-06T08:57:00.000Z</published>
      <updated>2025-12-06T08:57:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/notes-about-syntax-directed-translation/"/>
      <summary type="text">对于编译器，一般不能只判断输入的语言是否符合语法规则。以算术表达式为例，还需要能确定最后的结果，即翻译目标为计算表达式的值。 语法制导就是如此，根据翻译目标， 语法制导翻译过程就是根据语法分析过程中所使用的产生式，在适当的时机执行与之相应的语义规则，完成符号属性值的计算，从而完成翻译。</summary>
      <content type="html"><![CDATA[<div><div><div></div><div>Note</div></div><div><p>尝试在抽象中建立一些理解。（起码对我来说很抽象）</p></div></div>
<section><h2>概述<a href="#概述"><span>#</span></a></h2><section><h3>语法制导翻译的功能<a href="#语法制导翻译的功能"><span>#</span></a></h3><p>对于编译器，一般不能只判断输入的语言是否符合语法规则。以算术表达式为例，还需要能确定最后的结果，即<strong>翻译目标</strong>为计算表达式的值。</p><p>语法制导就是如此，根据翻译目标，</p><ul>
<li>为上下文无关文法的每个符号设置<strong>语义属性</strong>（例如一个变量的属性有类型，层次，存储地址，一个表达式的属性有类型和值等）；</li>
<li>给每条产生式规则附加一个<strong>语义动作</strong>（本质上是一个代码片段，用于计算或输出语义属性的值），相当于对文法进行了拓广。</li>
</ul><p><strong>语法制导翻译</strong>过程就是根据语法分析过程中所使用的产生式，在适当的时机执行与之相应的语义规则，完成符号属性值的计算，从而完成翻译。</p></section><section><h3>语法制导定义（SDD）<a href="#语法制导定义sdd"><span>#</span></a></h3><p>语法制导定义：</p><ul>
<li>将文法符号和某些属性相关联</li>
<li>通过语义规则描述如何计算属性的值</li>
</ul><p>可以简单将语法制导定义理解为<strong>产生式 + 语义规则</strong>，例如：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251229212313370.png" alt="" /></p><p>语义规则的制定由翻译目标 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 决定产生式的含义 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 决定文法符号属性 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 决定产生式的语义规则。</p><p>需要注意的是，SDD 本身还无法给出语义规则的具体计算顺序。</p><p><strong>对应题型</strong>：写出语法制导定义。</p></section><section><h3>语法制导翻译（SDT）<a href="#语法制导翻译sdt"><span>#</span></a></h3><p>相较于 SDD，SDT 在上下文无关文法的产生式右部嵌入了程序片段，即<strong>语义动作</strong>，一个语义动作在产生式中的位置决定了这个动作的执行时间。</p><p>这样，编译器在语法分析过程中，在适当的时候执行这些语义动作，完成语义分析和检查。例如：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251229212344172.png" alt="" /></p><p>以第一条插入的语义动作为例，它表示当分析出T之后，就可以使用 T 的 type 属性的值来计算 L 的 in 属性值。</p><p>因此，SDT 可以视为对 SDD 的一种补充，是 SDD 的具体实施方案。</p><p><strong>对应题型</strong>：设计翻译方案。</p></section></section>
<section><h2>语法制导定义<a href="#语法制导定义"><span>#</span></a></h2><section><h3>产生式与语义规则<a href="#产生式与语义规则"><span>#</span></a></h3><p>对于每一个文法产生式 <span><span>A→X1X2⋯XnA\rightarrow X_1X_2\cdots X_n</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>⋯</span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，都有与之相联系的语义规则：</p><span><span><span>b=f(c1,c2,⋯ ,ck)b=f(c_1,c_2,\cdots,c_k)</span><span><span><span></span><span>b</span><span></span><span>=</span><span></span></span><span><span></span><span>f</span><span>(</span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span></span><p>其中：</p><ul>
<li><span><span>b,c1,c2,⋯ ,ckb,c_1,c_2,\cdots,c_k</span><span><span><span></span><span>b</span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>  都是某文法符号的属性；</li>
<li><span><span>ff</span><span><span><span></span><span>f</span></span></span></span> 是函数，比如具体的计算操作。</li>
</ul></section><section><h3>文法属性<a href="#文法属性"><span>#</span></a></h3><p><strong>属性文法</strong>扩展了上下文无关文法的定义，将文法符号与额外的属性（常见的包括值、类型等）建立了联系，具体又分为：</p><section><h4>综合属性<a href="#综合属性"><span>#</span></a></h4><p><strong>综合属性</strong>：通过子节点符号的属性或通过自身的属性计算得到。</p><ul>
<li><strong>通常用于从下往上传递信息</strong>。</li>
<li><span><span>bb</span><span><span><span></span><span>b</span></span></span></span> 是 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的一个综合属性，且 <span><span>c1,c2,⋯ ,ckc_1,c_2,\cdots,c_k</span><span><span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 为产生式右部符号的属性（即 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的子节点的属性），或 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的继承属性；</li>
<li>向上看继承，向下看子节点</li>
</ul><p><img src="https://webp-pic.yokumi.cn/2025/12/20251229215420772.png" alt="" /></p><div><div><div></div><div>Warning</div></div><div><p><strong>注意</strong>：你可能有以下疑惑，终结符是叶子节点，没有孩子了，可以有综合属性吗？答案是可以的，<em><strong>终结符可以有综合属性，词法分析程序提供的就是综合属性值，并且不能有继承属性</strong></em>。例如，<code>digit.lexval</code>，通常是一个常量。</p></div></div><p>还有一类比较特殊的属性，一般用于拓广文法的起始符号 <span><span>S′S^\prime</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span>，其不依赖任何属性，仅用于在属性计算完成后输出某个值或完成一项功能，例如 <code>Print(E.val)</code>，所以称其为<strong>虚拟综合属性</strong>。</p></section><section><h4>继承属性<a href="#继承属性"><span>#</span></a></h4><p><strong>继承属性</strong>：某节点的继承属性由它<strong>兄弟、父亲或者自己</strong>的属性计算得到。</p><ul>
<li><strong>通常用于自上而下，或横向传递信息</strong>。</li>
<li>如果 <span><span>bb</span><span><span><span></span><span>b</span></span></span></span> 是产生式右部某个符号 <span><span>XiX_i</span><span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的一个继承属性，那么  <span><span>c1,c2,⋯ ,ckc_1,c_2,\cdots,c_k</span><span><span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 是 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span>（它爹） 或任何产生式右部符号 <span><span>XjX_j</span><span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>（兄弟或它自己）的属性；</li>
<li>属性 <span><span>bb</span><span><span><span></span><span>b</span></span></span></span> 依赖于属性 <span><span>c1,c2,⋯ ,ckc_1,c_2,\cdots,c_k</span><span><span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>；</li>
</ul><p><img src="https://webp-pic.yokumi.cn/2025/12/20251229220432204.png" alt="" /></p></section></section><section><h3>注释分析树<a href="#注释分析树"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230103525529.png" alt="" /></p><p>一般默认约定：<strong>继承属性写文法符号的左边，综合属性写在右边</strong>。</p></section><section><h3>计算次序<a href="#计算次序"><span>#</span></a></h3><section><h4>依赖图<a href="#依赖图"><span>#</span></a></h4><p>分析树中不同的节点间的属性存在依赖关系，可以用依赖图表示。</p><p><strong>表示方法</strong>：</p><ul>
<li>分析树中，为符号的每个属性设置一个结点，一般画在符号旁边</li>
<li>如果属性 b 依赖于 c，那么存在一条 c -&gt; b 的有向边（被依赖者指向依赖者，因为只有 c 先算了才能算 b）；</li>
</ul><p>因此，属性的计算次序可以由依赖图的<strong>拓扑排序</strong>确定。</p><p>引入 2 种特殊的语法制导定义，这两种 SDD 的依赖图一定无环，因此可以通过拓扑排序计算出所有属性的属性值。</p></section></section><section><h3>S属性定义<a href="#s属性定义"><span>#</span></a></h3><p>即语法制导定义的所有属性都是综合属性，因此属性的计算过程就是自底向上的。因此可以配合自底向上的语法分析过程中实现。一般可以在进行归约时，按照语义规则计算归约得到的符号的属性值。</p><p>从依赖图来看，综合属性的边都是从下往上的。</p></section><section><h3>L属性定义<a href="#l属性定义"><span>#</span></a></h3><p>既有综合属性又有继承属性，但要求继承属性满足特定条件，即对于任意产生式 <span><span>A→X1X2⋯XnA\rightarrow X_1X_2\cdots X_n</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>⋯</span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 以及继承属性 <span><span>Xi.aX_i.a</span><span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>.</span><span>a</span></span></span></span>，其只能依赖于：</p><ul>
<li><span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的继承属性（父亲的继承属性）；
<ul>
<li>这里之所以只能依赖父亲的继承属性，是因为如果依赖父亲的综合属性，但父亲的综合属性又依赖子节点的属性，就会产生环路；</li>
</ul>
</li>
<li><span><span>X1,X2,⋯ ,Xi−1X_1,X_2,\cdots,X_{i-1}</span><span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span><span>i</span><span>−</span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的属性（左边兄弟的属性）；</li>
</ul><p>因此，从依赖图上来看，就是只允许：</p><ul>
<li>继承属性：<strong>从左向右，或从上到下</strong>的边；</li>
<li>综合属性：同 S 属性定义，<strong>从下到上</strong>的边；</li>
</ul><p>显然，每一个 S 属性定义都是 L 属性定义。</p></section><section><h3>构造依赖图<a href="#构造依赖图"><span>#</span></a></h3><p>在已经画出分析树的基础上，假如有产生式 <span><span>A→XYA\rightarrow XY</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span>Y</span></span></span></span>，对应的语义规则为 <code>A.a=f(X.x,Y.y)</code> 和 <code>X.i=g(A.a,Y.y)</code>。</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251206231051072.png" alt="" /></p></section><section><h3>计算次序<a href="#计算次序-1"><span>#</span></a></h3><p>计算顺序是有向非循环图的拓扑排序。</p><p>即必须按依赖图中箭头指向的方向进行排序。</p></section></section>
<section><h2>语法制导翻译<a href="#语法制导翻译"><span>#</span></a></h2><p><strong>翻译方案</strong>：把 SDD 的<strong>语义规则改写为计算属性值的程序片段</strong>，语义动作括在 <code>{}</code> 中，并插入到产生式右部某个合适的位置上。例如：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251207140642675.png" alt="" /></p><p>基本实现方法如下：</p><ol>
<li>建立语法分析树；</li>
<li>将语义动作看作是虚拟的结点；</li>
<li>从左到右，<strong>深度优先</strong>地遍历分析树，在访问虚拟结点时执行相应语义动作；</li>
</ol><section><h3>翻译方案的设计<a href="#翻译方案的设计"><span>#</span></a></h3><section><h4>S属性定义翻译<a href="#s属性定义翻译"><span>#</span></a></h4><ol>
<li>为每一个语义规则建立一个包含赋值的动作；</li>
<li>把这个动作放在相应的产生式右边末尾；</li>
</ol><p>这是比较显然的，因为都是综合属性，而父节点的综合属性依赖于子节点，因此需要等子节点先都分析完才能计算。例如，以产生式 <span><span>T→T1∗FT\rightarrow T_1*F</span><span><span><span></span><span>T</span><span></span><span>→</span><span></span></span><span><span></span><span><span>T</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∗</span><span></span></span><span><span></span><span>F</span></span></span></span> 和语义规则 <span><span>T.val=T1.val∗F.valT.val=T_1.val*F.val</span><span><span><span></span><span>T</span><span>.</span><span>v</span><span>a</span><span>l</span><span></span><span>=</span><span></span></span><span><span></span><span><span>T</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>.</span><span>v</span><span>a</span><span>l</span><span></span><span>∗</span><span></span></span><span><span></span><span>F</span><span>.</span><span>v</span><span>a</span><span>l</span></span></span></span> 为例，将语义动作如下插入：</p><span><span><span>T→T1∗F{T.val=T1.val∗F.val}T\rightarrow T_1*F\{T.val=T_1.val*F.val\}</span><span><span><span></span><span>T</span><span></span><span>→</span><span></span></span><span><span></span><span><span>T</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∗</span><span></span></span><span><span></span><span>F</span><span>{</span><span>T</span><span>.</span><span>v</span><span>a</span><span>l</span><span></span><span>=</span><span></span></span><span><span></span><span><span>T</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>.</span><span>v</span><span>a</span><span>l</span><span></span><span>∗</span><span></span></span><span><span></span><span>F</span><span>.</span><span>v</span><span>a</span><span>l</span><span>}</span></span></span></span></span><p>S 属性定义的基础文法是 <strong>LR 文法</strong>，其与 LR 分析过程是兼容的，当<strong>归约</strong>发生时，即可执行对应语义动作。</p></section><section><h4>L属性定义翻译<a href="#l属性定义翻译"><span>#</span></a></h4><p>L 属性定义既有综合属性又有继承属性，因此需要遵守以下原则：</p><ul>
<li>一个动作不能引用这个动作右边的文法符号的综合属性；
<ul>
<li>因为是处理过程是从左到右的，右边文法符号的综合属性还没有计算出来；</li>
</ul>
</li>
<li>对于<strong>产生式左部符号的综合属性</strong>：
<ul>
<li>只有在它所引用的所有属性都计算出来之后才能计算；</li>
<li>因此放在<strong>产生式右端末尾</strong>；（与 S 属性文法要求一致）</li>
</ul>
</li>
<li>对于<strong>产生式右部符号的继承属性</strong>：
<ul>
<li>必须在这个符号以前的动作中计算出来；</li>
<li>因此放在该文法符号前面；</li>
<li>即对于右部符号 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的继承属性的动作，<strong>插入到右部紧靠 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 之前的位置上（<span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的左边）</strong>；</li>
</ul>
</li>
</ul></section></section><section><h3>S属性定义的自底向上翻译<a href="#s属性定义的自底向上翻译"><span>#</span></a></h3><section><h4>语法树 vs. 分析树<a href="#语法树-vs-分析树"><span>#</span></a></h4><p><strong>具体语法树 vs. 抽象语法树 vs. 分析树</strong>：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230095203359.png" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230095230037.png" alt="" /></p><p>抽象语法树将节点按照以下操作抽象：</p><ol>
<li>对于<strong>叶子节点</strong>：用一个附加域存储此叶子结点的词法值/符号表入口
<ul>
<li><code>makeleaf(id, entry)</code>：建立一个标识符节点，标号为 id，附加域指向该标识符在符号表中条目的入口；</li>
<li><code>makeleaf(num, val)</code>：建立一个数节点，标号为 num，附加域直接存词法值；</li>
</ul>
</li>
<li>对于<strong>内部节点</strong>：附加字段数量等于结点的子结点数量，通过构造函数连接子节点
<ul>
<li><code>makenode(op, left, right)</code>：<code>op</code> 表示运算操作，<code>left/right</code> 分别指向左右孩子；</li>
</ul>
</li>
</ol></section><section><h4>构造 AST 及其语法制导定义<a href="#构造-ast-及其语法制导定义"><span>#</span></a></h4><p>对于 AST，产生式的语义应该是创建与产生式左部符号代表的子表达式对应的子树，即创建子树的根结点。</p><p>因此，文法符号的属性应包括 <code>nptr</code> 表示指向创建的子树的根节点，例如，对于产生式 <span><span>E→E1+TE\rightarrow E_1+T</span><span><span><span></span><span>E</span><span></span><span>→</span><span></span></span><span><span></span><span><span>E</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>T</span></span></span></span>，对应的语义规则为：</p><span><span><span>E.nptr=makenode(′+′,E1.nptr,T.nptr)E.nptr = makenode('+',E_1.nptr, T.nptr)</span><span><span><span></span><span>E</span><span>.</span><span>n</span><span>pt</span><span>r</span><span></span><span>=</span><span></span></span><span><span></span><span>mak</span><span>e</span><span>n</span><span>o</span><span>d</span><span>e</span><span><span>(</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span><span>+</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span>,</span><span></span><span><span>E</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>.</span><span>n</span><span>pt</span><span>r</span><span>,</span><span></span><span>T</span><span>.</span><span>n</span><span>pt</span><span>r</span><span>)</span></span></span></span></span><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230100658214.png" alt="" /></p><p>分析和翻译过程形如：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230100907725.png" alt="" /></p></section><section><h4>有向非循环图dag<a href="#有向非循环图dag"><span>#</span></a></h4><p>简单来说， 在语法树的基础上，公共表达式可以有多个父亲节点，节省了存储开销。</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230101312518.png" alt="" /></p></section><section><h4>自底向上的翻译过程实现（LR）<a href="#自底向上的翻译过程实现lr"><span>#</span></a></h4><p><strong>修改分析栈</strong>：使其能保存综合属性。</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230102100175.png" alt="" /></p><p><strong>改写语义规则</strong>：使之变成具体<strong>可执行的栈操作</strong>。</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251230102346117.png" alt="" /></p><p>简单来说，当要执行归约操作 <span><span>A→XYZ⋅A\rightarrow XYZ\cdot</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span>Y</span><span>Z</span><span>⋅</span></span></span></span>前，执行对应代码，先改写对应的符号栈，然后计算对应的综合属性栈。然后在执行归约操作，将 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 入栈，<span><span>X,Y,ZX,Y,Z</span><span><span><span></span><span>X</span><span>,</span><span></span><span>Y</span><span>,</span><span></span><span>Z</span></span></span></span> 出栈，栈顶变化为 <span><span>top→top−2top\rightarrow top - 2</span><span><span><span></span><span>t</span><span>o</span><span>p</span><span></span><span>→</span><span></span></span><span><span></span><span>t</span><span>o</span><span>p</span><span></span><span>−</span><span></span></span><span><span></span><span>2</span></span></span></span>。因此，综合属性刚好在每次归约前计算。</p><p>对于终结符，其在移进时，综合属性值（由词法分析程序提供）随状态一起入栈。</p></section></section><section><h3>L属性的自底向上翻译<a href="#l属性的自底向上翻译"><span>#</span></a></h3><p>主要介绍以下 4 种方法：</p><section><h4>1. 移走翻译方案中嵌入的语义规则<a href="#1-移走翻译方案中嵌入的语义规则"><span>#</span></a></h4><p>只要把嵌入（除产生式右部最右侧）在产生式右部之中的语义动作想办法移动到最右边，那么之后的分析思路与 S属性的翻译过程就类似了。</p><p>因此，针对嵌入在产生式右部的语义规则，例如 <span><span>R→+T{print(′+′)}RR\rightarrow +T\{print('+')\}R</span><span><span><span></span><span>R</span><span></span><span>→</span><span></span></span><span><span></span><span>+</span><span>T</span><span>{</span><span>p</span><span>r</span><span>in</span><span>t</span><span><span>(</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span><span>+</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span>)}</span><span>R</span></span></span></span>：</p><ul>
<li>引入一个新的非终结符 <span><span>MM</span><span><span><span></span><span>M</span></span></span></span>；</li>
<li>以及对应产生式 <span><span>M→ε{print(′+′)}M\rightarrow\varepsilon\{print('+')\}</span><span><span><span></span><span>M</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span><span>{</span><span>p</span><span>r</span><span>in</span><span>t</span><span><span>(</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span><span>+</span><span><span><span><span><span><span></span><span><span><span>′</span></span></span></span></span></span></span></span></span><span>)}</span></span></span></span></li>
</ul></section><section><h4>2. 直接使用分析栈中的继承属性<a href="#2-直接使用分析栈中的继承属性"><span>#</span></a></h4><p>使用该方法的前提是：</p><ul>
<li>继承属性在栈中</li>
<li><strong>位置已知</strong></li>
</ul></section><section><h4>3. 变换继承属性的计算规则<a href="#3-变换继承属性的计算规则"><span>#</span></a></h4><p>前一种方法中，虽然我们知道 LR 分析过程中，属性值都放在分析栈 val 中，但是无法知道继承属性的值在栈中的位置。</p><p>例如，如果同时包含产生式 <span><span>A→aXZA\rightarrow aXZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>a</span><span>X</span><span>Z</span></span></span></span> 和 <span><span>A→bXYZA\rightarrow bXYZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>b</span><span>X</span><span>Y</span><span>Z</span></span></span></span>，对应的语义动作均为 <span><span>Z.i=X.sZ.i = X.s</span><span><span><span></span><span>Z</span><span>.</span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>X</span><span>.</span><span>s</span></span></span></span>，即继承属性 <span><span>Z.iZ.i</span><span><span><span></span><span>Z</span><span>.</span><span>i</span></span></span></span> 依赖之前的 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span>，当问题是，在到达 <span><span>ZZ</span><span><span><span></span><span>Z</span></span></span></span> 并使用 <span><span>Z→zZ\rightarrow z</span><span><span><span></span><span>Z</span><span></span><span>→</span><span></span></span><span><span></span><span>z</span></span></span></span> 进行归约时，无法知道 <span><span>Z.iZ.i</span><span><span><span></span><span>Z</span><span>.</span><span>i</span></span></span></span> 该取 <code>val[top-1]</code> 还是 <code>val[top-2]</code>，即无法知道 <span><span>ZZ</span><span><span><span></span><span>Z</span></span></span></span> 离 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 有多远。</p><p>个人理解，解决的思路是想办法<strong>区分语义动作相同的产生式</strong>，例如如果产生式 <span><span>A→aXZA\rightarrow aXZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>a</span><span>X</span><span>Z</span></span></span></span> 以及语义动作均不变，我们在希望使用它时，<span><span>Z.iZ.i</span><span><span><span></span><span>Z</span><span>.</span><span>i</span></span></span></span> 需要的就是 <code>val[top-1]</code>。而对于产生式 <span><span>A→bXYZA\rightarrow bXYZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>b</span><span>X</span><span>Y</span><span>Z</span></span></span></span>，我们引入一个新的符号，并<strong>中转属性的继承关系</strong>：</p><ol>
<li>在 <span><span>ZZ</span><span><span><span></span><span>Z</span></span></span></span> 前面添加一个非终结符 <span><span>MM</span><span><span><span></span><span>M</span></span></span></span> 及产生式 <span><span>M→εM\rightarrow\varepsilon</span><span><span><span></span><span>M</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span>，使之紧挨着 <span><span>ZZ</span><span><span><span></span><span>Z</span></span></span></span>；</li>
<li>产生式 <span><span>A→bXYMZA\rightarrow bXYMZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>b</span><span>X</span><span>Y</span><span>M</span><span>Z</span></span></span></span> 对应的语义规则修改为 <span><span>M.i=X.s,Z.i=M.sM.i=X.s,Z.i=M.s</span><span><span><span></span><span>M</span><span>.</span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>X</span><span>.</span><span>s</span><span>,</span><span></span><span>Z</span><span>.</span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>M</span><span>.</span><span>s</span></span></span></span>；</li>
<li>产生式 <span><span>M→εM\rightarrow\varepsilon</span><span><span><span></span><span>M</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span> 对应的语义规则为 <span><span>M.s=M.iM.s = M.i</span><span><span><span></span><span>M</span><span>.</span><span>s</span><span></span><span>=</span><span></span></span><span><span></span><span>M</span><span>.</span><span>i</span></span></span></span>；</li>
</ol><p>因此，如果我们希望 <span><span>Z.iZ.i</span><span><span><span></span><span>Z</span><span>.</span><span>i</span></span></span></span> 继承自 <code>val[top-2]</code>，进行上述修改后，分析过程变成：当分析到 <span><span>MM</span><span><span><span></span><span>M</span></span></span></span> 之前，让 <span><span>M.iM.i</span><span><span><span></span><span>M</span><span>.</span><span>i</span></span></span></span> 去接收本来要继承给 <span><span>ZZ</span><span><span><span></span><span>Z</span></span></span></span> 的 <span><span>X.sX.s</span><span><span><span></span><span>X</span><span>.</span><span>s</span></span></span></span>，分析到 <span><span>MM</span><span><span><span></span><span>M</span></span></span></span> 时，计算 <span><span>M.s=M.iM.s = M.i</span><span><span><span></span><span>M</span><span>.</span><span>s</span><span></span><span>=</span><span></span></span><span><span></span><span>M</span><span>.</span><span>i</span></span></span></span>，接下来分析到 <span><span>ZZ</span><span><span><span></span><span>Z</span></span></span></span>，此时 <span><span>M.sM.s</span><span><span><span></span><span>M</span><span>.</span><span>s</span></span></span></span> 就位于 <code>val[top-1]</code>，<span><span>Z.iZ.i</span><span><span><span></span><span>Z</span><span>.</span><span>i</span></span></span></span> 能确定继承 <span><span>M.sM.s</span><span><span><span></span><span>M</span><span>.</span><span>s</span></span></span></span>。</p><p>上面只是针对“复制规则”，即直接等于，进一步可以考虑对所有继承属性的“<strong>非复制规则</strong>“进行改造，将其改造为复制规则，考虑：</p><ul>
<li><span><span>A→aXY,Y.i=f(X.s)A \rightarrow a X Y,Y.i = f(X.s)</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>a</span><span>X</span><span>Y</span><span>,</span><span></span><span>Y</span><span>.</span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>f</span><span>(</span><span>X</span><span>.</span><span>s</span><span>)</span></span></span></span>（<strong>非复制规则</strong>）</li>
<li><span><span>Y→y,Y.s=g(Y.i)Y → y,Y.s = g(Y.i)</span><span><span><span></span><span>Y</span><span></span><span>→</span><span></span></span><span><span></span><span>y</span><span>,</span><span></span><span>Y</span><span>.</span><span>s</span><span></span><span>=</span><span></span></span><span><span></span><span>g</span><span>(</span><span>Y</span><span>.</span><span>i</span><span>)</span></span></span></span></li>
</ul><p>同样，引入非终结符 <span><span>NN</span><span><span><span></span><span>N</span></span></span></span> 和对应产生式 <span><span>N→εN\rightarrow\varepsilon</span><span><span><span></span><span>N</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span>，将以上文法改写为：</p><ul>
<li><span><span>A→aXNY,N.i=X.s,Y.i=N.sA → a X N Y,N.i = X.s,Y.i = N.s</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>a</span><span>X</span><span>N</span><span>Y</span><span>,</span><span></span><span>N</span><span>.</span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>X</span><span>.</span><span>s</span><span>,</span><span></span><span>Y</span><span>.</span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>N</span><span>.</span><span>s</span></span></span></span></li>
<li><span><span>N→ε,N.s=f(N.i)N\rightarrow\varepsilon,N.s = f(N.i)</span><span><span><span></span><span>N</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span><span>,</span><span></span><span>N</span><span>.</span><span>s</span><span></span><span>=</span><span></span></span><span><span></span><span>f</span><span>(</span><span>N</span><span>.</span><span>i</span><span>)</span></span></span></span></li>
<li><span><span>Y→y,Y.s=g(Y.i)Y → y,Y.s = g(Y.i)</span><span><span><span></span><span>Y</span><span></span><span>→</span><span></span></span><span><span></span><span>y</span><span>,</span><span></span><span>Y</span><span>.</span><span>s</span><span></span><span>=</span><span></span></span><span><span></span><span>g</span><span>(</span><span>Y</span><span>.</span><span>i</span><span>)</span></span></span></span></li>
</ul><p>和复制规则基本一致，只是 <span><span>N→εN\rightarrow\varepsilon</span><span><span><span></span><span>N</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span> 中执行的计算改变。</p><p>在归约 <span><span>N→εN\rightarrow\varepsilon</span><span><span><span></span><span>N</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span> 前，栈中状态形如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>a       X(top)</span></div></div><div><div><div>2</div></div><div><span>a.lex   X.s</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当归约 <span><span>N→εN\rightarrow\varepsilon</span><span><span><span></span><span>N</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span> 时，N 入栈，且 <span><span>N.iN.i</span><span><span><span></span><span>N</span><span>.</span><span>i</span></span></span></span> 来自当前栈顶 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 对应的综合属性值处，因此 <code>N.s: val[top+1]=f(val[top])</code>。栈中状态变为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>a       X     N</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>a.lex   X.s   N.s</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><span><span>yy</span><span><span><span></span><span>y</span></span></span></span> 入栈：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>a       X     N(top-1)  y(top)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>a.lex   X.s   N.s       y.lex</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>随后归约 <span><span>Y→yY\rightarrow y</span><span><span><span></span><span>Y</span><span></span><span>→</span><span></span></span><span><span></span><span>y</span></span></span></span>，<span><span>yy</span><span><span><span></span><span>y</span></span></span></span> 出栈而 <span><span>YY</span><span><span><span></span><span>Y</span></span></span></span> 入栈，<span><span>Y.iY.i</span><span><span><span></span><span>Y</span><span>.</span><span>i</span></span></span></span> 来自于 <span><span>N.sN.s</span><span><span><span></span><span>N</span><span>.</span><span>s</span></span></span></span>，因此对应的执行过程为 <code>Y.s: val[top]=g(val[top-1])</code>。栈中状态变为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>a       X     N     Y</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>a.lex   X.s   N.s   Y.i</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>事实上按照上述方法构造，基本可以得出以下归约时执行动作的结论：</p>

<table><thead><tr><th><strong>归约</strong></th><th><strong>栈顶状态</strong></th><th><strong>语义本质</strong></th><th><strong>赋值位置</strong></th></tr></thead><tbody><tr><td>N → ε</td><td>top 是 X.s</td><td>N.s = f(X.s)</td><td>val[top+1] = f(val[top])</td></tr><tr><td>Y → y</td><td>top 是 y，top-1 是 N.s</td><td>Y.s = g(N.s)</td><td>val[top] = g(val[top-1])</td></tr></tbody></table></section><section><h4>4. 改写语法制导定义为S属性定义<a href="#4-改写语法制导定义为s属性定义"><span>#</span></a></h4><ul>
<li>改写文法</li>
<li>让信息传递方向与 LR 归约方向一致</li>
</ul><blockquote><p>😄<em><strong>依旧似懂非懂。</strong></em></p></blockquote></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/my-first-vps-setup-journey/</id>
      <title type="text">第一台 VPS 折腾小记</title>
      <published>2025-11-30T04:23:00.000Z</published>
      <updated>2025-11-30T04:23:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/my-first-vps-setup-journey/"/>
      <summary type="text">在使用本项目/教程/脚本之前，请务必仔细阅读以下声明。一旦您开始使用、复制或修改相关内容，即视为您已完全接受并同意本免责声明的所有条款。 请谨记：使用技术时请务必遵守您所在国家和地区的法律法规。 使用 IPv6-only VPS 低成本独享家宽，解锁 ChatGPT 不风控</summary>
      <content type="html"><![CDATA[<p>人生的意义在于折腾。</p>
<section><h2>宇宙级安全声明<a href="#宇宙级安全声明"><span>#</span></a></h2><p><strong>在使用本项目/教程/脚本之前，请务必仔细阅读以下声明。一旦您开始使用、复制或修改相关内容，即视为您已完全接受并同意本免责声明的所有条款。</strong></p><ol>
<li><strong>用途说明</strong>
<ul>
<li>本项目涉及的所有脚本、代码、配置文件及教程仅供 <strong>资源共享、技术研究和学术交流</strong> 使用。</li>
<li>本文作者对其中包含的信息的合法性、准确性、完整性和有效性 <strong>不作任何保证</strong>。用户应根据自身情况自行判断并承担相应风险。</li>
</ul>
</li>
<li><strong>免责条款</strong>
<ul>
<li>任何用户（包括但不限于个人、组织或自动化程序）间接使用本项目代码（包括但不限于建立 VPS、服务器配置或网络代理服务），或在违反其所在国家/地区法律法规的情况下进行传播和使用，<strong>本文作者对于由此引起的任何隐私泄漏、数据丢失、设备损坏或其他任何形式的后果概不负责</strong>。</li>
<li>作者不对任何脚本在使用过程中可能出现的问题负责，包括但不限于由脚本错误、逻辑漏洞或兼容性问题导致的任何直接或间接损失。</li>
</ul>
</li>
<li><strong>禁止行为</strong>
<ul>
<li><strong>严禁</strong> 将本项目内的任何内容用于 <strong>商业用途</strong> 或 <strong>非法目的</strong>。</li>
<li>不得利用本项目进行任何形式的网络攻击、非法入侵、绕过监管或其他违反当地法律法规的行为。</li>
<li>若用户利用本项目从事任何违法行为，一切法律后果由用户 <strong>自行承担</strong>，与本文作者无关。</li>
</ul>
</li>
<li><strong>知识产权与侵权处理</strong>
<ul>
<li>本项目均来自网络收集或个人学习总结。如果任何单位或个人认为该项目的脚本或内容可能涉嫌侵犯其合法权益，<strong>请及时通知并提供有效的身份证明、所有权证明及侵权情况说明</strong>。</li>
<li>作者在收到上述法律文件并核实后，将会在第一时间删除相关内容。</li>
</ul>
</li>
<li><strong>条款变更</strong>
<ul>
<li>本文作者保留在不预先通知的情况下，随时更改、补充或解释本免责声明的权利。</li>
<li>请用户定期查看本声明，以确保了解最新的条款内容。</li>
</ul>
</li>
</ol><p><strong>请谨记：使用技术时请务必遵守您所在国家和地区的法律法规。</strong></p></section>
<section><h2>0. 参考<a href="#0-参考"><span>#</span></a></h2><p><a href="https://linux.do/t/topic/1093618" target="_blank">使用 IPv6-only VPS 低成本独享家宽，解锁 ChatGPT 不风控</a></p><p><a href="https://linux.do/t/topic/333976" target="_blank">年轻人的第一台 VPS 代理服务器</a></p></section>
<section><h2>1. VPS 选择<a href="#1-vps-选择"><span>#</span></a></h2><p>这里不做推荐，我用的是 ipv6-only，一年 15 刀乐，折算一下的话一个月 9 块（加上域名也没多贵），在家宽里算很便宜了，而且可以通过 warp 获得 ipv4 地址，所以问题不是很大。这里先贴一张节点质量：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251210221149405.png" alt="" /></p><p>可以看到，非常纯净，一些服务有屏蔽是因为网站不支持纯 v6 访问，问题不大。</p></section>
<section><h2>2. VPS 搭建<a href="#2-vps-搭建"><span>#</span></a></h2><section><h3>2.1 连接并安装基础工具<a href="#21-连接并安装基础工具"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>user@hostname</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>连接成功后，安装：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span><span> &amp;&amp; </span><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>curl</span><span> </span><span>vim</span><span> </span><span>ufw</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>安装 Docker：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>apt-transport-https</span><span> </span><span>ca-certificates</span><span> </span><span>curl</span><span> </span><span>gnupg</span><span> </span><span>lsb-release</span><span> </span><span>-y</span></div></div><div><div><div>2</div></div><div><span># 创建 keyrings 目录</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/etc/apt/keyrings</span></div></div><div><div><div>4</div></div><div><span># 下载并添加 GPG 密钥</span></div></div><div><div><div>5</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://download.docker.com/linux/debian/gpg</span><span> | </span><span>sudo</span><span> </span><span>gpg</span><span> </span><span>--dearmor</span><span> </span><span>-o</span><span> </span><span>/etc/apt/keyrings/docker.gpg</span></div></div><div><div><div>6</div></div><div><span># 添加 Docker 仓库</span></div></div><div><div><div>7</div></div><div><span>echo</span><span> </span><span>"deb [arch=$(</span><span>dpkg</span><span> </span><span>--print-architecture</span><span>) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(</span><span>lsb_release</span><span> </span><span>-cs</span><span>) stable"</span><span> | </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/apt/sources.list.d/docker.list</span><span> &gt; </span><span>/dev/null</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span></div></div><div><div><div>10</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>docker-ce</span><span> </span><span>docker-ce-cli</span><span> </span><span>containerd.io</span><span> </span><span>docker-buildx-plugin</span><span> </span><span>docker-compose-plugin</span><span> </span><span>-y</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 安全性防护<a href="#22-安全性防护"><span>#</span></a></h3><blockquote><p>虽然 vps 厂商本身就提供了一定的安全保障措施，不过防人（爆破）之心的不可无。</p></blockquote><section><h4>2.2.1 创建用户组和用户<a href="#221-创建用户组和用户"><span>#</span></a></h4><p>将以下命令中的 <code>username</code> 和 <code>passwd</code> 自行替换即可：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>getent</span><span> </span><span>group</span><span> </span><span>1001</span><span> || </span><span>groupadd</span><span> </span><span>-g</span><span> </span><span>1001</span><span> </span><span>vps</span><span> </span><span>\</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&amp;&amp; </span><span>getent</span><span> </span><span>passwd</span><span> </span><span>username</span><span> || </span><span>useradd</span><span> </span><span>-ms</span><span> </span><span>/bin/bash</span><span> </span><span>-u</span><span> </span><span>1001</span><span> </span><span>-g</span><span> </span><span>1001</span><span> </span><span>-G</span><span> </span><span>sudo</span><span> </span><span>username</span><span> </span><span>\</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&amp;&amp; </span><span>echo</span><span> </span><span>"username:passwd"</span><span> | </span><span>chpasswd</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>创建用户组，创建用户并拉入附加组 sudo，并设置密码。</p></section><section><h4>2.2.2 修改 ssh 端口<a href="#222-修改-ssh-端口"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>nano</span><span> </span><span>/etc/ssh/sshd_config</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>找到被注释掉的 <code>Port 22</code> 并修改为：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>Port</span><span> </span><span>11451</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>记住这里你修改的端口号，如果你启动了防火墙需要进行对应的放行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span></div></div><div><div><div>2</div></div><div><span>ssh</span><span> </span><span>sudo</span><span> </span><span>ufw</span><span> </span><span>enable</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span><span> </span><span>11451</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>请务必在确认端口放行后再重启 ssh 服务：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>restart</span><span> </span><span>ssh</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>deny</span><span> </span><span>22</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>2.2.3 禁用 root 用户远程登录<a href="#223-禁用-root-用户远程登录"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo nano /etc/ssh/sshd_config</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>找到 <code>PermitRootLogin</code> 并修改为 <code>no</code>。</p></section><section><h4>2.2.4 RSA 密钥登录验证<a href="#224-rsa-密钥登录验证"><span>#</span></a></h4><p>略。</p></section><section><h4>2.2.5 Fail2ban 工具<a href="#225-fail2ban-工具"><span>#</span></a></h4><p>Fail2ban 工具用于对于密码连续输错三次封 IP：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>fail2ban</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>nano</span><span> </span><span>/etc/fail2ban/jail.local</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输入以下内容：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[DEFAULT]</span></div></div><div><div><div>2</div></div><div><span>bantime = 1h</span></div></div><div><div><div>3</div></div><div><span>findtime = 10m</span></div></div><div><div><div>4</div></div><div><span>maxretry = 3</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>[sshd]</span></div></div><div><div><div>7</div></div><div><span>enabled = true</span></div></div><div><div><div>8</div></div><div><span>port = 11451</span></div></div><div><div><div>9</div></div><div><span>filter = sshd</span></div></div><div><div><div>10</div></div><div><span>logpath = /var/log/auth.log</span></div></div><div><div><div>11</div></div><div><span>backend = systemd</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>保存并重启 fail2ban：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>restart</span><span> </span><span>fail2ban</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>enable</span><span> </span><span>fail2ban</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>2.3 通过 Warp 获取 ipv4 地址<a href="#23-通过-warp-获取-ipv4-地址"><span>#</span></a></h3><p>如果你和我一样是 v6-only 的 vps，那么可以通过套一层的 warp 来获得 ipv4 地址，这样后续在使用代理服务时，也能正常访问 github.com 等不支持 v6 的网站（虽然速度比较慢）。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wget</span><span> </span><span>-N</span><span> </span><span>https://gitlab.com/fscarmen/warp/-/raw/main/menu.sh</span><span> &amp;&amp; </span><span>bash</span><span> </span><span>menu.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果分配不到 ipv4 地址，可以修改 DNS 为：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>nano</span><span> </span><span>/etc/resolv.conf</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>nameserver</span><span> </span><span>2606:4700:4700::1111</span></div></div><div><div><div>4</div></div><div><span>nameserver</span><span> </span><span>2001:4860:4860::8888</span></div></div><div><div><div>5</div></div><div><span>nameserver</span><span> </span><span>2001:4860:4860::8844</span></div></div><div><div><div>6</div></div><div><span>nameserver</span><span> </span><span>1.1.1.1</span></div></div><div><div><div>7</div></div><div><span>nameserver</span><span> </span><span>8.8.8.8</span></div></div><div><div><div>8</div></div><div><span>nameserver</span><span> </span><span>8.8.4.4</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你可以通过 Cloudflare 的测试端点再次查看给你分配的地址：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-4</span><span> </span><span>https://www.cloudflare.com/cdn-cgi/trace</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>3. 域名配置<a href="#3-域名配置"><span>#</span></a></h2><section><h3>3.1 域名购买<a href="#31-域名购买"><span>#</span></a></h3><p>略。</p></section><section><h3>3.2 域名托管<a href="#32-域名托管"><span>#</span></a></h3><p>将域名从你的购买处托管到 Cloudflare，具体步骤略，在 Cloudflare 控制面板添加你的域名时已经提供了相当详细的步骤。</p></section><section><h3>3.3 申请 TLS 证书<a href="#33-申请-tls-证书"><span>#</span></a></h3><p>这里使用 <a href="https://acme.sh/" target="_blank">acme.sh</a> 开源工具，用来自动获取和管理 SSL/TLS 证书，自动续签，非常方便。</p><section><h4>3.3.1 创建令牌<a href="#331-创建令牌"><span>#</span></a></h4><p>该工具使用 Cloudflare DNS 验证的模式申请泛域名证书，因此需要：</p><ol>
<li>Zone ID</li>
<li>Account ID</li>
<li>API Token</li>
</ol><p>前两个分别在“域-&gt;Overview”右下角即可找到，令牌进入 <a href="https://dash.cloudflare.com/profile/api-tokens" target="_blank">API令牌</a> 获取，选择 <code>编辑区域 DNS</code> 模版：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251210235919311.png" alt="" /></p><p>选择对应域名，以及选择自己 VPS 的 IP 地址作为白名单（有 v4 和 v6 的话都写上）：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251211000414544.png" alt="" /></p><p>继续，注意保存生成的 Token。</p></section><section><h4>3.3.2 安装脚本<a href="#332-安装脚本"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>su</span></div></div><div><div><div>2</div></div><div><span># 脚本需要的定时服务</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span></div></div><div><div><div>4</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>cron</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>enable</span><span> </span><span>cron</span></div></div><div><div><div>6</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>start</span><span> </span><span>cron</span></div></div><div><div><div>7</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>socat</span><span> </span><span>-y</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 安装脚本并设置邮箱</span></div></div><div><div><div>10</div></div><div><span>curl</span><span> </span><span>https://get.acme.sh</span><span> | </span><span>sh</span><span> </span><span>-s</span><span> </span><span>email=your@email.com</span></div></div><div><div><div>11</div></div><div><span># 将 CA 服务器改成 Let’s Encrypt</span></div></div><div><div><div>12</div></div><div><span>~/.acme.sh/acme.sh --set-default-ca --server letsencrypt</span></div></div><div><div><div>13</div></div><div><span>source</span><span> </span><span>~/.bashrc</span></div></div><div><div><div>14</div></div><div><span># 开启自动更新</span></div></div><div><div><div>15</div></div><div><span>acme.sh</span><span> </span><span>--upgrade</span><span> </span><span>--auto-upgrad</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中，<code>email=xxxx</code> 用来设置向证书颁发机构注册时使用的邮箱，请填写为你实际使用的邮箱，用来接受 Let’s Encrypt 的邮件通知。</p></section><section><h4>3.3.3 配置环境变量<a href="#333-配置环境变量"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>~/.bashrc</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>CF_Token</span><span>=</span><span>"&lt;Your API Token&gt;"</span></div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>CF_Account_ID</span><span>=</span><span>"&lt;Your Account ID&gt;"</span></div></div><div><div><div>5</div></div><div><span>export</span><span> </span><span>CF_Zone_ID</span><span>=</span><span>"&lt;Your Zone ID&gt;"</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>source</span><span> </span><span>~/.bashrc</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>3.3.4 签发证书<a href="#334-签发证书"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>acme.sh</span><span> </span><span>--issue</span><span> </span><span>--dns</span><span> </span><span>dns_cf</span><span> </span><span>-d</span><span> </span><span>yourdomain.com</span><span> </span><span>-d</span><span> </span><span>*</span><span>.yourdomain.com</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>-d</code> 之后接上你需要签发的域名。如果成功，会在最后输出证书的位置。具体包括：</p><ul>
<li><code>yourdomain.com.cer</code>：只有域名的叶子证书。</li>
<li><code>ca.cer</code>：中间证书，用来构建完整的信任链。</li>
<li><code>fullchain.cer</code>：由域名证书与中间证书组合而成的完整证书链文件，配置服务器时常用此文件以确保客户端能正确验证证书链。</li>
</ul><p>通过以下命令，可以验证自动签发是否正确工作：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>crontab</span><span> </span><span>-l</span></div></div><div><div><div>2</div></div><div><span>2</span><span> </span><span>13</span><span> </span><span>*</span><span> </span><span>*</span><span> </span><span>*</span><span> </span><span>"/root/.acme.sh"/acme.sh</span><span> </span><span>--cron</span><span> </span><span>--home</span><span> </span><span>"/root/.acme.sh"</span><span> &gt; </span><span>/dev/null</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>例如，<code>2 13 * * *</code> 表示每天的 13&lt;02&gt; 执行一次。或者通过以下命令手动签发：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>acme.sh</span><span> </span><span>--cron</span><span> </span><span>--home</span><span> </span><span>/root/.acme.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>3.3.5 使用证书<a href="#335-使用证书"><span>#</span></a></h4><p>通常，采用 <code>fullchain.cer</code> 作为网站证书的配置。密钥只有一个，例如 <code>yourdomain.com.key</code>，对应域名证书的私钥，直接输入就行。</p><p>如需让 root 外用户能访问证书，还需要：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir -p /home/username/cert</span></div></div><div><div><div>2</div></div><div><span>cp /root/.acme.sh/yourdomain.com_ecc/yourdomain.com.key /home/username/cert/yourdomain.com.key</span></div></div><div><div><div>3</div></div><div><span>cp /root/.acme.sh/yourdomain.com_ecc/fullchain.cer /home/username/cert/fullchain.cer</span></div></div><div><div><div>4</div></div><div><span>sudo chmod 644 /home/username/cert/yourdomain.com.key</span></div></div><div><div><div>5</div></div><div><span>sudo chmod 644 /home/username/cert/fullchain.cer</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div></div><div>Warning</div></div><div><p><strong>注意</strong>：如果你在后面搭建节点或其他服务时，采用了 Docker 部署，容器是看不到宿主机的文件系统的，还需要移动到正确的映射路径。</p></div></div></section></section></section>
<section><h2>4. 节点搭建<a href="#4-节点搭建"><span>#</span></a></h2><section><h3>4.1 Hysteria<a href="#41-hysteria"><span>#</span></a></h3><p>参考：<a href="https://v2.hysteria.network/zh/docs/getting-started/Installation/" target="_blank">https://v2.hysteria.network/zh/docs/getting-started/Installation</a></p><p>Hysteria2 基于 UDP，在网络环境较差时表现优异。</p><section><h4>4.1.1 安装并运行官方脚本<a href="#411-安装并运行官方脚本"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>su</span></div></div><div><div><div>2</div></div><div><span>bash</span><span> </span><span>&lt;(</span><span>curl</span><span> </span><span>-fsSL</span><span> https://get.hy2.sh/)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>观察到输出 <code>Congratulation! Hysteria 2 has been successfully installed on your server.</code> 则证明安装成功。</p><p>如果提示 <code>useradd: command not found</code>，说明环境变量缺失。请先执行 <code>export PATH=$PATH:/usr/sbin</code> 后再次运行安装命令。</p></section><section><h4>4.1.2 创建配置文件<a href="#412-创建配置文件"><span>#</span></a></h4><p>我们需要手动创建配置文件。这里，我已经将证书文件移动到 <code>/home/username/cert/fullchain.cer</code> 和 <code>/home/username/cert/private.key</code>。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vi</span><span> </span><span>/etc/hysteria/config.yaml</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>粘贴并修改以下内容：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>listen</span><span>: </span><span>:8443</span><span> </span><span># 监听端口，需防火墙放行</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 使用之前申请的 CA 证书</span></div></div><div><div><div>4</div></div><div><span>tls</span><span>:</span></div></div><div><div><div>5</div></div><div><span>  </span><span>cert</span><span>: </span><span>/home/username/cert/fullchain.cer</span></div></div><div><div><div>6</div></div><div><span>  </span><span>key</span><span>: </span><span>/home/username/cert/yourdomain.com.key</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>auth</span><span>:</span></div></div><div><div><div>9</div></div><div><span>  </span><span>type</span><span>: </span><span>userpass</span></div></div><div><div><div>10</div></div><div><span>  </span><span>userpass</span><span>:</span></div></div><div><div><div>11</div></div><div><span>    </span><span># 修改这里：自定义用户名和密码</span></div></div><div><div><div>12</div></div><div><span>    </span><span>myuser1</span><span>: </span><span>password123</span></div></div><div><div><div>13</div></div><div><span>    </span><span>myuser2</span><span>: </span><span>password456</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>masquerade</span><span>:</span></div></div><div><div><div>16</div></div><div><span>  </span><span>type</span><span>: </span><span>proxy</span></div></div><div><div><div>17</div></div><div><span>  </span><span>proxy</span><span>:</span></div></div><div><div><div>18</div></div><div><span>    </span><span>url</span><span>: </span><span>https://bing.com</span><span> </span><span># 伪装网址</span></div></div><div><div><div>19</div></div><div><span>    </span><span>rewriteHost</span><span>: </span><span>true</span></div></div><div><div><div>20</div></div><div><span>  </span><span>listenHTTP</span><span>: </span><span>:80</span></div></div><div><div><div>21</div></div><div><span>  </span><span>listenHTTPS</span><span>: </span><span>:8443</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>trafficStats</span><span>: </span><span># 流量统计接口</span></div></div><div><div><div>24</div></div><div><span>  </span><span>listen</span><span>: </span><span>:22222</span></div></div><div><div><div>25</div></div><div><span>  </span><span>secret</span><span>: </span><span>trafficStats_passwd</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>bandwidth</span><span>:</span></div></div><div><div><div>28</div></div><div><span>  </span><span># 修改这里：根据你VPS的实际带宽调整，不要填满，留有余地</span></div></div><div><div><div>29</div></div><div><span>  </span><span>up</span><span>: </span><span>100 mbps</span></div></div><div><div><div>30</div></div><div><span>  </span><span>down</span><span>: </span><span>200 mbps</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>保存并退出，同时在 Cloudflare 控制面板添加对应的 A/AAAA 记录，指向你的服务器 IP，不要开启代理（云朵）。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>systemctl</span><span> </span><span>enable</span><span> </span><span>hysteria-server.service</span><span> </span><span>--now</span></div></div><div><div><div>2</div></div><div><span>systemctl</span><span> </span><span>status</span><span> </span><span>hysteria-server.service</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>启动并验证状态，如果观察到 <code>active (running)</code> 则证明配置并运行成功，否则请自行参照 Hysteria 官网检查。</p><p>注意如果有 ufw 还需要放行对应端口：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span><span> </span><span>8443</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如有问题，可通过以下命令观察调试信息：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 启动 Hysteria2</span></div></div><div><div><div>2</div></div><div><span>systemctl</span><span> </span><span>start</span><span> </span><span>hysteria-server.service</span></div></div><div><div><div>3</div></div><div><span># 重启 Hysteria2</span></div></div><div><div><div>4</div></div><div><span>systemctl</span><span> </span><span>restart</span><span> </span><span>hysteria-server.service</span></div></div><div><div><div>5</div></div><div><span># 查看 Hysteria2状态</span></div></div><div><div><div>6</div></div><div><span>systemctl</span><span> </span><span>status</span><span> </span><span>hysteria-server.service</span></div></div><div><div><div>7</div></div><div><span># 停止 Hysteria2</span></div></div><div><div><div>8</div></div><div><span>systemctl</span><span> </span><span>stop</span><span> </span><span>hysteria-server.service</span></div></div><div><div><div>9</div></div><div><span># 设置开机自启</span></div></div><div><div><div>10</div></div><div><span>systemctl</span><span> </span><span>enable</span><span> </span><span>hysteria-server.service</span></div></div><div><div><div>11</div></div><div><span># 查看日志</span></div></div><div><div><div>12</div></div><div><span>journalctl</span><span> </span><span>-u</span><span> </span><span>hysteria-server.service</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4.1.3 配置端口跳跃<a href="#413-配置端口跳跃"><span>#</span></a></h4><p>Hysteria 是基于 UDP 的，为了防止运营商针对单一 UDP 端口的阻断，我们需要利用 <code>iptables</code> 做端口转发。这意味着客户端连接 <code>50000-50100</code> 端口时，VPS 会自动转给 <code>8443</code> 处理。</p><ol>
<li><strong>确认网卡名称</strong>：输入 <code>ip addr</code> 查看，通常是 <code>eth0</code>，但也可能是 <code>ens3</code> 等。将下令中的 <code>eth0</code> 替换为你的真实网卡名。</li>
<li><strong>执行转发规则</strong>：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 放行对应端口</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span><span> </span><span>50220:50959/udp</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span><span> </span><span>60133:60968/udp</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 允许 UDP 端口段</span></div></div><div><div><div>6</div></div><div><span>iptables</span><span> </span><span>-t</span><span> </span><span>nat</span><span> </span><span>-A</span><span> </span><span>PREROUTING</span><span> </span><span>-i</span><span> </span><span>eth0</span><span> </span><span>-p</span><span> </span><span>udp</span><span> </span><span>--dport</span><span> </span><span>50220:50959</span><span> </span><span>-j</span><span> </span><span>REDIRECT</span><span> </span><span>--to-ports</span><span> </span><span>8443</span></div></div><div><div><div>7</div></div><div><span>iptables</span><span> </span><span>-t</span><span> </span><span>nat</span><span> </span><span>-A</span><span> </span><span>PREROUTING</span><span> </span><span>-i</span><span> </span><span>eth0</span><span> </span><span>-p</span><span> </span><span>udp</span><span> </span><span>--dport</span><span> </span><span>60133:60968</span><span> </span><span>-j</span><span> </span><span>REDIRECT</span><span> </span><span>--to-ports</span><span> </span><span>8443</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 对应的 IPv6 规则 (如果不需要 IPv6 可跳过)</span></div></div><div><div><div>10</div></div><div><span>ip6tables</span><span> </span><span>-t</span><span> </span><span>nat</span><span> </span><span>-A</span><span> </span><span>PREROUTING</span><span> </span><span>-i</span><span> </span><span>eth0</span><span> </span><span>-p</span><span> </span><span>udp</span><span> </span><span>--dport</span><span> </span><span>50220:50959</span><span> </span><span>-j</span><span> </span><span>REDIRECT</span><span> </span><span>--to-ports</span><span> </span><span>8443</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4.1.4 代理软件使用配置<a href="#414-代理软件使用配置"><span>#</span></a></h4><p>这里以 V2RayN 为例，其他软件类似，只要核心支持 Hysteria2 协议即可。点击”Configuration”-&gt;“Add[Hysteria2]“，配置如下，请修改为你自己的实际值：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251211151015565.png" alt="" /></p><p>如果能连通，说明上述配置成功。</p></section><section><h4>4.1.5 配置流量统计服务<a href="#415-配置流量统计服务"><span>#</span></a></h4><p>这里我偷懒了，建议在配置完 Nginx 之后在进行这一步骤。</p><p>安装一些所需依赖：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>jq</span><span> </span><span>bc</span><span> </span><span>-y</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>创建查询脚本：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>/root/check_traffic.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>写入并修改以下内容，域名和 API 地址也可以根据需要自行修改：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/bin/bash</span></div></div><div><div><div>2</div></div><div><span>green</span><span>=</span><span>'\033[0;32m'</span></div></div><div><div><div>3</div></div><div><span>cyan</span><span>=</span><span>'\033[0;36m'</span></div></div><div><div><div>4</div></div><div><span>NC</span><span>=</span><span>'\033[0m'</span><span> </span><span># No Color</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 请修改这里：你的域名</span></div></div><div><div><div>7</div></div><div><span>DOMAIN</span><span>=</span><span>"sub.yourdomain.com"</span></div></div><div><div><div>8</div></div><div><span># 请修改这里：你的 config.yaml 里设置的 secret</span></div></div><div><div><div>9</div></div><div><span>SECRET</span><span>=</span><span>"passwd"</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># API 地址 (走 HTTPS)</span></div></div><div><div><div>12</div></div><div><span>TRAFFIC_URL</span><span>=</span><span>"https://</span><span><span>${</span><span>DOMAIN</span><span>}</span></span><span>/hy-stats/traffic"</span></div></div><div><div><div>13</div></div><div><span>ONLINE_URL</span><span>=</span><span>"https://</span><span><span>${</span><span>DOMAIN</span><span>}</span></span><span>/hy-stats/online"</span></div></div><div><div><div>14</div></div><div><span>AUTH_HEADER</span><span>=</span><span>"Authorization: </span><span><span>${</span><span>SECRET</span><span>}</span></span><span>"</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 函数：将字节转换为可读格式</span></div></div><div><div><div>17</div></div><div><span>bytes_to_readable</span><span>() {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>local</span><span> </span><span>bytes</span><span><span>=</span><span>$1</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>if</span><span> (( </span><span>bytes</span><span><span> </span><span>&lt;</span><span> </span></span><span>1024</span><span> )); </span><span>then</span></div></div><div><div><div>20</div></div><div><span>        </span><span>printf</span><span> </span><span>"%d B"</span><span> </span><span>"</span><span>$bytes</span><span>"</span></div></div><div><div><div>21</div></div><div><span>    </span><span>elif</span><span> (( </span><span>bytes</span><span><span> </span><span>&lt;</span><span> </span></span><span>1024</span><span>**</span><span>2</span><span> )); </span><span>then</span></div></div><div><div><div>22</div></div><div><span>        </span><span>printf</span><span> </span><span>"%.2f KB"</span><span> </span><span>"$(</span><span>echo</span><span> "scale=2; </span><span>$bytes</span><span> / 1024" </span><span>|</span><span> </span><span>bc</span><span>)"</span></div></div><div><div><div>23</div></div><div><span>    </span><span>elif</span><span> (( </span><span>bytes</span><span><span> </span><span>&lt;</span><span> </span></span><span>1024</span><span>**</span><span>3</span><span> )); </span><span>then</span></div></div><div><div><div>24</div></div><div><span>        </span><span>printf</span><span> </span><span>"%.2f MB"</span><span> </span><span>"$(</span><span>echo</span><span> "scale=2; </span><span>$bytes</span><span> / (1024^2)" </span><span>|</span><span> </span><span>bc</span><span>)"</span></div></div><div><div><div>25</div></div><div><span>    </span><span>else</span></div></div><div><div><div>26</div></div><div><span>        </span><span>printf</span><span> </span><span>"%.2f GB"</span><span> </span><span>"$(</span><span>echo</span><span> "scale=2; </span><span>$bytes</span><span> / (1024^3)" </span><span>|</span><span> </span><span>bc</span><span>)"</span></div></div><div><div><div>27</div></div><div><span>    </span><span>fi</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>echo</span><span> </span><span>-e</span><span> </span><span>"</span><span><span>${</span><span>green</span><span>}</span></span><span>=== Hysteria2 流量与在线用户统计 ===</span><span><span>${</span><span>NC</span><span>}</span></span><span>"</span></div></div><div><div><div>31</div></div><div><span>echo</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span># 1. 获取在线用户数</span></div></div><div><div><div>34</div></div><div><span>echo</span><span> </span><span>-n</span><span> </span><span>"正在获取在线用户..."</span></div></div><div><div><div>35</div></div><div><span>online_resp</span><span><span>=</span><span>$(</span></span><span>curl</span><span> </span><span>-s</span><span> </span><span>-H</span><span> </span><span>"</span><span>$AUTH_HEADER</span><span>"</span><span> </span><span>"</span><span>$ONLINE_URL</span><span>"</span><span>)</span></div></div><div><div><div>36</div></div><div><span># 简单的整数判断，如果返回不是数字可能是认证失败</span></div></div><div><div><div>37</div></div><div><span>if</span><span> [[ </span><span>"</span><span>$online_resp</span><span>"</span><span> </span><span>=~</span><span> ^[0-9]+$ ]]; </span><span>then</span></div></div><div><div><div>38</div></div><div><span>    </span><span>echo</span><span> </span><span>-e</span><span> </span><span>"\r当前在线用户数: </span><span><span>${</span><span>cyan</span><span>}${</span><span>online_resp</span><span>}${</span><span>NC</span><span>}</span></span><span>"</span></div></div><div><div><div>39</div></div><div><span>else</span></div></div><div><div><div>40</div></div><div><span>    </span><span>echo</span><span> </span><span>-e</span><span> </span><span>"\r</span><span><span>${</span><span>cyan</span><span>}</span></span><span>获取在线用户失败 (可能是密钥错误或 Nginx 配置问题)</span><span><span>${</span><span>NC</span><span>}</span></span><span>"</span></div></div><div><div><div>41</div></div><div><span>    </span><span># echo "Debug: $online_resp" # 调试用</span></div></div><div><div><div>42</div></div><div><span>fi</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>echo</span><span> </span><span>"-----------------------------------"</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span># 2. 获取流量统计</span></div></div><div><div><div>47</div></div><div><span>response</span><span><span>=</span><span>$(</span></span><span>curl</span><span> </span><span>-s</span><span> </span><span>-w</span><span> </span><span>"HTTPSTATUS:%{http_code}"</span><span> </span><span>-H</span><span> </span><span>"</span><span>$AUTH_HEADER</span><span>"</span><span> </span><span>"</span><span>$TRAFFIC_URL</span><span>"</span><span>)</span></div></div><div><div><div>48</div></div><div><span>body</span><span><span>=</span><span>$(</span></span><span>echo</span><span> </span><span>"</span><span>$response</span><span>"</span><span> | </span><span>sed</span><span> </span><span>-e</span><span> </span><span>'s/HTTPSTATUS\:.*//g'</span><span>)</span></div></div><div><div><div>49</div></div><div><span>status_code</span><span><span>=</span><span>$(</span></span><span>echo</span><span> </span><span>"</span><span>$response</span><span>"</span><span> | </span><span>tr</span><span> </span><span>-d</span><span> </span><span>'\n'</span><span> | </span><span>sed</span><span> </span><span>-e</span><span> </span><span>'s/.*HTTPSTATUS://'</span><span>)</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>if</span><span> [ </span><span>"</span><span>$status_code</span><span>"</span><span><span> </span><span>-eq</span><span> </span></span><span>200</span><span> ]; </span><span>then</span></div></div><div><div><div>52</div></div><div><span>    </span><span># 使用 jq 解析 JSON</span></div></div><div><div><div>53</div></div><div><span>    </span><span># 格式: 用户名 上传 下载</span></div></div><div><div><div>54</div></div><div><span>    </span><span>echo</span><span> </span><span>"</span><span>$body</span><span>"</span><span> | </span><span>jq</span><span> </span><span>-r</span><span> </span><span>'to_entries[] | "\(.key) \(.value.tx) \(.value.rx)"'</span><span> | </span><span>while</span><span> </span><span>read</span><span> </span><span>-r</span><span> </span><span>key</span><span> </span><span>tx</span><span> </span><span>rx</span><span>; </span><span>do</span></div></div><div><div><div>55</div></div><div><span>        </span><span>readable_tx</span><span><span>=</span><span>$(</span></span><span>bytes_to_readable</span><span> </span><span>"</span><span>$tx</span><span>"</span><span>)</span></div></div><div><div><div>56</div></div><div><span>        </span><span>readable_rx</span><span><span>=</span><span>$(</span></span><span>bytes_to_readable</span><span> </span><span>"</span><span>$rx</span><span>"</span><span>)</span></div></div><div><div><div>57</div></div><div><span>        </span><span>echo</span><span> </span><span>-e</span><span> </span><span>"用户 </span><span><span>${</span><span>green</span><span>}</span><span>$key</span><span>${</span><span>NC</span><span>}</span></span><span>: 上传 </span><span><span>${</span><span>cyan</span><span>}${</span><span>readable_tx</span><span>}${</span><span>NC</span><span>}</span></span><span> | 下载 </span><span><span>${</span><span>cyan</span><span>}${</span><span>readable_rx</span><span>}${</span><span>NC</span><span>}</span></span><span>"</span></div></div><div><div><div>58</div></div><div><span>    </span><span>done</span></div></div><div><div><div>59</div></div><div><span>else</span></div></div><div><div><div>60</div></div><div><span>    </span><span>echo</span><span> </span><span>"流量请求失败，状态码：</span><span>$status_code</span><span>"</span></div></div><div><div><div>61</div></div><div><span>    </span><span>echo</span><span> </span><span>"请检查密钥或 HTTPS 连接。"</span></div></div><div><div><div>62</div></div><div><span>fi</span></div></div><div><div><div>63</div></div><div><span>echo</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h3>4.2 Xray<a href="#42-xray"><span>#</span></a></h3><section><h4>4.2.1  安装面板<a href="#421--安装面板"><span>#</span></a></h4><p>可以选择 x-ui 或者一个大佬魔改的 x-panel，没区别，x-panel 加了一些付费的一键配置等功能，对新手可能更友好吧。</p><p>如果安装 x-panel：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>docker-compose.yml</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>将以下内容添加进去：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>services</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>x-panel</span><span>:</span></div></div><div><div><div>3</div></div><div><span>    </span><span>image</span><span>: </span><span>xeefei/x-panel:latest</span></div></div><div><div><div>4</div></div><div><span>    </span><span>container_name</span><span>: </span><span>x-panel</span></div></div><div><div><div>5</div></div><div><span>    </span><span>restart</span><span>: </span><span>unless-stopped</span></div></div><div><div><div>6</div></div><div><span>    </span><span>network_mode</span><span>: </span><span>host</span></div></div><div><div><div>7</div></div><div><span>    </span><span>volumes</span><span>:</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>- </span><span>./db/:/etc/x-ui/</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>- </span><span>./cert:/root/cert/</span></div></div><div><div><div>10</div></div><div><span>    </span><span>environment</span><span>:</span></div></div><div><div><div>11</div></div><div><span>      </span><span>XRAY_VMESS_AEAD_FORCED</span><span>: </span><span>"false"</span></div></div><div><div><div>12</div></div><div><span>      </span><span>XUI_ENABLE_FAIL2BAN</span><span>: </span><span>"true"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>启动容器：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>docker</span><span> </span><span>compose</span><span> </span><span>up</span><span> </span><span>-d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>本地建立连接：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>-L</span><span> </span><span>13688:127.0.0.1:13688</span><span> </span><span>user@host</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后在本机访问 <a href="127.0.0.1:13688">127.0.0.1&lt;13688&gt;</a> 进行配置即可，默认用户名和密码均为 <code>admin</code>，登录后记得修改并重启面板。</p><p>如果你不想用 x-panel 或者不想用 docker 部署，可以参考大佬的这篇文章中的步骤 <a href="https://linux.do/t/topic/333976" target="_blank">年轻人的第一台 VPS 代理服务器</a>，思路都是大差不差的。</p></section><section><h4>4.2.2 面板设置<a href="#422-面板设置"><span>#</span></a></h4><p>点击面板设置中，找到 <code>面板证书公钥文件路径</code> 和 <code>面板证书密钥文件路径</code>，分别填入 <code>/root/cert/fullchain.cer</code> 和 <code>/root/cert/yourdomain.com.key</code>，注意这里跟前面的容器设置有关，如果按照我的设置，宿主机目录 <code>./cert</code>，即 <code>home/username/cert</code> 被映射到容器目录 <code>root/cert</code>，可通过以下命令验证：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>docker</span><span> </span><span>exec</span><span> </span><span>-it</span><span> </span><span>x-panel</span><span> </span><span>ls</span><span> </span><span>/root/cert</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img src="https://webp-pic.yokumi.cn/2025/12/20251211160548547.png" alt="" /></p><p>设置完成后，你就可以通过 <code>ip:port</code> （如果是 ipv6 地址请用 <code>[]</code> 括起来）的方式访问面板了，不过浏览器会提示不安全，如下图：</p><p><img src="https://webp-pic.yokumi.cn/2025/12/20251211160654604.png" alt="" /></p><p>这是因为证书中认证的域名并不符合 ip，请在 Cloudflare 控制面板再次添加对应的 A/AAAA 记录，指向你的服务器 IP，不要开启代理（云朵），若名为 <code>xui</code>，则在之后 Nginx 也配置完成后，你可以通过 <code>xui.yourdomain.com</code> 访问面板。</p></section><section><h4>4.2.3 搭建节点<a href="#423-搭建节点"><span>#</span></a></h4><p>这里提供目前 Xray-core 中比较顶尖和主流的抗干扰配置，VLESS + Vision + TLS 和 VLESS + Reality + xhttp，简单来说：</p><p>对于前者，Vision 专门用于解决“TLS in TLS”特征和流量指纹问题。它能极其逼真地模拟正常的 HTTPS 网页浏览流量，让防火墙认为你正在访问一个正常的加密网站，不过由于基于 TLS，因此必须拥有一个真实的域名和 SSL 证书。</p><p>对于后者，Reality 协议会通过中间人攻击的方式，偷取别的网站（如 Apple, Microsoft, Amazon 等）的 TLS 握手特征。因此不需要域名和证书。同时，xhttp 在 packet 层面做了更精细的 Padding 和头部处理，使得流量特征更像现代浏览器访问网站的行为，配合 Reality 使用效果极佳。</p><p>这里给出 Gemini 总结的两者的区别（我也不是特别懂）：</p>

<table><thead><tr><th><strong>特性</strong></th><th><strong>VLESS + Vision + TLS</strong></th><th><strong>VLESS + Reality + xhttp</strong></th></tr></thead><tbody><tr><td><strong>域名 (Domain)</strong></td><td><strong>必须拥有</strong> (每年需付费或申请免费)</td><td><strong>不需要</strong> (直接使用 IP)</td></tr><tr><td><strong>证书 (Cert)</strong></td><td><strong>需要管理</strong> (需申请并续期，如 acme.sh)</td><td><strong>不需要</strong> (模拟目标网站 SNI)</td></tr><tr><td><strong>CDN 支持</strong></td><td><strong>支持</strong> (但在 Vision 下不建议开启 CDN)</td><td><strong>不支持</strong> (绝对无法套 CDN)</td></tr><tr><td><strong>抗封锁能力</strong></td><td>极高 (只要域名干净，IP 正常)</td><td>极高 (专门针对主动探测优化)</td></tr><tr><td><strong>配置难度</strong></td><td>中等 (需解析域名、配置 Nginx/回落)</td><td>简单 (填入 IP 和目标 SNI 即可)</td></tr><tr><td><strong>端口要求</strong></td><td>通常占用 443 端口</td><td>任意端口 (但推荐 443 以模拟真实)</td></tr><tr><td><strong>适用场景</strong></td><td>长期稳定使用，不怕麻烦，有建站需求</td><td>快速部署，不想买域名，IP 经常变动</td></tr></tbody></table><p>这里因为我有域名，所以选择更稳定的“VLESS + Vision + TLS”，入站规则的创建我就省略了，基本都按照默认，记得在 <strong>SNI (域名)</strong> 处填写你的自己的三级域名，同时在证书设置部分填写之前准备好的证书路径，flow 记得选择 <code>xtls-rprx-vision</code>。</p><p>然后放行对应的端口。同理，在 Cloudflare 控制面板添加对应的 A/AAAA 记录，指向你的服务器 IP，不要开启代理（云朵），名字和你入站规则中的域名一致。</p><p>搞完之后可以在 V2rayN 中先测一下，没问题的话再继续。</p></section></section></section>
<section><h2>5. 订阅服务搭建<a href="#5-订阅服务搭建"><span>#</span></a></h2><p>这里暂时先不折腾了。</p><div><div><div></div><div>Warning</div></div><div><p>由于 VPS 大部分是小内存机器（例如 512MB 或 1GB），且此时已经运行了 Hysteria、Nginx 等服务，在进行 Sub-Store 的安装和配置时极有可能导致内存溢出，进程被强行杀死，如果遇到这种情况，可以尝试从硬盘上划分一部分空间当作“虚拟内存”来使用，防止内存不足导致程序崩溃：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 创建一个 1GB 的文件用于 swap</span></div></div><div><div><div>2</div></div><div><span>dd</span><span> </span><span>if=/dev/zero</span><span> </span><span>of=/swapfile</span><span> </span><span>bs=1M</span><span> </span><span>count=</span><span>1024</span></div></div><div><div><div>3</div></div><div><span># 设置权限，确保安全</span></div></div><div><div><div>4</div></div><div><span>chmod</span><span> </span><span>600</span><span> </span><span>/swapfile</span></div></div><div><div><div>5</div></div><div><span># 将其标记为 swap 空间</span></div></div><div><div><div>6</div></div><div><span>mkswap</span><span> </span><span>/swapfile</span></div></div><div><div><div>7</div></div><div><span># 启用 swap</span></div></div><div><div><div>8</div></div><div><span>swapon</span><span> </span><span>/swapfile</span></div></div><div><div><div>9</div></div><div><span># (可选) 写入系统配置，确保重启后依然有效</span></div></div><div><div><div>10</div></div><div><span>echo</span><span> </span><span>'/swapfile none swap sw 0 0'</span><span> | </span><span>tee</span><span> </span><span>-a</span><span> </span><span>/etc/fstab</span></div></div><div><div><div>11</div></div><div><span># 查看是否生效</span></div></div><div><div><div>12</div></div><div><span>free</span><span> </span><span>-h</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果配置正确，应该能看到 <code>Swap:</code> 这一行有了 <code>1.0Gi</code> 左右的空间。</p></div></div><section><h3>5.1 安装基础组件与 FNM<a href="#51-安装基础组件与-fnm"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 更新并安装 unzip 等工具</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span><span> </span><span>-y</span><span> &amp;&amp; </span><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>unzip</span><span> </span><span>curl</span><span> </span><span>wget</span><span> </span><span>git</span><span> </span><span>-y</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 安装 FNM</span></div></div><div><div><div>5</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://fnm.vercel.app/install</span><span> | </span><span>bash</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 让 FNM 生效</span></div></div><div><div><div>8</div></div><div><span>source</span><span> </span><span>/root/.bashrc</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.2 安装 Node.js v20.18.0<a href="#52-安装-nodejs-v20180"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>fnm</span><span> </span><span>install</span><span> </span><span>v20.18.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.3 安装 PNPM<a href="#53-安装-pnpm"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://get.pnpm.io/install.sh</span><span> | </span><span>sh</span><span> </span><span>-</span></div></div><div><div><div>2</div></div><div><span># 同样让它生效</span></div></div><div><div><div>3</div></div><div><span>source</span><span> </span><span>/root/.bashrc</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.4 安装 Sub-Store<a href="#54-安装-sub-store"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/root/sub-store</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>/root/sub-store</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 下载后端</span></div></div><div><div><div>5</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store.bundle.js</span><span> </span><span>-o</span><span> </span><span>sub-store.bundle.js</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 下载前端并解压</span></div></div><div><div><div>8</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://github.com/sub-store-org/Sub-Store-Front-End/releases/latest/download/dist.zip</span><span> </span><span>-o</span><span> </span><span>dist.zip</span></div></div><div><div><div>9</div></div><div><span>unzip</span><span> </span><span>dist.zip</span><span> &amp;&amp; </span><span>mv</span><span> </span><span>dist</span><span> </span><span>frontend</span><span> &amp;&amp; </span><span>rm</span><span> </span><span>dist.zip</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.5 配置 Sub-Store 系统服务<a href="#55-配置-sub-store-系统服务"><span>#</span></a></h3><section><h4>5.5.1 配置密钥<a href="#551-配置密钥"><span>#</span></a></h4><p>随便生成一个密钥并记下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>openssl</span><span> </span><span>rand</span><span> </span><span>-hex</span><span> </span><span>16</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>5.5.2 创建服务文件<a href="#552-创建服务文件"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>/etc/systemd/system/sub-store.service</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>写入并修改以下内容：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[Unit]</span></div></div><div><div><div>2</div></div><div><span>Description=Sub-Store</span></div></div><div><div><div>3</div></div><div><span>After=network-online.target</span></div></div><div><div><div>4</div></div><div><span>Wants=network-online.target systemd-networkd-wait-online.service</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>[Service]</span></div></div><div><div><div>7</div></div><div><span>LimitNOFILE=32767</span></div></div><div><div><div>8</div></div><div><span>Type=simple</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 修改这里：这一串随机字符就是你以后访问 Sub-Store 的路径</span></div></div><div><div><div>11</div></div><div><span>Environment="SUB_STORE_FRONTEND_BACKEND_PATH=/你的随机密钥"</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>Environment="SUB_STORE_BACKEND_SYNC_CRON=0 0 * * *"</span></div></div><div><div><div>14</div></div><div><span>Environment="SUB_STORE_FRONTEND_PATH=/root/sub-store/frontend"</span></div></div><div><div><div>15</div></div><div><span>Environment="SUB_STORE_FRONTEND_HOST=0.0.0.0"</span></div></div><div><div><div>16</div></div><div><span># 前端运行在 3001 端口</span></div></div><div><div><div>17</div></div><div><span>Environment="SUB_STORE_FRONTEND_PORT=3001"</span></div></div><div><div><div>18</div></div><div><span>Environment="SUB_STORE_DATA_BASE_PATH=/root/sub-store"</span></div></div><div><div><div>19</div></div><div><span>Environment="SUB_STORE_BACKEND_API_HOST=127.0.0.1"</span></div></div><div><div><div>20</div></div><div><span>Environment="SUB_STORE_BACKEND_API_PORT=3000"</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span># 启动命令 (确保路径正确)</span></div></div><div><div><div>23</div></div><div><span>ExecStart=/root/.local/share/fnm/fnm exec --using v20.18.0 node /root/sub-store/sub-store.bundle.js</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>User=root</span></div></div><div><div><div>26</div></div><div><span>Group=root</span></div></div><div><div><div>27</div></div><div><span>Restart=on-failure</span></div></div><div><div><div>28</div></div><div><span>RestartSec=5s</span></div></div><div><div><div>29</div></div><div><span>ExecStartPre=/bin/sh -c ulimit -n 51200</span></div></div><div><div><div>30</div></div><div><span>StandardOutput=journal</span></div></div><div><div><div>31</div></div><div><span>StandardError=journal</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>[Install]</span></div></div><div><div><div>34</div></div><div><span>WantedBy=multi-user.target</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>5.5.3 启动服务<a href="#553-启动服务"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>systemctl</span><span> </span><span>daemon-reload</span></div></div><div><div><div>2</div></div><div><span>systemctl</span><span> </span><span>enable</span><span> </span><span>sub-store.service</span><span> </span><span>--now</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如出现错误请使用以下命令打印调试信息自行调试：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>systemctl</span><span> </span><span>start</span><span> </span><span>sub-store.service</span><span>     </span><span># 启动服务</span></div></div><div><div><div>2</div></div><div><span>systemctl</span><span> </span><span>enable</span><span> </span><span>sub-store.service</span><span>    </span><span># 设置为开机自启</span></div></div><div><div><div>3</div></div><div><span>systemctl</span><span> </span><span>status</span><span> </span><span>sub-store.service</span><span>    </span><span># 查看服务状态</span></div></div><div><div><div>4</div></div><div><span>systemctl</span><span> </span><span>stop</span><span> </span><span>sub-store.service</span><span>      </span><span># 停止服务</span></div></div><div><div><div>5</div></div><div><span>systemctl</span><span> </span><span>restart</span><span> </span><span>sub-store.service</span><span>   </span><span># 重启服务</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>
<section><h2>6. Nginx 配置<a href="#6-nginx-配置"><span>#</span></a></h2><p>由于上述服务暴露在公网，因此暴露端口有一定的安全隐患，采用 Nginx 进行反代，将 443 加密流量在 Nginx 内部分流到不同的本地端口，本地端口不需要暴露。</p><section><h3>6.1 安装<a href="#61-安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>nginx</span><span> </span><span>libnginx-mod-stream</span><span> </span><span>-y</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 配置 Stream 分流<a href="#62-配置-stream-分流"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>/etc/nginx/nginx.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 <code>http</code> 块外部添加我们即将要引入的分流规则：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>include /etc/nginx/vps.conf;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>配置 Stream 分流 (<code>vps.conf</code>)：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>/etc/nginx/vps.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>填入以下内容（注意修改域名和端口为你实际需要使用的）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>stream {</span></div></div><div><div><div>2</div></div><div><span>    </span><span># 1. 域名识别区 (SNI Map)</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>map $ssl_preread_server_name $backend {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>hostnames</span><span>;</span></div></div><div><div><div>5</div></div><div><span>        </span><span># 修改这里：你的 Sub-Store 域名 -&gt; 转发给 sub 后端</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>sub.yourdomain.com    sub</span><span>;</span></div></div><div><div><div>7</div></div><div><span>        </span><span># 修改这里：你的 X-UI 面板域名 -&gt; 转发给 xui 后端</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>xui.yourdomain.com    xui</span><span>;</span></div></div><div><div><div>9</div></div><div><span>        </span><span># 默认情况（比如直接访问 IP 或 hy2 域名） -&gt; 转发给 hysteria</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>default          hysteria</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span># 2. 后端定义区 (Upstreams)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span># Sub-Store 后端：转发给 Nginx 自己的 8444 端口 (处理 SSL 后再给 Sub-Store)</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>upstream sub {</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>server 127.0.0.1:8444</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span># X-UI 面板后端：直接转发给 X-UI 面板端口</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>upstream xui {</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>server 127.0.0.1:13688</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span># Hysteria 后端：转发给 Hysteria 的 HTTP 伪装端口</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>upstream hysteria {</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>server 127.0.0.1:8443</span><span>;</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span># 3. 监听服务区</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>server {</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>listen 443 reuseport</span><span>;</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>listen [::]:443 reuseport</span><span>;</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>proxy_pass $backend</span><span>;</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>ssl_preread on</span><span>; # 开启 SNI 预读，否则无法识别域名</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>37</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.3 配置 Sub-Store 反代<a href="#63-配置-sub-store-反代"><span>#</span></a></h3><p>Stream 模块只是把流量扔过来了，对于 Sub-Store，我们还需要 Nginx 帮忙处理 HTTPS 加密（卸载证书），然后以 HTTP 协议传给 Sub-Store 程序（默认运行在 3001 端口）。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>/etc/nginx/sites-enabled/vps.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>写入并修改以下内容：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>server</span><span> </span><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span># 监听 8444，接收来自 Stream 模块分流过来的 sub 流量</span></div></div><div><div><div>3</div></div><div><span>    </span><span>listen</span><span> </span><span>8444</span><span> </span><span>ssl</span><span> </span><span>http2</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>listen</span><span> [::]:8444 ssl http2;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span># 修改这里：你的 Sub-Store 域名</span></div></div><div><div><div>7</div></div><div><span>    </span><span>server_name</span><span> </span><span>sub.yourdomain.com</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span># 修改这里：你的证书绝对路径</span></div></div><div><div><div>10</div></div><div><span>    </span><span>ssl_certificate</span><span> </span><span>/home/username/fullchain.cer</span><span>;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>ssl_certificate_key</span><span> </span><span>/home/username/private.key</span><span>;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>location</span><span> </span><span>/</span><span> </span><span>{</span></div></div><div><div><div>14</div></div><div><span>        </span><span># 转发给 Sub-Store 默认端口 3001</span></div></div><div><div><div>15</div></div><div><span>        </span><span>proxy_pass</span><span> </span><span>http://127.0.0.1:3001</span><span>;</span></div></div><div><div><div>16</div></div><div><span>        </span><span>proxy_set_header</span><span> </span><span>Host</span><span> </span><span>$host</span><span>;</span></div></div><div><div><div>17</div></div><div><span>        </span><span>proxy_set_header</span><span> </span><span>X-Real-IP</span><span> </span><span>$remote_addr</span><span>;</span></div></div><div><div><div>18</div></div><div><span>        </span><span>proxy_set_header</span><span> </span><span>X-Forwarded-For</span><span> </span><span>$proxy_add_x_forwarded_for</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span># Hysteria 流量统计反代，访问 https://sub.yokumi.cn/hy-stats/traffic 就会被转到 http://127.0.0.1:22222/traffic</span></div></div><div><div><div>22</div></div><div><span>    </span><span>location</span><span> </span><span>/hy-stats/</span><span> </span><span>{</span></div></div><div><div><div>23</div></div><div><span>        </span><span>proxy_pass</span><span> </span><span>http://127.0.0.1:22222/</span><span>;</span></div></div><div><div><div>24</div></div><div><span>        </span><span>proxy_set_header</span><span> </span><span>Host</span><span> </span><span>$host</span><span>;</span></div></div><div><div><div>25</div></div><div><span>        </span><span>proxy_set_header</span><span> </span><span>X-Real-IP</span><span> </span><span>$remote_addr</span><span>;</span></div></div><div><div><div>26</div></div><div><span>        </span><span>proxy_set_header</span><span> </span><span>X-Forwarded-For</span><span> </span><span>$proxy_add_x_forwarded_for</span><span>;</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.4 启动服务<a href="#64-启动服务"><span>#</span></a></h3><p>先删除默认配置（防止占用端口或冲突）：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>rm</span><span> </span><span>/etc/nginx/sites-enabled/default</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在启动前测试一下配置是否有误：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>nginx</span><span> </span><span>-t</span></div></div><div><div><div>2</div></div><div><span>nginx:</span><span> </span><span>the</span><span> </span><span>configuration</span><span> </span><span>file</span><span> </span><span>/etc/nginx/nginx.conf</span><span> </span><span>syntax</span><span> </span><span>is</span><span> </span><span>ok</span></div></div><div><div><div>3</div></div><div><span>nginx:</span><span> </span><span>configuration</span><span> </span><span>file</span><span> </span><span>/etc/nginx/nginx.conf</span><span> </span><span>test</span><span> </span><span>is</span><span> </span><span>successful</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>启动服务：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>restart</span><span> </span><span>nginx</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>nginx</span><span> </span><span>-s</span><span> </span><span>reload</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>status</span><span> </span><span>nginx</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>TODO<a href="#todo"><span>#</span></a></h2><p>目前的所有配置之后基本已经能正常使用了，至于问题等后续慢慢发现吧，前前后后加起来花了差不多一天，还是比较折腾的。</p><ul>
<li>PT/BT 配置；</li>
<li>整台🇭🇰双栈中转机搞链式代理（等有米了）；</li>
<li>后续迁移考虑整成一键脚本；</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/setting-up-a-cisco-lab-on-linux-using-dynamips-and-dynagen/</id>
      <title type="text">在 Linux 下编译安装 Dynamips 和 Dynagen 搭建 Cisco 实验环境</title>
      <published>2025-10-31T12:54:00.000Z</published>
      <updated>2025-10-31T12:54:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/setting-up-a-cisco-lab-on-linux-using-dynamips-and-dynagen/"/>
      <summary type="text">最终还是花了一点时间把计网实践的实验平台搞到 Linux 下实现了，相比于 Windows，同样配置的路由器，Linux 下 CPU 占用可降低 50% 以上，应该也会更加稳定吧。 本文将详细介绍如何在 Linux (Ubuntu/Debian) 系统下（macOS 等也是类似的，一些依赖的安装略有不同罢了）从源码编…</summary>
      <content type="html"><![CDATA[<section><h2>写在前面<a href="#写在前面"><span>#</span></a></h2><p>最终还是花了一点时间把计网实践的实验平台搞到 Linux 下实现了，相比于 Windows，同样配置的路由器，Linux 下 CPU 占用可降低 50% 以上，应该也会更加稳定吧。</p><p>本文将详细介绍如何在 Linux (Ubuntu/Debian) 系统下（macOS 等也是类似的，一些依赖的安装略有不同罢了）从源码编译安装最新版 Dynamips，并配合经典的 Dynagen 0.11.0 （停止维护了，后来基本都用 GNS3，但 BUPT 要求，没办法）搭建完整的 Cisco 实验环境。</p></section>
<section><h2>1. 环境准备<a href="#1-环境准备"><span>#</span></a></h2><section><h3>1.1 系统信息<a href="#11-系统信息"><span>#</span></a></h3><p>这里贴一个我自己的，内存越大越好。</p>

<table><thead><tr><th>配置项</th><th>参数</th></tr></thead><tbody><tr><td>操作系统</td><td>Ubuntu 20.04.6 LTS</td></tr><tr><td>Architecture</td><td>x86_64</td></tr><tr><td>CPU</td><td>Intel(R) Xeon(R) Platinum 8369HC</td></tr><tr><td>内存</td><td>22G</td></tr></tbody></table></section><section><h3>1.2 安装依赖<a href="#12-安装依赖"><span>#</span></a></h3><p>在开始编译 Dynamips 之前，需要安装必要的编译工具和开发库：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>build-essential</span><span> </span><span>cmake</span><span> </span><span>git</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>libelf-dev</span><span> </span><span>libpcap-dev</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>2. 编译和安装 Dynampis<a href="#2-编译和安装-dynampis"><span>#</span></a></h2><section><h3>2.1 下载源代码<a href="#21-下载源代码"><span>#</span></a></h3><p>Dynamips 是目前仍在维护的项目，我们从 GitHub 获取最新版本：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>https://github.com/GNS3/dynamips.git</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>dynamips</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 配置编译选项<a href="#22-配置编译选项"><span>#</span></a></h3><p>Dynamips 支持两种代码版本：</p><ul>
<li><strong>stable</strong>（稳定版）：推荐大多数用户使用，兼容性好（推荐）</li>
<li><strong>unstable</strong>（开发版）：包含更多优化，但在某些平台可能不稳定</li>
</ul><p>创建 build 目录并运行 CMake：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>build</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>build</span></div></div><div><div><div>3</div></div><div><span>cmake</span><span> </span><span>..</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 推荐编译稳定版</span></div></div><div><div><div>6</div></div><div><span>cmake</span><span> </span><span>..</span><span> </span><span>-DDYNAMIPS_CODE=stable</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 如需自定义安装路径（可选）</span></div></div><div><div><div>9</div></div><div><span>cmake</span><span> </span><span>..</span><span> </span><span>-DCMAKE_INSTALL_PREFIX=/usr/local</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.3 编译和安装<a href="#23-编译和安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 编译</span></div></div><div><div><div>2</div></div><div><span>make</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 编译（使用多核加速）</span></div></div><div><div><div>5</div></div><div><span>make</span><span> </span><span>-j$(</span><span>nproc</span><span>)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 安装到系统</span></div></div><div><div><div>8</div></div><div><span>sudo</span><span> </span><span>make</span><span> </span><span>install</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.4 验证安装<a href="#24-验证安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>which</span><span> </span><span>dynamips</span></div></div><div><div><div>2</div></div><div><span>dynamips</span><span> </span><span>-h</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输出形如：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251031220149400.png" alt="" /></p></section></section>
<section><h2>3. 安装 Dynagen<a href="#3-安装-dynagen"><span>#</span></a></h2><p>Dynagen 是 Dynamips 的前端管理工具，虽然已经不再维护，但仍是管理 Dynamips 最简单高效的工具之一。我将安装最后的稳定版本 0.11.0。</p><section><h3>3.1 下载 Dynagen<a href="#31-下载-dynagen"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>~/downloads</span></div></div><div><div><div>2</div></div><div><span>wget</span><span> </span><span>https://sourceforge.net/projects/dyna-gen/files/dynagen/dynagen-0.11.0/dynagen-0.11.0.tar.gz</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或者使用其他下载方式获取 <code>dynagen-0.11.0.tar.gz</code>。反正我只找到了这个软件源。</p></section><section><h3>3.2 解压和安装<a href="#32-解压和安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 解压</span></div></div><div><div><div>2</div></div><div><span>tar</span><span> </span><span>-zxvf</span><span> </span><span>dynagen-0.11.0.tar.gz</span></div></div><div><div><div>3</div></div><div><span>cd</span><span> </span><span>dynagen-0.11.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>Dynagen 0.11.0 是纯 Python 脚本，不需要编译。我们将其复制到系统目录：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 复制到 /opt</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>cp</span><span> </span><span>-r</span><span> </span><span>~/downloads/dynagen-0.11.0</span><span> </span><span>/opt/dynagen</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 设置权限</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>chmod</span><span> </span><span>+x</span><span> </span><span>/opt/dynagen/dynagen</span></div></div><div><div><div>6</div></div><div><span>sudo</span><span> </span><span>chmod</span><span> </span><span>644</span><span> </span><span>/opt/dynagen/</span><span>*</span><span>.py</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 创建软链接</span></div></div><div><div><div>9</div></div><div><span>sudo</span><span> </span><span>ln</span><span> </span><span>-s</span><span> </span><span>/opt/dynagen/dynagen</span><span> </span><span>/usr/local/bin/dynagen</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.3 验证安装<a href="#33-验证安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>which</span><span> </span><span>dynagen</span></div></div><div><div><div>2</div></div><div><span># 查看帮助</span></div></div><div><div><div>3</div></div><div><span>dynagen</span><span> </span><span>-h</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img src="https://webp-pic.yokumi.cn/2025/10/20251031220717624.png" alt="" /></p></section></section>
<section><h2>4. 配置实验环境<a href="#4-配置实验环境"><span>#</span></a></h2><section><h3>4.1 创建目录结构<a href="#41-创建目录结构"><span>#</span></a></h3><p>为了更好地组织文件，我创建一个专门的实验环境目录（仿照学校提供的 Windows 版的结构）：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>cisco-lab/{ios,net,configs,working}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>说明如下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>.</span></div></div><div><div><div>2</div></div><div><span>├──</span><span> </span><span>configs</span><span>   </span><span># 路由器配置文件自动保存位置</span></div></div><div><div><div>3</div></div><div><span>├──</span><span> </span><span>ios</span><span>       </span><span># 存放 Cisco IOS 镜像文件</span></div></div><div><div><div>4</div></div><div><span>├──</span><span> </span><span>net</span><span>       </span><span># 存放网络拓扑配置文件（.net 文件）</span></div></div><div><div><div>5</div></div><div><span>└──</span><span> </span><span>working</span><span>   </span><span># Dynamips 运行时工作目录</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.2 准备 IOS 镜像<a href="#42-准备-ios-镜像"><span>#</span></a></h3><p>将 Cisco IOS 镜像文件复制到 <code>~/cisco-lab/ios/</code> 目录下。我直接使用学校提供的镜像：</p><ul>
<li><code>c2600-i-mz.121-3.T.bin</code> - Cisco 2600</li>
<li><code>c2691-advsecurityk9-mz.124-11.T2.bin</code> - Cisco 2691</li>
<li><code>c3640-js-mz.124-10.bin</code> - Cisco 3640</li>
<li><code>c7200-is-mz.122-37.bin</code> - Cisco 7200</li>
</ul><p>注：Cisco IOS 镜像受版权保护，请确保您有合法的使用权限。可以通过从 Cisco 官方网站下载等渠道获取。</p></section><section><h3>4.3 配置防火墙（可选）<a href="#43-配置防火墙可选"><span>#</span></a></h3><p>如果需要远程访问实验环境，需要开放相应端口：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 iptables-persistent</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>iptables-persistent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 编辑防火墙规则</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>nano</span><span> </span><span>/etc/iptables/rules.v4</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>添加以下规则（在 <code>*filter</code> 部分的 <code>COMMIT</code> 之前）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># Dynamips 端口</span></div></div><div><div><div>2</div></div><div><span>-A INPUT -p tcp -m multiport --dports 7200:7201 -j ACCEPT</span></div></div><div><div><div>3</div></div><div><span>-A INPUT -p udp -m multiport --dports 7200:7201 -j ACCEPT</span></div></div><div><div><div>4</div></div><div><span># 路由器控制台端口</span></div></div><div><div><div>5</div></div><div><span>-A INPUT -p tcp -m multiport --dports 2000:2100 -j ACCEPT</span></div></div><div><div><div>6</div></div><div><span># Dynamips 通信端口</span></div></div><div><div><div>7</div></div><div><span>-A INPUT -p udp -m udp --dport 10000 -j ACCEPT</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>重启防火墙：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>service</span><span> </span><span>iptables-persistent</span><span> </span><span>restart</span></div></div><div><div><div>2</div></div><div><span># 或</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>restart</span><span> </span><span>netfilter-persistent</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/notes-about-syntax-analysis/</id>
      <title type="text">浅谈编译原理——语法分析篇</title>
      <published>2025-10-19T02:35:00.000Z</published>
      <updated>2025-10-19T02:35:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/notes-about-syntax-analysis/"/>
      <summary type="text">词法分析程序接受字符流（即源程序），输出记号流，作为语法分析程序的输入（按照自左向右的顺序扫描输入的记号序列），语法分析程序会根据语法规则，判断输入的记号流是否合法，并输出分析树。 分析方法包括：自顶向下分析和自底向上分析。 由于上下文无关文法（CFG）中所有的产生式左边只有一个非终结符，所以我们在调用产生式规则的函…</summary>
      <content type="html"><![CDATA[<section><h2>1. 语法分析简介<a href="#1-语法分析简介"><span>#</span></a></h2><p>语法分析程序的地位：</p><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/09/20250923102353002.png" alt="语法分析程序的地位" /><figcaption>语法分析程序的地位</figcaption></figure><p></p><p>词法分析程序接受字符流（即源程序），输出记号流，作为语法分析程序的输入（按照<strong>自左向右</strong>的顺序扫描输入的记号序列），语法分析程序会根据语法规则，判断输入的记号流是否合法，并输出分析树。</p><p>分析方法包括：自顶向下分析和自底向上分析。</p></section>
<section><h2>2. 自顶向下分析方法<a href="#2-自顶向下分析方法"><span>#</span></a></h2><section><h3>2.1 递归下降分析<a href="#21-递归下降分析"><span>#</span></a></h3><p>核心思想是：</p><ul>
<li>对每一个非终结符构造一个分析函数</li>
<li>用前看符号指导产生式规则的选择</li>
</ul><p>由于上下文无关文法（CFG）中所有的产生式左边只有一个非终结符，所以我们在调用产生式规则的函数后，就分为两种情况：</p><ol>
<li>遇到终结符，则直接把这个终结符和句子中对应位置的 token 进行比较，判断是否符合即可；符合就继续，不符合就返回（回溯）；</li>
<li>遇到非终结符，则调用这个非终结符对应的函数（可能会递归的调用，所以叫递归下降文法）；</li>
</ol><p>以一个简单的文法为例，分析 <code>aab</code> 是否为该文法的句子：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>S –&gt; AB</span></div></div><div><div><div>2</div></div><div><span>A –&gt; aA | ε</span></div></div><div><div><div>3</div></div><div><span>B –&gt; b | bB</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>从起始状态 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> 开始，调用 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> 的函数，得到非终结符 <span><span>A,BA,B</span><span><span><span></span><span>A</span><span>,</span><span></span><span>B</span></span></span></span>；</li>
<li>调用 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的函数，得到 <span><span>a,Aa,A</span><span><span><span></span><span>a</span><span>,</span><span></span><span>A</span></span></span></span>，由于 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 为终结符，则取句子中的 token <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 进行比较，符合，继续；</li>
<li>继续递归调用 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的函数，得到 <span><span>a,Aa,A</span><span><span><span></span><span>a</span><span>,</span><span></span><span>A</span></span></span></span>，取句子中的 token <span><span>bb</span><span><span><span></span><span>b</span></span></span></span> 进行比较，不符合，回溯；</li>
<li>调用 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的另一个函数，得到 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span>，符合，继续；</li>
<li>调用 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 的函数，由于前面都是终结符 <span><span>bb</span><span><span><span></span><span>b</span></span></span></span>，但产生式 1 已经到达结尾，所以选择第二个产生式并调用，得到 <span><span>b,Bb,B</span><span><span><span></span><span>b</span><span>,</span><span></span><span>B</span></span></span></span>，继续递归调用 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 的函数，后略；</li>
</ol><p>显然，这种方法不好：</p><ol>
<li>如果语法存在左递归（形如 <span><span>A⇒+Aβ,A∈NA\Rightarrow^+ A\beta,A\in N</span><span><span><span></span><span>A</span><span></span><span><span>⇒</span><span><span><span><span><span><span></span><span><span>+</span></span></span></span></span></span></span></span><span></span></span><span><span></span><span>A</span><span>β</span><span>,</span><span></span><span>A</span><span></span><span>∈</span><span></span></span><span><span></span><span>N</span></span></span></span> 的推导），则会导致递归调用无限进行下去，使得分析过程陷入死循环；</li>
<li>通过回溯不断试探，效率低；</li>
</ol></section><section><h3>2.2 递归调用预测分析<a href="#22-递归调用预测分析"><span>#</span></a></h3><p>从递归下降分析方法的问题来看，优化的点就在于解决回溯，实现一种确定的、不带回溯的方法 —— 递归调用预测分析。</p><p>如何克服回溯？<strong>能够根据所面临的输入符号准确地指派一个候选式去执行任务</strong>。因此通常需要对文法进行改造，以满足自顶向下的分析方法的要求，具体包括：</p><ul>
<li>消除文法的二义性</li>
<li>消除左递归</li>
<li>提取左公共因子</li>
<li>非终结符号 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的所有候选式的开头终结符号集两两互不相交；
<ul>
<li>即 <span><span>FIRST(αi)∪FIRST(αj)=∅(i≠j)FIRST(\alpha_i)\cup FIRST(\alpha_j)=\emptyset (i\ne j)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>α</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>∪</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>α</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>∅</span><span>(</span><span>i</span><span></span><span><span><span><span><span><span></span><span><span><span></span></span></span><span></span></span></span></span></span><span></span><span>=</span></span><span></span></span><span><span></span><span>j</span><span>)</span></span></span></span></li>
</ul>
</li>
</ul><p><strong>注</strong>：一般情况下，消除左递归和提取左公共因子后的文法无二义性。</p><div><div><div></div><div>Note</div></div><div><p><span><span>FIRST(αi)FIRST(\alpha_i)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>α</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span>: 由 <span><span>αi\alpha_i</span><span><span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 开始推导，可以推导出的所有<strong>开头终结符号</strong>的集合（得出的句子开头的所有可能终结符的集合）。</p><p><strong>求解 First 集合的方法</strong>：对于产生式 <span><span>A→β1β2⋯βnA\rightarrow \beta_1\beta_2\cdots\beta_n</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>⋯</span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，根据开头的 <span><span>β1\beta_1</span><span><span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 分情况讨论：</p><ol>
<li><span><span>β1\beta_1</span><span><span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 是终结符，则 <span><span>β1∈FIRST(A)\beta_1\in FIRST(A)</span><span><span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>A</span><span>)</span></span></span></span>；</li>
<li><span><span>β1\beta_1</span><span><span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 是非终结符，则 <span><span>FIRST(β1)⊆FIRST(A)FIRST(\beta_1)\subseteq FIRST(A)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>⊆</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>A</span><span>)</span></span></span></span></li>
</ol><p>注：针对考虑 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span> 产生式的 FIRST 集合，还需要处理：</p><ul>
<li>如果 <span><span>A→εA\rightarrow\varepsilon</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span> 也是产生式，则 <span><span>ε∈FIRST(A)\varepsilon\in FIRST(A)</span><span><span><span></span><span>ε</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>A</span><span>)</span></span></span></span>；</li>
<li>如果 <span><span>A→β1⋯βi−1βi⋯βnA\rightarrow\beta_1\cdots\beta_{i-1}\beta_i\cdots\beta_n</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>⋯</span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span><span>i</span><span>−</span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>⋯</span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，其中 <span><span>FIRST(β1)−FIRST(βi−1)FIRST(\beta_1)- FIRST(\beta_{i-1})</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>−</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>β</span><span><span><span><span><span><span></span><span><span><span>i</span><span>−</span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> 均含有 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span>，则将 <span><span>FIRST(βi)⊆FIRST(A)FIRST(\beta_i)\subseteq FIRST(A)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>β</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>⊆</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>A</span><span>)</span></span></span></span>；</li>
<li>同理如果 <span><span>FIRST(β1)−FIRST(βn)FIRST(\beta_1)-FIRST(\beta_n)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>−</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>β</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> 均含有 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span>，则 <span><span>ε∈FIRST(A)\varepsilon\in FIRST(A)</span><span><span><span></span><span>ε</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>A</span><span>)</span></span></span></span>；</li>
</ul><p>这里的额外处理事实上是有一个专门的术语：<strong>NULLABLE 集合</strong>，即所有可直接或间接推导出空串的集合：</p><p><strong>NULLABLE 集合的构造方法</strong>：对于非终结符 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span>，若满足 <span><span>X⇒∗εX\Rightarrow^*\varepsilon</span><span><span><span></span><span>X</span><span></span><span><span>⇒</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span></span><span><span></span><span>ε</span></span></span></span>，则 <span><span>X∈NULLABLEX\in NULLABLE</span><span><span><span></span><span>X</span><span></span><span>∈</span><span></span></span><span><span></span><span>N</span><span>U</span><span>LL</span><span>A</span><span>B</span><span>L</span><span>E</span></span></span></span>。</p><p>上述对 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span> 产生式的处理可以简化为：如果 <span><span>A→XYA\rightarrow XY</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span>Y</span></span></span></span> 且 <span><span>X∈NULLABLEX\in NULLABLE</span><span><span><span></span><span>X</span><span></span><span>∈</span><span></span></span><span><span></span><span>N</span><span>U</span><span>LL</span><span>A</span><span>B</span><span>L</span><span>E</span></span></span></span>，则 <span><span>FIRST(A)=FIRST(X)∪FIRST(Y)FIRST(A)=FIRST(X)\cup FIRST(Y)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>A</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>X</span><span>)</span><span></span><span>∪</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>Y</span><span>)</span></span></span></span>；</p><p>显然，这个过程是一个当任何集合有变化就要检查其他集合的动态更新过程。</p><p><strong>对于 FIRST 集合的理解</strong>？FIRST 集合是后续用来构造分析表的，这里构建一个个人的理解视角，给定输入的记号流 <span><span>S=abcS = abc</span><span><span><span></span><span>S</span><span></span><span>=</span><span></span></span><span><span></span><span>ab</span><span>c</span></span></span></span>：</p><p>假如推导的第一步是 <span><span>S→ABCS\rightarrow ABC</span><span><span><span></span><span>S</span><span></span><span>→</span><span></span></span><span><span></span><span>A</span><span>B</span><span>C</span></span></span></span>，而 FIRST 集合的作用就是用于判断该推导能否满足文法要求，判断方式就是输入的字符 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 是否在 <span><span>FIRST(A)FIRST(A)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>A</span><span>)</span></span></span></span> 中，那么这一步推导是可以接受的，推导变为 <span><span>S→aBCS\rightarrow aBC</span><span><span><span></span><span>S</span><span></span><span>→</span><span></span></span><span><span></span><span>a</span><span>B</span><span>C</span></span></span></span>，之后同理。</p><p>并且，由于前面对于文法的要求，非终结符号 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 的所有候选式的开头终结符号集两两互不相交，所以有且只有这种选择，避免了回溯。</p></div></div><section><h4>预测分析程序的转换图构造方法<a href="#预测分析程序的转换图构造方法"><span>#</span></a></h4><p><strong>1. 消除左递归</strong></p><blockquote><p>这里主要以消除直接左递归为主，实际题目中很少出现间接左递归。</p></blockquote><p>消除直接左递归：假如有 <span><span>Ai→Aiα∣βA_i\rightarrow A_i\alpha\mid \beta</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>α</span><span></span><span>∣</span><span></span></span><span><span></span><span>β</span></span></span></span>，则引入一个新的非终结符 <span><span>Ai′A^\prime_i</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，并添加产生式：</p><span><span><span>Ai→βAi′Ai′→αAi′∣ε\begin{aligned}
A_i&amp;\rightarrow \beta A^\prime_i\\
A^\prime_i&amp;\rightarrow \alpha A^\prime_i\mid\varepsilon
\end{aligned}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span><span><span><span><span><span></span><span><span></span><span></span><span>→</span><span></span><span>β</span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span><span></span><span></span><span>→</span><span></span><span>α</span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∣</span><span></span><span>ε</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><p>消除间接左递归：在消除 <span><span>AiA_i</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 后面的 <span><span>AjA_j</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的直接左递归之前，发现有 <span><span>Aj→AiγA_j\rightarrow A_i\gamma</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>γ</span></span></span></span>，则用所有已经在新文法产生式中的 <span><span>Ai→δA_i\rightarrow \delta</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span>δ</span></span></span></span>  替换 <span><span>AiA_i</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，即将产生式 <span><span>Aj→AiγA_j\rightarrow A_i\gamma</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>γ</span></span></span></span> 替换为：</p><span><span><span>Aj→δγA_j\rightarrow \delta\gamma</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span>δ</span><span>γ</span></span></span></span></span><p>直至 <span><span>δ\delta</span><span><span><span></span><span>δ</span></span></span></span> 最前面为终结符或大于等于 <span><span>AjA_j</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的非终结符。</p><p><strong>2. 提取左公因子</strong></p><p>如果有产生式形如：<span><span>A→αβ1∣αβ2A\rightarrow\alpha\beta_1\mid\alpha\beta_2</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∣</span><span></span></span><span><span></span><span>α</span><span><span>β</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则提取左公因子 <span><span>α\alpha</span><span><span><span></span><span>α</span></span></span></span>，将产生式替换为：</p><span><span><span>A→αA′A′→β1∣β2\begin{aligned}
A&amp;\rightarrow\alpha A^\prime\\
A^\prime&amp;\rightarrow\beta_1\mid\beta_2
\end{aligned}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span><span>A</span></span></span><span><span></span><span><span><span>A</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span><span><span><span><span><span></span><span><span></span><span></span><span>→</span><span></span><span>α</span><span><span>A</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span><span><span></span><span><span></span><span></span><span>→</span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∣</span><span></span><span><span>β</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><p><strong>3. 绘制转换图</strong></p><p>对于每一个非终结符号 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span>，创建一个初态和终态，对于每个产生式 <span><span>A→X1X2⋯XnA\rightarrow X_1X_2\cdots X_n</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>⋯</span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 构造一条创建一条从初态到终态的路径，有向边依次为 <span><span>Xi,i=1,2,⋯ ,nX_i,i=1,2,\cdots,n</span><span><span><span></span><span><span>X</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span><span>,</span><span></span><span>2</span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span>n</span></span></span></span>。</p><p><strong>4. 化简状态转换图</strong></p><p>总之就是反复代入化简。</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251019142131825.png" alt="" /></p><p><strong>5. 预测分析程序的实现</strong></p><p>以下图为例：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251019142325813.png" alt="" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>procE</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>procT</span><span>();</span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span> (</span><span>char</span><span> </span><span>==</span><span> </span><span>'+'</span><span>) {</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>forward pointer;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>procE</span><span>();</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>简单来说：</p><ul>
<li>对于 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span> 的有向边一般不做任何处理；</li>
<li>遇到终结符则移动记号流的指针指向后一个待读如的字符；</li>
<li>遇到非终结符则调用它的对应函数；</li>
</ul></section></section><section><h3>2.3 非递归预测分析/LL(1) 分析算法<a href="#23-非递归预测分析ll1-分析算法"><span>#</span></a></h3><section><h4>分析过程<a href="#分析过程"><span>#</span></a></h4><p>上面的方法中，虽然不存在回溯和死循环，但函数的调用还是存在嵌套的，所以引入非递归预测分析方法，它通过<strong>分析表</strong>和<strong>分析栈</strong>联合控制，消除递归。</p><blockquote><p>按照网上的说法，似乎基本鲜有关于递归调用预测分析的介绍，而是直接介绍 LL(1) 分析算法，对应我们教材上的非递归预测分析。</p></blockquote><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/10/20251019143133757.png" alt="预测分析程序模型" /><figcaption>预测分析程序模型</figcaption></figure><p></p><p>按照个人理解，分析表是用来解决回溯的，而符号栈则是用来解决函数的递归调用的。</p><p><strong>输入</strong>：符号串 <span><span>ω\omega</span><span><span><span></span><span>ω</span></span></span></span>（词法分析输出的记号流）；文法的预测分析表 <span><span>MM</span><span><span><span></span><span>M</span></span></span></span>（手动构造，方法见下/通过语法分析器自动生成）；</p><p><strong>初始化</strong>：</p><ol>
<li>将符号串 <span><span>ω\omega</span><span><span><span></span><span>ω</span></span></span></span> 放入输入缓冲区，注意后面要添加一个 <code>$</code>；</li>
<li>将 <code>$</code> 放入符号栈，再将文法的起始符号 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> 入栈，此时栈顶元素 <span><span>X=SX=S</span><span><span><span></span><span>X</span><span></span><span>=</span><span></span></span><span><span></span><span>S</span></span></span></span>；</li>
<li>pointer 指向输入缓冲区的第一个符号，代表当前输入符号 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span>；</li>
</ol><p><strong>执行分析</strong>：</p><p>根据符号栈栈顶元素 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 和输入符号 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 执行如下循环过程：</p><ol>
<li>若 <span><span>X=a=X=a=</span><span><span><span></span><span>X</span><span></span><span>=</span><span></span></span><span><span></span><span>a</span><span></span><span>=</span></span></span></span> <code>$</code>，则分析成功，停止；</li>
<li>若 <span><span>X=a≠X=a\ne</span><span><span><span></span><span>X</span><span></span><span>=</span><span></span></span><span><span></span><span>a</span><span></span><span><span><span><span><span><span></span><span><span><span></span></span></span><span></span></span></span></span></span><span></span><span>=</span></span></span></span></span> <code>$</code>，说明当前栈顶的终结符命中输入，匹配成功，将 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 弹出，同时 pointer 前移指向下一个输入符号；</li>
<li>若 <span><span>X∈VT,X≠aX\in V_T,X\ne a</span><span><span><span></span><span>X</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>V</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>X</span><span></span><span><span><span><span><span><span></span><span><span><span></span></span></span><span></span></span></span></span></span><span></span><span>=</span></span><span></span></span><span><span></span><span>a</span></span></span></span>，说明发现错误；</li>
<li>若 <span><span>X∈VNX\in V_N</span><span><span><span></span><span>X</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>V</span><span><span><span><span><span><span></span><span><span>N</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则访问分析表 <span><span>M[X,a]M[X,a]</span><span><span><span></span><span>M</span><span>[</span><span>X</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>，即找到对应的需要被调用的产生式，根据产生式分情况执行：
<ol>
<li>若 <span><span>M[X,a]=X→Y1Y2⋯YnM[X,a]=X\rightarrow Y_1Y_2\cdots Y_n</span><span><span><span></span><span>M</span><span>[</span><span>X</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>X</span><span></span><span>→</span><span></span></span><span><span></span><span><span>Y</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>Y</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>⋯</span><span></span><span><span>Y</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则将 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 弹栈，然后按照 <span><span>Yn,⋯ ,Y2,Y1Y_n,\cdots,Y_2,Y_1</span><span><span><span></span><span><span>Y</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>Y</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>Y</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的顺序压入栈（<span><span>YnY_n</span><span><span><span></span><span><span>Y</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 显然是被最后调用的，所以先入栈，放在底下），</li>
<li>若 <span><span>M[X,a]=X→εM[X,a]=X\rightarrow \varepsilon</span><span><span><span></span><span>M</span><span>[</span><span>X</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>X</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span>，则将 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 弹栈；</li>
<li>若 <span><span>M[X,a]=X→errorM[X,a]=X\rightarrow error</span><span><span><span></span><span>M</span><span>[</span><span>X</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>X</span><span></span><span>→</span><span></span></span><span><span></span><span>er</span><span>r</span><span>or</span></span></span></span>，即分析表中没有任何内容，出错；</li>
</ol>
</li>
</ol><p><strong>输出</strong>：每次命中分析表对应的产生式时，输出产生式，最终结果为依次调用的产生式序列，相当于一个最左推导的过程。</p></section><section><h4>预测分析表的构造<a href="#预测分析表的构造"><span>#</span></a></h4><p><strong>1. 改写文法</strong>：消除左递归 + 消除左公因子；（见<a href="#%E9%A2%84%E6%B5%8B%E5%88%86%E6%9E%90%E7%A8%8B%E5%BA%8F%E7%9A%84%E8%BD%AC%E6%8D%A2%E5%9B%BE%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95">前面</a>）</p><p><strong>2. 构造 FIRST 集合</strong>：使用<a href="#%E9%A2%84%E6%B5%8B%E5%88%86%E6%9E%90%E7%A8%8B%E5%BA%8F%E7%9A%84%E8%BD%AC%E6%8D%A2%E5%9B%BE%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95">前面</a>提到的方法；</p><p><strong>3. 构造 FOLLOW 集合</strong>：</p><div><div><div></div><div>Note</div></div><div><p><strong>FOLLOW 集合的定义</strong>：假定 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> 是文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的开始符号，对于 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的任何非终结符号 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span>，集合 <span><span>FOLLOW(A)FOLLOW(A)</span><span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span></span></span></span> 是在所有句型中，紧跟 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 之后出现的终结符号或 <code>$</code> 组成的集合，即所有可以合法的站在此非终结符后面的终结符（可以包括结束符 <code>$</code> ，但不包括 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span> ）的集合。</p><span><span><span>FOLLOW(A)={a∣S⇒∗⋯Aa⋯ ,a∈VT}FOLLOW(A)=\{a\mid S\Rightarrow^* \cdots Aa\cdots,a\in V_T\}</span><span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span>a</span><span></span><span>∣</span><span></span></span><span><span></span><span>S</span><span></span><span><span>⇒</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span></span><span><span></span><span>⋯</span><span></span><span>A</span><span>a</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span>a</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>V</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>}</span></span></span></span></span><p>特别地，若有 <span><span>S⇒∗⋯AS\Rightarrow^*\cdots A</span><span><span><span></span><span>S</span><span></span><span><span>⇒</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span></span><span><span></span><span>⋯</span><span></span><span>A</span></span></span></span>，则规定 <code>$</code> <span><span>∈FOLLOW(A)\in FOLLOW(A)</span><span><span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span></span></span></span>。</p><p><strong>FOLLOW 集合的构造方法</strong>：</p><ol>
<li>对于起始符号 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span>，<code>$</code> <span><span>∈FOLLOW(S)\in FOLLOW(S)</span><span><span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>S</span><span>)</span></span></span></span>；</li>
<li>若有 <span><span>A→αBβA\rightarrow\alpha B\beta</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>B</span><span>β</span></span></span></span>，则 <span><span>FIRST(β)⊆FOLLOW(B)FIRST(\beta)\subseteq FOLLOW(B)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>β</span><span>)</span><span></span><span>⊆</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>B</span><span>)</span></span></span></span>，注意除去 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span>；</li>
<li>若有 <span><span>A→αBA\rightarrow\alpha B</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>B</span></span></span></span> 或 <span><span>A→αBβ,β⇒∗εA\rightarrow\alpha B\beta,\beta\Rightarrow^*\varepsilon</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>B</span><span>β</span><span>,</span><span></span><span>β</span><span></span><span><span>⇒</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span></span><span><span></span><span>ε</span></span></span></span>，则 <span><span>FOLLOW(A)⊆FOLLOW(B)FOLLOW(A)\subseteq FOLLOW(B)</span><span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span><span></span><span>⊆</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>B</span><span>)</span></span></span></span>；</li>
</ol><p><strong>FOLLOW 集合的意义</strong>？确定某个非终结符可能出现在产生式右侧的上下文环境。举个例子，当栈顶为 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> ，读入的符号为 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span>，但 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 不在任何 FIRST 集合中，如果有 <span><span>X→εX \rightarrow\varepsilon</span><span><span><span></span><span>X</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span>，那么 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 必须是 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 的后继字符才能保证最终句子是一个符合语法的句子，即此时调用 FOLLOW 集合。</p></div></div><p><strong>4. 预测分析表的构造</strong>：对于每条产生式 <span><span>A→αA\rightarrow \alpha</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span></span></span></span>：</p><ul>
<li>对所有终结符 <span><span>a∈FIRST(α)a\in FIRST(\alpha)</span><span><span><span></span><span>a</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>α</span><span>)</span></span></span></span>，<span><span>{A→α}⊆M[A,a]\{A\rightarrow\alpha\}\subseteq M[A,a]</span><span><span><span></span><span>{</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>}</span><span></span><span>⊆</span><span></span></span><span><span></span><span>M</span><span>[</span><span>A</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>；</li>
<li>对 <span><span>ε∈FIRST(α)\varepsilon\in FIRST(\alpha)</span><span><span><span></span><span>ε</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>α</span><span>)</span></span></span></span>，则对所有 <span><span>b∈FOLLOW(A)b\in FOLLOW(A)</span><span><span><span></span><span>b</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span></span></span></span>，<span><span>{A→α}⊆M[A,b]\{A\rightarrow\alpha\}\subseteq M[A,b]</span><span><span><span></span><span>{</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>}</span><span></span><span>⊆</span><span></span></span><span><span></span><span>M</span><span>[</span><span>A</span><span>,</span><span></span><span>b</span><span>]</span></span></span></span>；</li>
</ul><p>最终留空处均为 error。</p></section><section><h4>LL(1) 文法<a href="#ll1-文法"><span>#</span></a></h4><p>可以用上述方法解析的文法称之为 LL(1) 文法，即<strong>从左 (L) 向右读入一个程序，最左 (L) 推导，采用一个 (1) 前看符号</strong>。要求对应的预测分析表不含<strong>多重表项</strong>，也就是要求：对所有产生式 <span><span>A→u1∣u2∣⋯∣unA\rightarrow u_1\mid u_2\mid \cdots\mid u_n</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span><span>u</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∣</span><span></span></span><span><span></span><span><span>u</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∣</span><span></span></span><span><span></span><span>⋯</span><span></span><span>∣</span><span></span></span><span><span></span><span><span>u</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，有：</p><span><span><span>FIRST(u1)∩⋯∩FIRST(un)=∅,即 FIRST 集合互不相交ui⇒∗ε,FIRST(ui)∩FOLLOW(A)=∅,i=1,2,⋯ ,n,即 FOLLOW(A) 与所有 FIRST 集合互不相交\begin{aligned}
&amp;FIRST(u_1)\cap \cdots \cap FIRST(u_n)=\emptyset,\text{即 FIRST 集合互不相交}\\
u_i\Rightarrow^*\varepsilon,&amp;FIRST(u_i)\cap FOLLOW(A)=\emptyset,i=1,2,\cdots,n,\text{即 FOLLOW(A) 与所有 FIRST 集合互不相交}
\end{aligned}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span></span></span><span><span></span><span><span><span>u</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span><span>⇒</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span><span>ε</span><span>,</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span><span><span><span><span><span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>u</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>∩</span><span></span><span>⋯</span><span></span><span>∩</span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>u</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span><span>∅</span><span>,</span><span></span><span><span>即</span><span> FIRST </span><span>集合互不相交</span></span></span></span><span><span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span><span>u</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>∩</span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span><span></span><span>=</span><span></span><span>∅</span><span>,</span><span></span><span>i</span><span></span><span>=</span><span></span><span>1</span><span>,</span><span></span><span>2</span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span>n</span><span>,</span><span></span><span><span>即</span><span> FOLLOW(A) </span><span>与所有</span><span> FIRST </span><span>集合互不相交</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span></section></section></section>
<section><h2>3. 自底向上分析方法<a href="#3-自底向上分析方法"><span>#</span></a></h2><p>LL(1) 分析法的优点是不需要回溯，构造方法较简单，且分析速度非常快，每读到第一个符号就可以预测出整个产生式来。缺点是对语法的限制太强，能满足此要求的语法相当少，而将一个不满足此要求的语法改写到满足要求也相当不容易。因此， LL(1) 分析法目前已经应用的比较少了。基于此，我们引入现在被广泛使用的自底向上分析方法。</p><ul>
<li>对输入串的扫描：自左向右；</li>
<li>分析树的构造：自底向上；</li>
<li>分析过程：
<ul>
<li>从输入符号串开始分析</li>
<li>查找当前句型的“可归约串”</li>
<li>使用规则，把它归约成相应的非终结符号</li>
<li>重复</li>
</ul>
</li>
</ul><p>以下列文法为例，输入串为 <code>id + id * id</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>E → E + T | T</span></div></div><div><div><div>2</div></div><div><span>T → T * F | F</span></div></div><div><div><div>3</div></div><div><span>F → (E) | id</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>一种自底向上的过程如下：</p>

<table><thead><tr><th><strong>步骤</strong></th><th><strong>当前句型</strong></th><th><strong>可归约串</strong></th><th><strong>使用的产生式</strong></th><th><strong>归约结果</strong></th></tr></thead><tbody><tr><td>1</td><td>id + id * id</td><td>id</td><td>F → id</td><td>F</td></tr><tr><td>2</td><td>F + id * id</td><td>F</td><td>T → F</td><td>T</td></tr><tr><td>3</td><td>T + id * id</td><td>id</td><td>F → id</td><td>F</td></tr><tr><td>4</td><td>T + F * id</td><td>F</td><td>T → F</td><td>T</td></tr><tr><td>5</td><td>T + T * id</td><td>id</td><td>F → id</td><td>F</td></tr><tr><td>6</td><td>T + T * F</td><td>T * F</td><td>T → T * F</td><td>T</td></tr><tr><td>7</td><td>T + T</td><td>T</td><td>E → T</td><td>E</td></tr><tr><td>8</td><td>E + T</td><td>E + T</td><td>E → E + T</td><td>E</td></tr><tr><td>9</td><td>E</td><td>-</td><td>-</td><td>结束</td></tr></tbody></table><p>从上述例子可见，分析过程的关键在于<strong>如何找出“可归约串”</strong>。</p><section><h3>3.1 “移进-归约”分析方法<a href="#31-移进-归约分析方法"><span>#</span></a></h3><section><h4>“移进-归约”分析过程<a href="#移进-归约分析过程"><span>#</span></a></h4><ol>
<li>把输入符号逐个地移进符号栈中；（即“移进”，把下一个输入符号移入栈顶）</li>
<li>当栈顶的符号串形成某个产生式的一个候选式（右部）时，在一定条件下， 把该符号串替换（归约）为该产生式的左部符号；（即“归约”）</li>
<li>重复步骤 2 直到栈顶符号串不再是“可归约串”为止；</li>
<li>重复步骤 1 ～ 3 直到归约出文法起始符号 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span>；</li>
<li>接收：宣布分析成功，停止分析；</li>
<li>错误处理：调用错误处理程序进行诊断和恢复；</li>
</ol><p>简单来说，“移进”相当于“往栈里装符号，等待形成句法单位”，“归约”相当于“识别出一个语法成分，用它的非终结符替代”。通过不断循环“读符号 + 识别结构”，最终构建整个语法树。</p></section><section><h4>规范归约<a href="#规范归约"><span>#</span></a></h4><p>假定 <span><span>α\alpha</span><span><span><span></span><span>α</span></span></span></span> 为文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的一个句子，若右句型序列 <span><span>αn,αn−1,⋯ ,α1,α0\alpha_n,\alpha_{n-1},\cdots,\alpha_1,\alpha_0</span><span><span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span><span>n</span><span>−</span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 满足：</p><ol>
<li><span><span>αn=α\alpha_n=\alpha</span><span><span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>α</span></span></span></span>，<span><span>α0=S\alpha_0=S</span><span><span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>S</span></span></span></span>；</li>
<li><span><span>∀0&lt;i≤n\forall 0&lt;i\le n</span><span><span><span></span><span>∀0</span><span></span><span>&lt;</span><span></span></span><span><span></span><span>i</span><span></span><span>≤</span><span></span></span><span><span></span><span>n</span></span></span></span>，<span><span>αi−1\alpha_{i-1}</span><span><span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span><span>i</span><span>−</span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 是经过把 <span><span>αi\alpha_i</span><span><span><span></span><span><span>α</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的句柄替换为相应产生式的左部符号而得到的；</li>
</ol><p>规范归约相当于最右推导的逆过程，每次只归约当前句型中的<strong>句柄（handle）</strong>，也就是<strong>恰好能还原出上一步最右推导的右部</strong>的那一部分。</p></section></section><section><h3>3.2 LR 分析方法<a href="#32-lr-分析方法"><span>#</span></a></h3><p>LR 分析是移进-归约分析的一种规范化、自动化的实现方法。即<strong>从左 (L) 向右扫描输入符号串，为输入符号串构造一个最右 (R) 推导的逆过程，采用 k 个前看符号</strong>。</p><p>LR 分析法的基本思想是在归约的过程中，一方面记住移入和归约的整个符号串（历史信息），另一方面通过产生式推测未来可能碰到的输入符号（预测信息）。在分析的每一步，只须根据分析栈当前已移进和归约出的全部文法符号，并至多再向前查看 k 个输入符号，就能确定栈顶的符号串是否构成相对于某一产生式的句柄，从而确定当前所应采取的分析动作（是移进还是按某一产生式进行归约等）。</p><section><h4>LR 分析程序的模型及工作过程<a href="#lr-分析程序的模型及工作过程"><span>#</span></a></h4><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/10/20251019193813073.png" alt="LR分析程序模型" /><figcaption>LR分析程序模型</figcaption></figure><p></p><p>栈包括：（两者同步变化）</p><ul>
<li>状态栈 S；</li>
<li>符号栈 X；</li>
</ul><p>分析表包括：</p><ul>
<li>动作表 action：用于指导分析器在<strong>遇到不同输入符号时</strong>应采取的动作；
<ul>
<li><span><span>action[Sm,ai]action[S_m,a_i]</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>]</span></span></span></span>：<span><span>SmS_m</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 遇到输入符号 <span><span>aia_i</span><span><span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> （包括终结符和非终结符）时的动作；
<ul>
<li>shift <span><span>SS</span><span><span><span></span><span>S</span></span></span></span>（移进）：将当前输入符号 <span><span>aia_i</span><span><span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 和状态 <span><span>S=goto[Sm,ai]S=goto[S_m,a_i]</span><span><span><span></span><span>S</span><span></span><span>=</span><span></span></span><span><span></span><span>g</span><span>o</span><span>t</span><span>o</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>]</span></span></span></span> 压入栈；</li>
<li>reduce by <span><span>A→βA\rightarrow\beta</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>β</span></span></span></span>（归约）：若 <span><span>∣β∣=r\left|\beta\right|=r</span><span><span><span></span><span><span>∣</span><span>β</span><span>∣</span></span><span></span><span>=</span><span></span></span><span><span></span><span>r</span></span></span></span>，则从栈中弹出 <span><span>rr</span><span><span><span></span><span>r</span></span></span></span> 项，栈顶变为 <span><span>Sm−rS_{m-r}</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>m</span><span>−</span><span>r</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，然后把 <span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 和状态 <span><span>S=goto[Sm−r,A]S=goto[S_{m-r},A]</span><span><span><span></span><span>S</span><span></span><span>=</span><span></span></span><span><span></span><span>g</span><span>o</span><span>t</span><span>o</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>m</span><span>−</span><span>r</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>A</span><span>]</span></span></span></span> 压入栈；</li>
<li>accept：宣布分析成功，停止分析；</li>
<li>error：调用出错处理程序，进行错误恢复；</li>
</ul>
</li>
</ul>
</li>
<li>状态转移表 goto：用于指导分析器<strong>在采取归约动作后</strong>应转移到的新状态。
<ul>
<li><span><span>goto[Sm,X]goto[S_m,X]</span><span><span><span></span><span>g</span><span>o</span><span>t</span><span>o</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>X</span><span>]</span></span></span></span>：<span><span>SmS_m</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 经过 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 的后继状态；</li>
</ul>
</li>
</ul><p>工作过程如下，初始二元式为 <span>(S_0,a_1a_2\cdots a_n\</span>)<span><span>，分析过程中的每步的结果均可以表示为，分析过程中的每步的结果均可以表示为 </span><span><span><span></span><span>，分析过程中的每步的结果均可以表示为</span></span></span></span>(S_0S_1\cdots S_m,a_ia_{i+1}\cdots a_n$)$：</p><ul>
<li>若 <span><span>action[Sm,ai]=shift S,S=goto[Sm,ai]action[S_m,a_i]=shift\space S,S=goto[S_m,a_i]</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>s</span><span>hi</span><span>f</span><span>t</span><span> </span><span>S</span><span>,</span><span></span><span>S</span><span></span><span>=</span><span></span></span><span><span></span><span>g</span><span>o</span><span>t</span><span>o</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>]</span></span></span></span>，则二元式变为 <span>(S_0S_1\cdots S_mS,a_{i+1}\cdots a_n\</span>)$；</li>
<li>若 <span><span>action[Sm,ai]=reduce by A→βaction[S_m,a_i]=reduce\space by\space A\rightarrow\beta</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>r</span><span>e</span><span>d</span><span>u</span><span>ce</span><span> </span><span>b</span><span>y</span><span> </span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>β</span></span></span></span>，则二元式变为 <span>(S_0S_1\cdots S_{m-r}S,a_ia_{i+1}\cdots a_n\</span>)$；</li>
<li>若 <span><span>action[Sm,ai]=acceptaction[S_m,a_i]=accept</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>a</span><span>cce</span><span>pt</span></span></span></span>，则分析成功；</li>
<li>若 <span><span>action[Sm,ai]=erroraction[S_m,a_i]=error</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span><span>S</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>er</span><span>r</span><span>or</span></span></span></span>，则调用错误处理程序；</li>
</ul><div><div><div></div><div>Note</div></div><div><p><strong>活前缀</strong>：一个规范句型的一个前缀，如果不含句柄之后的任何符号，则称它为该句型的一个活前缀。</p></div></div><p>同样，分析方法的核心在于构造分析表，不同 LR 算法构造方法不同，下文将具体展开介绍。</p></section><section><h4>LR(0) 算法<a href="#lr0-算法"><span>#</span></a></h4><section><h5>关键概念<a href="#关键概念"><span>#</span></a></h5><div><div><div></div><div>Note</div></div><div><p><strong>形态/LR(0) 项目</strong>： 一个产生式的解析程度，用一个产生式加一个位置黑点 <span><span>⋅\cdot</span><span><span><span></span><span>⋅</span></span></span></span> 来表示，黑点左边表示已解析的部分，右部表示待解析的部分，一般也称为 LR(0) 项目。</p></div></div><p>产生式 <span><span>A→XYZA\rightarrow XYZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span>Y</span><span>Z</span></span></span></span> 有 4 种 LR(0) 项目：</p><ul>
<li><span><span>A→⋅XYZA\rightarrow\cdot XYZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>X</span><span>Y</span><span>Z</span></span></span></span>：起始形态，</li>
<li><span><span>A→X⋅YZA\rightarrow X\cdot YZ</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span></span><span>⋅</span><span></span></span><span><span></span><span>Y</span><span>Z</span></span></span></span>；</li>
<li><span><span>A→XY⋅ZA\rightarrow XY\cdot Z</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span>Y</span><span></span><span>⋅</span><span></span></span><span><span></span><span>Z</span></span></span></span>；</li>
<li><span><span>A→XYZ⋅A\rightarrow XYZ\cdot</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span>Y</span><span>Z</span><span>⋅</span></span></span></span>：已完成形态/可折叠/形态，即<strong>归约项目</strong>；
<ul>
<li>文法起始符号 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> 的归约项目又称之为接收项目；</li>
</ul>
</li>
</ul><p>注：产生式 <span><span>A→εA\rightarrow\varepsilon</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>ε</span></span></span></span> 只有一个归约项目 <span><span>A→⋅A\rightarrow\cdot</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span></span></span></span>。</p><p>前三种根据圆点 <span><span>⋅\cdot</span><span><span><span></span><span>⋅</span></span></span></span> 后的第一个符号为终结符/非终结符又可以分为：</p><ul>
<li>移进项目：圆点后第一个符号为终结符号的 LR(0) 项目；</li>
<li>待约项目：圆点后第一个符号为非终结符号的 LR(0) 项目；</li>
</ul><div><div><div></div><div>Note</div></div><div><p><strong>拓广文法</strong>：是对原始文法的一种扩展，通过在原始文法中添加一个新的开始符号 <span><span>S′S^\prime</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 和产生式 <span><span>S′→SS^\prime\rightarrow S</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span>S</span></span></span></span>，使得文法开始符号仅出现在一个产生式的左边，从而使分析器只有一个接受状态。（接收项目唯一）</p></div></div><blockquote><p>教材上还讲了有效项目的定义，但没什么用，直接略去。</p></blockquote><div><div><div></div><div>Note</div></div><div><p><strong>闭包 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span> 的定义和构造</strong>：设 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 是文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的一个 LR(0) 项目集合，则 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span> 是从 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 出发，并使用以下方法构造的项目集合：</p><ol>
<li><span><span>II</span><span><span><span></span><span>I</span></span></span></span> 中的每一个 LR(0) 项目均属于 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>；</li>
<li>若 <span><span>A→α⋅Bβ∈closure(I)A\rightarrow\alpha\cdot B\beta\in closure(I)</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>B</span><span>β</span><span></span><span>∈</span><span></span></span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>，且有产生式 <span><span>B→ηB\rightarrow \eta</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>η</span></span></span></span>，若 <span><span>B→⋅η∉closure(I)B\rightarrow\cdot\eta\notin closure(I)</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>η</span><span></span><span><span><span>∈</span></span><span><span><span><span></span><span><span><span>/</span><span></span></span></span><span></span></span></span></span></span><span></span></span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>，则将 <span><span>B→⋅ηB\rightarrow\cdot\eta</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>η</span></span></span></span> 加入 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>；</li>
<li>重复步骤 2 直至 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span> 不再增大；</li>
</ol><p>注：<span><span>B→⋅ηB\rightarrow\cdot\eta</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>η</span></span></span></span> 称之为 <span><span>A→α⋅BβA\rightarrow\alpha\cdot B\beta</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>B</span><span>β</span></span></span></span> 的<strong>延伸形态</strong>，延伸的方向是单向的。</p><p>上述过程就是把集合 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 里所有形态的所有延伸形态全部添加进这个集合。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>状态</strong>：设 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 是文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的一个 LR(0) 项目集合，状态即为 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>，即状态是进行过闭合操作的 LR(0) 项目（形态）集合。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>转移函数 go</strong>：若 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 是文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的一个 LR(0) 项目集，<span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 是一个文法符号，定义</p><span><span><span>go[I,X]=closure(J)go[I,X]=closure(J)</span><span><span><span></span><span>g</span><span>o</span><span>[</span><span>I</span><span>,</span><span></span><span>X</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>J</span><span>)</span></span></span></span></span><p>其中，<span><span>J={A→αX⋅β∣当 A→α⋅Xβ∈I 时}J=\{A\rightarrow\alpha X\cdot\beta\mid\text{当 }A\rightarrow\alpha\cdot X\beta\in I\text{ 时}\}</span><span><span><span></span><span>J</span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>X</span><span></span><span>⋅</span><span></span></span><span><span></span><span>β</span><span></span><span>∣</span><span></span></span><span><span></span><span><span>当</span><span> </span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>X</span><span>β</span><span></span><span>∈</span><span></span></span><span><span></span><span>I</span><span><span> </span><span>时</span></span><span>}</span></span></span></span>。</p><p><span><span>A→αX⋅βA\rightarrow\alpha X\cdot\beta</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>X</span><span></span><span>⋅</span><span></span></span><span><span></span><span>β</span></span></span></span> 为 <span><span>A→α⋅XβA\rightarrow\alpha\cdot X\beta</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>X</span><span>β</span></span></span></span> 遇到符号 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 时的<strong>后继形态</strong>，</p></div></div></section><section><h5>构造文法 G 的 LR(0) 项目集规范族<a href="#构造文法-g-的-lr0-项目集规范族"><span>#</span></a></h5><p>输入为文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span>，输出为 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的 <span><span>LR(0)LR(0)</span><span><span><span></span><span>L</span><span>R</span><span>(</span><span>0</span><span>)</span></span></span></span> 项目集规范族 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span>：</p><ol>
<li>构造文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的拓广文法 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span>；</li>
<li><span><span>C={closure({S′→⋅S})}C=\{closure(\{S^\prime\rightarrow\cdot S\})\}</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>({</span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>S</span><span>})}</span></span></span></span>；</li>
<li>对于 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> 中的每一个项目集 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 和每一个文法符号 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span>，如果 <span><span>go[I,X]go[I,X]</span><span><span><span></span><span>g</span><span>o</span><span>[</span><span>I</span><span>,</span><span></span><span>X</span><span>]</span></span></span></span> 不为空且不在 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> 中，则将 <span><span>go[I,X]go[I,X]</span><span><span><span></span><span>g</span><span>o</span><span>[</span><span>I</span><span>,</span><span></span><span>X</span><span>]</span></span></span></span> 加入 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span>；</li>
<li>重复步骤 2 直至 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> 不再更新；</li>
</ol><p>简单来说，就是从初始项目集 <span><span>I0=closure({S′→⋅S})I_0 = closure(\{S^\prime\rightarrow\cdot S\})</span><span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>({</span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>S</span><span>})</span></span></span></span> 开始（事实上 <span><span>I0I_0</span><span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 就是起始状态），按以上方法，不断的生成新的项目集（状态），直到不再出现新的项目集（状态）。</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251019222419394.png" alt="" /></p><p>如上图所示，这个状态转移图和有限状态自动机的运行图很相似，一般称之为<strong>识别文法 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 所有活前缀的 DFA</strong>。不过解析过程并不能按照 DFA 运行，上下文无关文法需要下推自动机来进行识别。</p></section><section><h5>LR(0) 项目集中的冲突及解决<a href="#lr0-项目集中的冲突及解决"><span>#</span></a></h5><div><div><div></div><div>Note</div></div><div><p><strong>可折叠形态</strong>：某状态/项目集中，只有一个项目，且该项目为归约项目。</p></div></div><p>语法分析中，如果遇到不可折叠状态，就会发生冲突，即：</p><ol>
<li>一个项目集 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 中，既包含 <span><span>X→α⋅bβX\rightarrow\alpha\cdot b\beta</span><span><span><span></span><span>X</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>b</span><span>β</span></span></span></span>，又存在 <span><span>A→α⋅A\rightarrow\alpha\cdot</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>⋅</span></span></span></span>，即<strong>既包含不可折叠形态，又包含可折叠形态</strong>（归约项目）；
<ul>
<li>这会造成“<strong>移进-归约</strong>”冲突，也就是无法判断出应该 shift，还是应该 reduce；</li>
</ul>
</li>
<li>一个项目集中，既包含 <span><span>A→α⋅A\rightarrow\alpha\cdot</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>⋅</span></span></span></span>，又包含 <span><span>B→β⋅B\rightarrow\beta\cdot</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>β</span><span>⋅</span></span></span></span>，即<strong>包含多条可折叠形态</strong>（归约项目）；
<ul>
<li>这会造成“<strong>归约-归约</strong>”冲突，也就是无法判断出应该采用哪条产生式来归约；</li>
</ul>
</li>
</ol><p>解决方法见下，即 SLR(1) 算法。</p></section><section><h5>判断某文法是否属于 LR(0) 文法<a href="#判断某文法是否属于-lr0-文法"><span>#</span></a></h5><p>一个文法是LR(0)文法，每个状态/项目集中：</p><ol>
<li>要么所有 LR(0) 项目都是“移进-待约项目”；（即不能同时含有可折叠和不可折叠形态）</li>
<li>要么只含有唯一的归约项目（可折叠形态）；</li>
<li>起始符号不出现在任何产生式右部（一般通过拓广文法已经解决了）；</li>
</ol></section></section><section><h4>SLR(1) 算法<a href="#slr1-算法"><span>#</span></a></h4><section><h5>LR(0) 冲突的解决<a href="#lr0-冲突的解决"><span>#</span></a></h5><p>由于 LR(0) 不向前看下一个符号，大大增加了冲突的可能，因此这种冲突通过向前看若干个输入符号或许能够解决。</p><p>还是考虑上面的冲突场景，即 <span><span>I={X→α⋅bβ,A→α⋅,B→β⋅}I=\{X\rightarrow\alpha\cdot b\beta,A\rightarrow\alpha\cdot,B\rightarrow\beta\cdot\}</span><span><span><span></span><span>I</span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span>X</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>b</span><span>β</span><span>,</span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>⋅</span><span>,</span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>β</span><span>⋅</span><span>}</span></span></span></span>。现在，如果下一个要读入的符号为 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span>，假设此时允许使用 <span><span>B→β⋅B\rightarrow\beta\cdot</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>β</span><span>⋅</span></span></span></span> 进行归约，那么进行归约后，符号 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span> 紧随 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 之后，即 <span><span>X∈FOLLOW(B)X\in FOLLOW(B)</span><span><span><span></span><span>X</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>B</span><span>)</span></span></span></span>。</p><p>所以，当</p><ul>
<li><span><span>FOLLOW(A)∩FOLLOW(B)=∅FOLLOW(A)\cap FOLLOW(B)=\emptyset</span><span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span><span></span><span>∩</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>B</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>∅</span></span></span></span>；</li>
<li>终结符号 <span><span>b∉FOLLOW(A),b∉FOLLOW(B)b\notin FOLLOW(A),b\notin FOLLOW(B)</span><span><span><span></span><span>b</span><span></span><span><span><span>∈</span></span><span><span><span><span></span><span><span><span>/</span><span></span></span></span><span></span></span></span></span></span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span><span>,</span><span></span><span>b</span><span></span><span><span><span>∈</span></span><span><span><span><span></span><span><span><span>/</span><span></span></span></span><span></span></span></span></span></span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>B</span><span>)</span></span></span></span>；</li>
</ul><p>则上述冲突全部消失，决策如下：</p><ol>
<li>当下一个读入符号 <span><span>x=bx=b</span><span><span><span></span><span>x</span><span></span><span>=</span><span></span></span><span><span></span><span>b</span></span></span></span> 时，用 <span><span>X→α⋅bβX\rightarrow\alpha\cdot b\beta</span><span><span><span></span><span>X</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>b</span><span>β</span></span></span></span> 移进 <span><span>bb</span><span><span><span></span><span>b</span></span></span></span>，即将  <span><span>bb</span><span><span><span></span><span>b</span></span></span></span> 入栈；</li>
<li>当下一个读入符号 <span><span>x∈FOLLOW(A)x\in FOLLOW(A)</span><span><span><span></span><span>x</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span></span></span></span> 时，用 <span><span>A→αA\rightarrow\alpha</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span></span></span></span> 归约；</li>
<li>当下一个读入符号 <span><span>x∈FOLLOW(B)x\in FOLLOW(B)</span><span><span><span></span><span>x</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>B</span><span>)</span></span></span></span> 时，用 <span><span>B→βB\rightarrow\beta</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>β</span></span></span></span> 归约；</li>
</ol><p>上述思路即为 SLR(1) 分析表构造的形式化描述。<strong>SLR(Simple LR)</strong> 算法的核心思想是利用“FOLLOW集”来解决冲突。</p></section><section><h5>SLR(1) 分析表的构造算法<a href="#slr1-分析表的构造算法"><span>#</span></a></h5><p><strong>输入</strong>：拓广文法 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span>；
<strong>输出</strong>：<span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 的 SLR 分析表</p><ol>
<li>构造 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 的 LR(0) 项目集规范族 <span><span>C={I0,I1,⋯ ,In}C=\{I_0,I_1,\cdots,I_n\}</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span><span>I</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>}</span></span></span></span>；</li>
<li>对于状态 <span><span>IiI_i</span><span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的分析动作如下：
<ol>
<li>若 <span><span>A→α⋅aβ∈IiA\rightarrow \alpha\cdot a\beta\in I_i</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>a</span><span>β</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，且 <span><span>go(Ii,a)=Ijgo(I_i,a)=I_j</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>a</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则 <span><span>action[i,a]=Sj(shift Ij)action[i,a]=S_j(shift\space I_j)</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span>i</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>s</span><span>hi</span><span>f</span><span>t</span><span> </span><span><span>I</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span>；</li>
<li>若 <span><span>A→α⋅∈IiA\rightarrow \alpha\cdot\in I_i</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>⋅</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则对 <span><span>∀a∈FOLLOW(A)\forall a\in FOLLOW(A)</span><span><span><span></span><span>∀</span><span>a</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>O</span><span>LL</span><span>O</span><span>W</span><span>(</span><span>A</span><span>)</span></span></span></span>，则 <span><span>action[i,a]=R A→αaction[i,a]=R\space A\rightarrow\alpha</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span>i</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>R</span><span> </span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span></span></span></span>；</li>
<li>若 <span><span>S′→S⋅∈IiS^\prime\rightarrow S\cdot\in I_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span>S</span><span>⋅</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则 <span>action[i,\</span>]=accept$，表示分析成功；</li>
</ol>
</li>
<li>若 <span><span>go(Ii,A)=Ijgo(I_i,A)=I_j</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>A</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，<span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 为非终结符，则 <span><span>goto[i,A]=jgoto[i,A]=j</span><span><span><span></span><span>g</span><span>o</span><span>t</span><span>o</span><span>[</span><span>i</span><span>,</span><span></span><span>A</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>j</span></span></span></span>；</li>
<li>剩余空白项均置为 error；</li>
<li>分析程序的初态为包含 <span><span>S′→⋅SS^\prime\rightarrow\cdot S</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>S</span></span></span></span> 的有效项目集/状态；</li>
</ol></section><section><h5>判断某文法是否属于 SLR(1) 文法<a href="#判断某文法是否属于-slr1-文法"><span>#</span></a></h5><p>法一（一般用于证明是）：若文法 G 的 SLR(1) 分析表中没有多重定义项，则该文法是 SLR(1) 文法。</p><p>法二（一般用于证明不是）：不一定要全部计算出 SLR 分析表，可以挑选状态中存在“移进-归约”冲突的，计算一下应用 FOLLOW 集合后是否能解决冲突，如果仍存在冲突（即对应的动作表位置存在多重表项），则该文法不是 SLR(1) 文法。</p><p><strong>定理</strong>：<em><strong>每一个 SLR(1) 文法都是无二义的文法</strong></em>，但并非无二义的文法都是 SLR(1) 文法。</p></section></section><section><h4>LR(1) 算法<a href="#lr1-算法"><span>#</span></a></h4><p>SLR(1) 算法向前查看下一个符号，并利用 FOLLOW 集进行识别，但这样仍然不准确（不能确保一定能归约），仍会导致冲突。</p><p>而 LR(1) 算法更加强大，利用了下一个读入的符号的信息（lookahead，展望符），或者也被称为<strong>预测先行</strong>（预测下一个应该出现的输入符号）。</p><section><h5>关键概念<a href="#关键概念-1"><span>#</span></a></h5><div><div><div></div><div>Note</div></div><div><p><strong>LR(1) 项目</strong>：<span><span>[A→α⋅β,a][A\rightarrow\alpha\cdot\beta,a]</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>β</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>，<span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 即为展望符，表示在当前处理到 <span><span>A→α⋅βA\rightarrow\alpha\cdot\beta</span><span><span><span></span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>β</span></span></span></span> 时，下一个读入的终结符必须是 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 才能进行正确归约。</p></div></div><p>由于项目引入了展望符，后续的一些计算也发生了一些改变，但构造过程基本一致。</p><div><div><div></div><div>Note</div></div><div><p><strong>后继形态</strong>：形态/项目 <span><span>C=[A→X⋅YZ,a]C=[A\rightarrow X\cdot YZ,a]</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span></span><span>⋅</span><span></span></span><span><span></span><span>Y</span><span>Z</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span> 遇到符号 <span><span>YY</span><span><span><span></span><span>Y</span></span></span></span> 转移到后继形态 <span><span>C′=[A→XY⋅Z,a]C^\prime=[A\rightarrow XY\cdot Z,a]</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>X</span><span>Y</span><span></span><span>⋅</span><span></span></span><span><span></span><span>Z</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>。（这里的区别只是 LR(1) 项目的变化）</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>转移函数 go</strong>：若 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 是文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的一个 LR(1) 项目集，<span><span>XX</span><span><span><span></span><span>X</span></span></span></span> 是一个文法符号，定义</p><span><span><span>go[I,X]=closure(J)go[I,X]=closure(J)</span><span><span><span></span><span>g</span><span>o</span><span>[</span><span>I</span><span>,</span><span></span><span>X</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>J</span><span>)</span></span></span></span></span><p>其中，<span><span>J={[A→αX⋅β,a]∣当 [A→α⋅Xβ,a]∈I 时}J=\{[A\rightarrow\alpha X\cdot\beta,a]\mid\text{当 }[A\rightarrow\alpha\cdot X\beta,a]\in I\text{ 时}\}</span><span><span><span></span><span>J</span><span></span><span>=</span><span></span></span><span><span></span><span>{[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>X</span><span></span><span>⋅</span><span></span></span><span><span></span><span>β</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>∣</span><span></span></span><span><span></span><span><span>当</span><span> </span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>X</span><span>β</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>∈</span><span></span></span><span><span></span><span>I</span><span><span> </span><span>时</span></span><span>}</span></span></span></span>。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>延伸形态</strong>：若一个形态的黑点后是非终结符，形如 <span><span>C=[A→α⋅Bβ,a]C=[A\rightarrow\alpha\cdot B\beta,a]</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>B</span><span>β</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>，且有 <span><span>B→η,b∈FIRST(βa)B\rightarrow\eta,b\in FIRST(\beta a)</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>η</span><span>,</span><span></span><span>b</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>β</span><span>a</span><span>)</span></span></span></span>，则 <span><span>C′=[B→⋅η,b]C^\prime=[B\rightarrow\cdot\eta,b]</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>[</span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>η</span><span>,</span><span></span><span>b</span><span>]</span></span></span></span> 是 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> 的延伸形态。</p><p>简单记忆的话，延伸形态的产生式左部是黑点后的非终结符 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span>，展望符是非终结符 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 之后的符号串 <span><span>β\beta</span><span><span><span></span><span>β</span></span></span></span> + 原展望符 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 的 FIRST 集合。</p><p>为什么呢？形态 <span><span>C=[A→α⋅Bβ,a]C=[A\rightarrow\alpha\cdot B\beta,a]</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>B</span><span>β</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span> 表示，当前符号栈为 <span><span>α\alpha</span><span><span><span></span><span>α</span></span></span></span>，期待遇到符号 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span>，再遇到符号串 <span><span>β\beta</span><span><span><span></span><span>β</span></span></span></span>，最后遇到 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 才能进行归约。我们现在准备“识别”非终结符 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span>，接下来要读入能推导出 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 的符号串，即对 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 的每个产生式建立相应的“延伸形态”。</p><p>当我们识别完 B 后，会继续识别 <span><span>β\beta</span><span><span><span></span><span>β</span></span></span></span>，所以延伸形态的展望符号应该是 <span><span>FIRST(β)FIRST(\beta)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>β</span><span>)</span></span></span></span>，不过由于 <span><span>FIRST(β)FIRST(\beta)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>β</span><span>)</span></span></span></span> 可能包含 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span>，那么再后面就是 <span><span>aa</span><span><span><span></span><span>a</span></span></span></span> 了，所以展望符号即为 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 的后继终结符集合，即 <span><span>FIRST(βa)FIRST(\beta a)</span><span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>β</span><span>a</span><span>)</span></span></span></span>。</p></div></div><p>同理闭包的构造方法也进行微调即可：</p><div><div><div></div><div>Note</div></div><div><p><strong>闭包的定义及构造方式</strong>：</p><ol>
<li><span><span>II</span><span><span><span></span><span>I</span></span></span></span> 中的每一个 LR(1) 项目均属于 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>；</li>
<li>若 <span><span>[A→α⋅Bβ,a][A\rightarrow\alpha\cdot B\beta,a]</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>B</span><span>β</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>，且有产生式 <span><span>B→ηB\rightarrow \eta</span><span><span><span></span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>η</span></span></span></span>，且 <span><span>b∈FIRST(βa)b\in FIRST(\beta a)</span><span><span><span></span><span>b</span><span></span><span>∈</span><span></span></span><span><span></span><span>F</span><span>I</span><span>R</span><span>S</span><span>T</span><span>(</span><span>β</span><span>a</span><span>)</span></span></span></span>，若 <span><span>[B→⋅η,b]∉closure(I)[B\rightarrow\cdot\eta,b]\notin closure(I)</span><span><span><span></span><span>[</span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>η</span><span>,</span><span></span><span>b</span><span>]</span><span></span><span><span><span>∈</span></span><span><span><span><span></span><span><span><span>/</span><span></span></span></span><span></span></span></span></span></span><span></span></span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>，则将 <span><span>[B→⋅η,b][B\rightarrow\cdot\eta,b]</span><span><span><span></span><span>[</span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>⋅</span><span>η</span><span>,</span><span></span><span>b</span><span>]</span></span></span></span> 加入 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span>；</li>
<li>重复步骤 2 直至 <span><span>closure(I)closure(I)</span><span><span><span></span><span>c</span><span>l</span><span>os</span><span>u</span><span>r</span><span>e</span><span>(</span><span>I</span><span>)</span></span></span></span> 不再增大；</li>
</ol></div></div></section><section><h5>构造文法 G 的 LR(1) 项目集规范族<a href="#构造文法-g-的-lr1-项目集规范族"><span>#</span></a></h5><p>输入为文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span>，输出为 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的 <span><span>LR(1)LR(1)</span><span><span><span></span><span>L</span><span>R</span><span>(</span><span>1</span><span>)</span></span></span></span> 项目集规范族 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span>：</p><ol>
<li>构造文法 <span><span>GG</span><span><span><span></span><span>G</span></span></span></span> 的拓广文法 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span>；</li>
<li><span>C=\{closure(\{[S^\prime\rightarrow\cdot S,\</span>]})}$；</li>
<li>对于 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> 中的每一个项目集 <span><span>II</span><span><span><span></span><span>I</span></span></span></span> 和每一个文法符号 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span>，如果 <span><span>go[I,X]go[I,X]</span><span><span><span></span><span>g</span><span>o</span><span>[</span><span>I</span><span>,</span><span></span><span>X</span><span>]</span></span></span></span> 不为空且不在 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> 中，则将 <span><span>go[I,X]go[I,X]</span><span><span><span></span><span>g</span><span>o</span><span>[</span><span>I</span><span>,</span><span></span><span>X</span><span>]</span></span></span></span> 加入 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span>；</li>
<li>重复步骤 2 直至 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> 不再更新；</li>
</ol><p>这里和 LR(0) 项目集规范族的构造过程是完全一致的（除了项目的表示形式不同）。同理，识别文法所有活前缀的 DFA 的构造方法也是一致的。</p></section><section><h5>LR(1) 分析表的构造算法<a href="#lr1-分析表的构造算法"><span>#</span></a></h5><p><strong>输入</strong>：拓广文法 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span>；
<strong>输出</strong>：<span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 的 SLR 分析表</p><ol>
<li>构造 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 的 LR(1) 项目集规范族 <span><span>C={I0,I1,⋯ ,In}C=\{I_0,I_1,\cdots,I_n\}</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span><span>I</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>}</span></span></span></span>；</li>
<li>对于状态 <span><span>IiI_i</span><span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的分析动作如下：
<ol>
<li>若 <span><span>[A→α⋅aβ,b]∈Ii[A\rightarrow \alpha\cdot a\beta,b]\in I_i</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>a</span><span>β</span><span>,</span><span></span><span>b</span><span>]</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，且 <span><span>go(Ii,a)=Ijgo(I_i,a)=I_j</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>a</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则 <span><span>action[i,a]=Sj(shift Ij)action[i,a]=S_j(shift\space I_j)</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span>i</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>s</span><span>hi</span><span>f</span><span>t</span><span> </span><span><span>I</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span>；</li>
<li>若 <span><span>[A→α⋅,a]∈Ii[A\rightarrow \alpha\cdot,a]\in I_i</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>⋅</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，且 <span><span>A≠S′A\ne S^\prime</span><span><span><span></span><span>A</span><span></span><span><span><span><span><span><span></span><span><span><span></span></span></span><span></span></span></span></span></span><span></span><span>=</span></span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span>，则 <span><span>action[i,a]=R A→αaction[i,a]=R\space A\rightarrow\alpha</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span>i</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>R</span><span> </span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span></span></span></span>；</li>
<li>若 <span>[S^\prime\rightarrow S\cdot,\</span>]\in I_i<span><span>，则，则 </span><span><span><span></span><span>，则</span></span></span></span>action[i,$]=accept$，表示分析成功；</li>
</ol>
</li>
<li>若 <span><span>go(Ii,A)=Ijgo(I_i,A)=I_j</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>A</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，<span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 为非终结符，则 <span><span>goto[i,A]=jgoto[i,A]=j</span><span><span><span></span><span>g</span><span>o</span><span>t</span><span>o</span><span>[</span><span>i</span><span>,</span><span></span><span>A</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>j</span></span></span></span>；</li>
<li>剩余空白项均置为 error；</li>
<li>分析程序的初态为包含 <span>[S^\prime\rightarrow\cdot S,\</span>]$ 的有效项目集/状态；</li>
</ol><p>相较于 SLR(1) 分析表，LR(1) 分析表的状态数量和每个状态中包含的项目数量都是及其恐怖的。不过状态数的增加带来的是分析能力的提升。</p></section><section><h5>LR(1) 文法<a href="#lr1-文法"><span>#</span></a></h5><div><div><div></div><div>Warning</div></div><div><p>不要求。</p></div></div><p>相比于 LR(0) 文法，LR(1) 文法允许：</p><ul>
<li>一个状态/项目集中同时出现可折叠状态和不可折叠状态；
<ul>
<li>只要可折叠形态的展望符不和不可折叠形态中黑点后面的符号冲突，不然遇到该输入，又分不清该移进还是归约了；</li>
</ul>
</li>
<li>也可以含有多条可折叠状态（归约状态）；
<ul>
<li>这些可折叠状态的展望符也不能冲突；</li>
</ul>
</li>
</ul><p>即要求：</p><ol>
<li>起始符号 S 不能位于任何产生式的右边；(一般通过拓广文法来解决)</li>
<li>要求每个状态中：
<ol>
<li>不能同时含有 <span><span>[A→α⋅aβ,b][A\rightarrow\alpha\cdot a\beta,b]</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>a</span><span>β</span><span>,</span><span></span><span>b</span><span>]</span></span></span></span> 和 <span><span>[B→η⋅,a][B\rightarrow\eta\cdot,a]</span><span><span><span></span><span>[</span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>η</span><span>⋅</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>；
<ul>
<li>否则会引起“移进-归约”冲突；</li>
</ul>
</li>
<li>不能同时含有 <span><span>[A→α⋅,a][A\rightarrow\alpha\cdot,a]</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>⋅</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span> 和 <span><span>[B→β⋅,a][B\rightarrow\beta\cdot,a]</span><span><span><span></span><span>[</span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>β</span><span>⋅</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span>；
<ul>
<li>否则会引起“归约-归约”冲突；</li>
</ul>
</li>
</ol>
</li>
</ol><p>解决冲突的一种方法是利用符号的优先级，不过此处略。</p></section></section><section><h4>LALR(1) 算法<a href="#lalr1-算法"><span>#</span></a></h4><p>前面已经提到，LR(1) 构造算法复杂且状态、形态数量多。而 LALR(1) 算法对此进行了一定的优化，其基本思想是将一些相似的状态进行 merge。一种 merge 方法是将 LR(1) 项目集规范族中的所有<strong>同心状态集</strong>合并，合并时，对应项目的搜索符（展望符）也会合并。原先各自接收的有向边，都改为由合并后的项目集接收；原先各自发出的有向边，都改为由合并后的项目集发出。</p><section><h5>关键概念<a href="#关键概念-2"><span>#</span></a></h5><div><div><div></div><div>Note</div></div><div><p><strong>同心集</strong>：如果两个 LR(1) 项目集去掉搜索符号（展望符）之后是相同的，则称这两个项目集具有相同的心（core），即这两个项目集是同心集。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>项目集的核（kernal）</strong>：除去初态项目集外，一个项目集的核（kernel）是由该项目集中那些圆点不在最左边的项目组成。</p><p>其中，初态项目集的核只有 <span>[S^\prime\rightarrow\cdot S,\</span>]$。</p></div></div></section><section><h5>LALR(1) 的项目集规范族<a href="#lalr1-的项目集规范族"><span>#</span></a></h5><ol>
<li>先得到 LR(1) 的项目集规范族；</li>
<li>合并 LR(1) 项目集规范族中的同心集；（减少分析表状态数）</li>
<li>用核代替项目集；（减少项目集的存储空间）</li>
<li>转移函数 go 同样进行合并修改；</li>
</ol></section><section><h5>LALR(1) 项目集中的冲突<a href="#lalr1-项目集中的冲突"><span>#</span></a></h5><p>合并可能导致“归约-归约”冲突，但不会引入新的“移进-归约”冲突，因为：</p><p>如果合并后产生“移进-归约”冲突，例如 <span><span>[A→α⋅aβ,b][A\rightarrow\alpha\cdot a\beta,b]</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>a</span><span>β</span><span>,</span><span></span><span>b</span><span>]</span></span></span></span> 和 <span><span>[B→η⋅,a][B\rightarrow\eta\cdot,a]</span><span><span><span></span><span>[</span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>η</span><span>⋅</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span> 被合并到一个项目集中，这说明原来 <span><span>[B→η⋅,a][B\rightarrow\eta\cdot,a]</span><span><span><span></span><span>[</span><span>B</span><span></span><span>→</span><span></span></span><span><span></span><span>η</span><span>⋅</span><span>,</span><span></span><span>a</span><span>]</span></span></span></span> 所在的项目集中还含有形如 <span><span>[A→α⋅aβ,c][A\rightarrow\alpha\cdot a\beta,c]</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>a</span><span>β</span><span>,</span><span></span><span>c</span><span>]</span></span></span></span> 的项目，而这两者原来存在于一个项目集，即原来已经存在“移进-归约”冲突。</p></section><section><h5>LALR(1) 分析表的构造算法<a href="#lalr1-分析表的构造算法"><span>#</span></a></h5><p><strong>输入</strong>：拓广文法 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span>；
<strong>输出</strong>：<span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 的 SLR 分析表</p><ol>
<li>构造 <span><span>G′G^\prime</span><span><span><span></span><span><span>G</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> 的 LR(1) 项目集规范族 <span><span>C={I0,I1,⋯ ,In}C=\{I_0,I_1,\cdots,I_n\}</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span><span>I</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>}</span></span></span></span>；</li>
<li>合并同心集。得到 <span><span>C′={J0,J1,⋯ ,Jm}C^\prime=\{J_0,J_1,\cdots,J_m\}</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span><span>J</span><span><span><span><span><span><span></span><span><span>0</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>}</span></span></span></span>；
<ul>
<li>分析程序的初态为包含 <span>[S^\prime\rightarrow\cdot S,\</span>]<span><span>的的</span><span><span><span></span><span>的</span></span></span></span>J_k$；</li>
</ul>
</li>
<li>构造 action 子表，对于状态 <span><span>JiJ_i</span><span><span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的分析动作如下：
<ol>
<li>若 <span><span>[A→α⋅aβ,b]∈Ji[A\rightarrow \alpha\cdot a\beta,b]\in J_i</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span></span><span>⋅</span><span></span></span><span><span></span><span>a</span><span>β</span><span>,</span><span></span><span>b</span><span>]</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，且 <span><span>go(Ji,a)=Jjgo(J_i,a)=J_j</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>J</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>a</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则 <span><span>action[i,a]=Sj(shift Jj)action[i,a]=S_j(shift\space J_j)</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span>i</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>s</span><span>hi</span><span>f</span><span>t</span><span> </span><span><span>J</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span>；</li>
<li>若 <span><span>[A→α⋅,a]∈Ji[A\rightarrow \alpha\cdot,a]\in J_i</span><span><span><span></span><span>[</span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span><span>⋅</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则 <span><span>action[i,a]=R A→αaction[i,a]=R\space A\rightarrow\alpha</span><span><span><span></span><span>a</span><span>c</span><span>t</span><span>i</span><span>o</span><span>n</span><span>[</span><span>i</span><span>,</span><span></span><span>a</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>R</span><span> </span><span>A</span><span></span><span>→</span><span></span></span><span><span></span><span>α</span></span></span></span>；</li>
<li>若 <span>[S^\prime\rightarrow S\cdot,\</span>]\in J_i<span><span>，则，则 </span><span><span><span></span><span>，则</span></span></span></span>action[i,$]=accept$，表示分析成功；</li>
</ol>
</li>
<li>构造 goto 子表：设合并后 <span><span>Jk={Ii1,Ii2,⋯ ,Iit}J_k=\{I_{i1},I_{i2},\cdots,I_{it}\}</span><span><span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>{</span><span><span>I</span><span><span><span><span><span><span></span><span><span><span>i</span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span><span>i</span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span><span>i</span><span>t</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>}</span></span></span></span>，原来这些 <span><span>IiI_i</span><span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 是同心集，那么显然 <span><span>go(Ii1,X),go(Ii2,X),⋯ ,go(Iit,X)go(I_{i1},X),go(I_{i2},X),\cdots,go(I_{it},X)</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>I</span><span><span><span><span><span><span></span><span><span><span>i</span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>X</span><span>)</span><span>,</span><span></span><span>g</span><span>o</span><span>(</span><span><span>I</span><span><span><span><span><span><span></span><span><span><span>i</span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>X</span><span>)</span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span>g</span><span>o</span><span>(</span><span><span>I</span><span><span><span><span><span><span></span><span><span><span>i</span><span>t</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>X</span><span>)</span></span></span></span> 也是同心集，把后继状态组成的同心集合并后的集合记为 <span><span>JiJ_i</span><span><span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，那么<span><span>go(Jk,X)=Jigo(J_k,X)=J_i</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>J</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>X</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>J</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>。
<ul>
<li>若 <span><span>go(Jk,A)=Iigo(J_k,A)=I_i</span><span><span><span></span><span>g</span><span>o</span><span>(</span><span><span>J</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>A</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>I</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，<span><span>AA</span><span><span><span></span><span>A</span></span></span></span> 为非终结符，则 <span><span>goto[k,A]=igoto[k,A]=i</span><span><span><span></span><span>g</span><span>o</span><span>t</span><span>o</span><span>[</span><span>k</span><span>,</span><span></span><span>A</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span>i</span></span></span></span>；</li>
<li>其实就是前面用文字描述的，原先各自接收的有向边，都改为由合并后的项目集接收；原先各自发出的有向边，都改为由合并后的项目集发出。</li>
</ul>
</li>
<li>剩余空白项均置为 error；</li>
</ol><p>基本与 LR(1) 分析表的构造算法一致，只需对同心集和对应的后继状态组成的同心集进行合并。</p></section><section><h5>LALR(1) 文法<a href="#lalr1-文法"><span>#</span></a></h5><ol>
<li>先判断是否属于 LR(1) 文法。（构造 LR(1) 分析表，采用<a href="#LR(1)%E6%96%87%E6%B3%95">前面</a>的方法判断是否有冲突）</li>
<li>如果不存在冲突，则是 LR(1) 文法，继续判断：
<ol>
<li>如果不存在同心集，即不用合并，那么显然也还是 LALR(1) 文法；</li>
<li>如果存在，则先进行合并，再检查合并后是否有冲突（即判断是否有“归约-归约”冲突）；
<ol>
<li>无冲突，是 LALR(1) 文法；</li>
<li>有冲突，不是 LALR(1) 文法；</li>
</ol>
</li>
</ol>
</li>
</ol></section><section><h5>LALR(1) vs. LR(1)<a href="#lalr1-vs-lr1"><span>#</span></a></h5><ul>
<li>形式上与 LR(1) 相同；</li>
<li>大小上与 SLR(1)/LR(0) 相当；</li>
<li>分析能力介于 SLR(1) 与 LR(1) 之间；</li>
</ul></section></section></section><section><h3>3.3 LR 分析的错误处理与恢复<a href="#33-lr-分析的错误处理与恢复"><span>#</span></a></h3><p>略。</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251021003335627.png" alt="" /></p></section></section>
<section><h2>4. 总结<a href="#4-总结"><span>#</span></a></h2><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/10/20251021003007262.png" alt="文法分类" /><figcaption>文法分类</figcaption></figure><p></p><p>“二义性文法”表示那些无论如何都无法消除二义性的文法，它们不可能被任何确定性分析方法识别。我们在上面介绍的均为确定性分析方法，即左侧这些。</p><p>左侧的分析方法又可以分为左侧（自顶向下）和右侧（自底向上），其中越外层表示的能处理的文法范围越大，分析能力越强，越内层针对文法的限制越多。</p><blockquote><p>TMD，很久没碰上这么一坨大的了💩。</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/an-incomplete-guide-to-bupt-computer-networking-practice/</id>
      <title type="text">BUPT 计网实践不完全指北</title>
      <published>2025-10-03T01:20:00.000Z</published>
      <updated>2025-10-03T01:20:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/an-incomplete-guide-to-bupt-computer-networking-practice/"/>
      <summary type="text">计算机网络实践这门课每个老师使用的仿真器并不相同，如果你有幸选了张海旸老师的课，那么恭喜你，你将穿越回 2007 年，和 BUPT 的历届计算机学子一起，使用原本在 Windows XP 系统上构建的仿真器 Dynamips 进行实验，由于年代久远，实验中发生不稳定现象也是常有的事。</summary>
      <content type="html"><![CDATA[<section><h2>写在前面<a href="#写在前面"><span>#</span></a></h2><p>计算机网络实践这门课每个老师使用的仿真器并不相同，如果你有幸选了张海旸老师的课，那么恭喜你，你将穿越回 2007 年，和 BUPT 的历届计算机学子一起，使用原本在 Windows XP 系统上构建的仿真器 Dynamips 进行实验，由于年代久远，实验中发生不稳定现象也是常有的事。</p><p>这篇文章主要是记录一下实验过程（前三次实验应该不用交报告，所以这里只是不正式的记录一下）。</p></section>
<section><h2>实验环境配置<a href="#实验环境配置"><span>#</span></a></h2><section><h3>简介<a href="#简介"><span>#</span></a></h3><ul>
<li>Dynamips 是一个 Cisco 路由器模拟软件。它可以模拟 Cisco 2600，2691，3620，3640，3660，3725，3745 和 Cisco7206 硬件平台，而且可以运行标准的 Cisco IOS 文件。</li>
<li>Dynagen 是 Dynamips 的一个基于文本的前端控制系统，它采用“Hypervisor”超级监控模式和 Dynamips 通信。Dynagen 简化了虚拟网络的创建和工作，主要用于模拟网络拓扑。</li>
</ul><p>你可以简单理解为后端和前端的区别，以下是我的实验环境，之后在老师退休前的若干年使用的仿真器也大抵如此：</p>

<table><thead><tr><th>配置项</th><th>参数</th></tr></thead><tbody><tr><td>操作系统</td><td>Windows 10 22H2 (OS Build 19045.2965)</td></tr><tr><td>网络模拟器</td><td>Dynamips 0.2.7 By N.L.F.E（后端）<br />Dynagen 0.11.0（前端）</td></tr></tbody></table><p>为了不污染本地环境，我装了个 Windows 10 的虚拟机，网卡选择的是<strong>桥接模式</strong>，这样不用额外配置端口映射等，便于远程连接。另外如果你在后续操作中找不到 telnet 命令，请通过以下方式<strong>启用 Windows 内置 Telnet 客户端</strong></p><ol>
<li>在 Windows 里打开 <strong>控制面板</strong> → <strong>程序和功能</strong> → <strong>启用或关闭 Windows 功能</strong></li>
<li>勾选 <strong>Telnet 客户端</strong></li>
<li>确定后等待安装完成</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004144207243.png" alt="" /></p></section><section><h3>安装 WinPcap<a href="#安装-winpcap"><span>#</span></a></h3><p>WinPcap，一个底层驱动，用于捕捉数据包和绕过协议栈方式来进行数据传输，通过运行 <code>setup</code> 目录下的 <code>1.安装Win_Pcap.cmd</code> 是无法直接在 Windows10 系统上进行安装的，你可以运行老师发的 <code>WinPcap_4_1_3.exe</code>，或者将脚本中的可执行文件版本替换成以下内容再通过脚本安装：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>@echo</span><span> </span><span>off</span></div></div><div><div><div>2</div></div><div><span>call</span><span> </span><span>../bin/script/copyright.cmd</span></div></div><div><div><div>3</div></div><div><span>echo</span><span> </span><span>*</span><span> </span><span>按任意键开始安装WinPcap</span><span>                                                     </span><span>*</span></div></div><div><div><div>4</div></div><div><span>echo</span><span> </span><span>*</span><span>=============================================================================</span><span>*</span></div></div><div><div><div>5</div></div><div><span>cd</span><span> </span><span>..</span></div></div><div><div><div>6</div></div><div><span>cd</span><span> </span><span>bin</span></div></div><div><div><div>7</div></div><div><span>cd</span><span> </span><span>winpcap</span></div></div><div><div><div>8</div></div><div><span>WinPcap_4_1_3.exe</span></div></div><div><div><div>9</div></div><div><span>exit</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>安装过程一路 next 就行。</p></section><section><h3>设置网卡参数<a href="#设置网卡参数"><span>#</span></a></h3><p>这步直接在 <code>setup</code> 目录下运行 <code>2.修改网卡参数.cmd</code> 大概率得不到输出的网卡参数，通过该脚本得知其通过调用 <code>/bin/script/get_para.php</code> 获取网卡参数，而 <code>get_para.php</code> 又是通过调用 <code>bin/dynamips/dynamips-wxp.exe</code> 获取网卡信息并通过正则表达式提取的。</p><p>直接运行 <code>bin/dynamips/dynamips-wxp.exe</code> 可以正确得到网卡信息：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004113724174.png" alt="" /></p><p>问题大概率是在正则表达式上，但是尝试修改仍有问题，所以干脆直接把上面的信息复制下来，即 <code>\Device\NPF_{xxxxxx}</code> 就是网卡参数，你也可以通过在终端运行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>getmac</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>得到：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004113950336.png" alt="" /></p><p>将上面内容中的 <code>Tcpip</code> 改为 <code>NPF</code> 也还是一样的。</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004114126299.png" alt="" /></p></section></section>
<section><h2>实验一：基本交换机配置<a href="#实验一基本交换机配置"><span>#</span></a></h2><section><h3>1. 启动前后端<a href="#1-启动前后端"><span>#</span></a></h3><ol>
<li>运行 <code>0.虚拟服务XP&amp;2003.exe</code> 启动后端；</li>
</ol><p>其中有用的事实上就以下 2 句：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>tmp</span></div></div><div><div><div>2</div></div><div><span>start</span><span> </span><span>/belownormal</span><span> </span><span>/B</span><span> </span><span>/wait</span><span> </span><span>"Dynamips"</span><span> </span><span>"../bin/dynamips/dynamips-wxp.exe"</span><span> </span><span>-H</span><span> </span><span>7200</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中：</p><ul>
<li><code>tmp</code> 一般用来存放 Dynamips 的临时文件或运行环境，比如 idlepc 数据库；</li>
<li>启动 dynamips-wxp.exe 程序：
<ul>
<li><code>H 7200</code> 表示 Dynamips 将监听 <strong>7200端口</strong>，用于和前端软件（如 Dynagen）通信；</li>
<li><code>/belownormal</code> 让进程以较低优先级运行，避免占用过多 CPU；</li>
</ul>
<ul>
<li><code>/B</code> 表示不打开新的命令窗口，在后台运行；</li>
<li><code>/wait</code> 表示等待进程结束后才返回；</li>
<li><code>"Dynamips"</code> 是启动窗口的标题；</li>
</ul>
</li>
</ul><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004130925259.png" alt="" /></p><ol>
<li>运行 <code>bupt_sw.cmd</code> 启动交换机实验环境（前端）；</li>
</ol><p>该脚本有用的内容也只有以下内容：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>tmp</span></div></div><div><div><div>2</div></div><div><span>"../bin/dynagen/dynagen.exe"</span><span> </span><span>..</span><span>\n</span><span>et</span><span>\b</span><span>upt_sw.net</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>即 运行 <code>dynagen.exe</code> 并把 <code>..\net\bupt_sw.net</code> 作为输入拓扑文件。</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004131522755.png" alt="" /></p></section><section><h3>2. 实验用拓扑<a href="#2-实验用拓扑"><span>#</span></a></h3><p><code>bupt_sw.net</code> 文件内容如下：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>autostart = False</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>[localhost]</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>port = 7200</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>udp = 10000</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>workingdir = ..\tmp\</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>[[router SW1]]</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>image = ..\ios\unzip-c3640-js-mz.124-10.bin</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>model = 3640</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>console = 3003</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>ram = 128</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>confreg = 0x2142</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>exec_area = 64</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>mmap = False</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>slot1 = NM-16ESW</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>f1/1 = SW2 f1/2</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>f1/2 = SW2 f1/1</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>f1/11 = PC1 f0/0</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>f1/12 = PC2 f0/0</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>[[router SW2]]</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>image = ..\ios\unzip-c3640-js-mz.124-10.bin</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>model = 3640</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>console = 3004</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>ram = 128</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>confreg = 0x2142</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>exec_area = 64</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>mmap = False</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>slot1 = NM-16ESW</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>f1/11 = PC3 f0/0</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>[[router PC1]]</span></div></div><div><div><div>36</div></div><div><span><span>  </span></span><span>model = 2621</span></div></div><div><div><div>37</div></div><div><span><span>  </span></span><span>ram = 20</span></div></div><div><div><div>38</div></div><div><span><span>  </span></span><span>image = ..\ios\unzip-c2600-i-mz.121-3.T.bin</span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>mmap = False</span></div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>confreg = 0x2142</span></div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>console = 3006</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>[[router PC2]]</span></div></div><div><div><div>44</div></div><div><span><span>  </span></span><span>model = 2621</span></div></div><div><div><div>45</div></div><div><span><span>  </span></span><span>ram = 20</span></div></div><div><div><div>46</div></div><div><span><span>  </span></span><span>image = ..\ios\unzip-c2600-i-mz.121-3.T.bin</span></div></div><div><div><div>47</div></div><div><span><span>  </span></span><span>mmap = False</span></div></div><div><div><div>48</div></div><div><span><span>  </span></span><span>confreg = 0x2142</span></div></div><div><div><div>49</div></div><div><span><span>  </span></span><span>console = 3007</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>[[router PC3]]</span></div></div><div><div><div>52</div></div><div><span><span>  </span></span><span>model = 2621</span></div></div><div><div><div>53</div></div><div><span><span>  </span></span><span>ram = 20</span></div></div><div><div><div>54</div></div><div><span><span>  </span></span><span>image = ..\ios\unzip-c2600-i-mz.121-3.T.bin</span></div></div><div><div><div>55</div></div><div><span><span>  </span></span><span>mmap = False</span></div></div><div><div><div>56</div></div><div><span><span>  </span></span><span>confreg = 0x2142</span></div></div><div><div><div>57</div></div><div><span><span>  </span></span><span>console = 3008</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>其中：</p><ul>
<li><code>autostart = False</code> 表示 Dynagen 载入拓扑后 <strong>不会自动启动设备</strong>，需要在 Dynagen 控制台中手动启动；</li>
<li><code>localhost</code>：指 Dynagen 要连接的 Dynamips 服务端运行在本机上。</li>
</ul><ul>
<li><code>port = 7200</code>：对应前一个脚本里 Dynamips 启动参数 -H 7200。Dynagen 会通过 TCP 7200 端口控制 Dynamips。</li>
<li><code>udp = 10000</code>：设置 Dynamips 实例的起始 UDP 端口（用于设备之间的帧交换通信）。</li>
<li><code>workingdir = ..\tmp\**</code>：Dynagen 会把各个设备的启动配置、NVRAM、快照等文件存放在这个路径中。</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[[router SW1]]</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>image = ..\ios\unzip-c3640-js-mz.124-10.bin</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>model = 3640</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>console = 3003</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>ram = 128</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>confreg = 0x2142</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>exec_area = 64</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>mmap = False</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>slot1 = NM-16ESW</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>f1/1 = SW2 f1/2</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>f1/2 = SW2 f1/1</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>f1/11 = PC1 f0/0</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>f1/12 = PC2 f0/0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>表示一台 Cisco 3640 路由器，加载了一个带交换模块的 IOS。</li>
<li><code>slot1 = NM-16ESW</code> 表示插入了一块 <strong>16端口交换模块</strong>。</li>
<li><code>console = 3003</code> 表示可通过 TCP 3003 端口连接控制台（例如用 telnet）。</li>
<li><code>ram = 128</code> 设置虚拟机内存大小（单位 MB）。</li>
<li><code>image</code> 指向 IOS 文件路径。</li>
<li>端口连接如下：
<ul>
<li>SW1 的 FastEthernet1/1 ↔ SW2 的 FastEthernet1/2</li>
<li>SW1 的 FastEthernet1/2 ↔ SW2 的 FastEthernet1/1（即双链路连接）</li>
<li>SW1 的 FastEthernet1/11 ↔ PC1 的 FastEthernet0/0</li>
<li>SW1 的 FastEthernet1/12 ↔ PC2 的 FastEthernet0/0</li>
</ul>
</li>
</ul><p>SW1 和 SW2 介绍略。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[[router PC1]]</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>model = 2621</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>ram = 20</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>image = ..\ios\unzip-c2600-i-mz.121-3.T.bin</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>mmap = False</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>confreg = 0x2142</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>console = 3006</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li>模型：Cisco 2621（轻量路由器）</li>
<li>RAM 仅 20 MB</li>
<li>IOS 文件：unzip-c2600-i-mz.121-3.T.bin</li>
<li>可以通过 Telnet 连接相应端口访问（例如 telnet localhost 3006）。</li>
</ul><p>对应的拓扑图如下：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004133305411.png" alt="" /></p></section><section><h3>3. 启动设备<a href="#3-启动设备"><span>#</span></a></h3><ol>
<li>启动 SW1：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>start</span><span> </span><span>SW1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>设置 idlepc（该设备的 CPU 占用率）：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>idlepc</span><span> </span><span>get</span><span> </span><span>SW1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>等待一段时间后，选择具有 * 号且括号中数值最大项的序号作为 idlepc 值（如果都没有 * 可以再重新统计一次），例如：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004134222545.png" alt="" /></p><p>保存：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>idlepc</span><span> </span><span>save</span><span> </span><span>SW1</span><span> </span><span>db</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004134428111.png" alt="" /></p><p>由于 PC 和 SW 使用的是不同的设备，所以也要在启动 PC1 时按照上述方法配置以下，演示略。</p><ol>
<li>配置设备；</li>
</ol><p>在新的 cmd 窗口中输入：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>telnet localhost 3006</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004145515576.png" alt="" /></p><p>输入 <code>en</code> 进入配置权限（<code>show running-config</code> 用于查看当前配置内容）：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004152615326.png" alt="" /></p><p>输入 <code>conf t</code> 进入配置命令行；</p><p>通过 <code>interface fx/y</code> 配置指定端口，通过 <code>ip add x.y.z.w 255.0.0.0</code> 添加端口对应的 IP 地址。</p><p>比如为该接口分配一个 IPv4 地址：</p><ul>
<li>IP 地址：1.1.1.1</li>
<li>子网掩码：255.0.0.0（即 /8）</li>
</ul><p>这意味着 PC1 所在的网络为 <code>1.0.0.0/8</code>。</p><p>本实验需要配置的内容如下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># start and configure SW1</span></div></div><div><div><div>2</div></div><div><span>start</span><span> </span><span>SW1</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># start and configure PC1</span></div></div><div><div><div>5</div></div><div><span>start</span><span> </span><span>PC1</span></div></div><div><div><div>6</div></div><div><span>telnet</span><span> </span><span>localhost</span><span> </span><span>3006</span></div></div><div><div><div>7</div></div><div><span>en</span></div></div><div><div><div>8</div></div><div><span>conf</span><span> </span><span>t</span></div></div><div><div><div>9</div></div><div><span>interface</span><span> </span><span>f0/0</span></div></div><div><div><div>10</div></div><div><span>ip</span><span> </span><span>add</span><span> </span><span>1.1.1.1</span><span> </span><span>255.0.0.0</span></div></div><div><div><div>11</div></div><div><span>no</span><span> </span><span>shutdown</span><span> </span><span># 开启接口（Cisco 默认接口是 shutdown 的）</span></div></div><div><div><div>12</div></div><div><span>exit</span></div></div><div><div><div>13</div></div><div><span>no</span><span> </span><span>cdp</span><span> </span><span>run</span><span> </span><span># CDP 是 Cisco 的邻居发现协议，一般在“PC 模拟设备”上没必要运行。关闭它可以减少不必要的控制流量</span></div></div><div><div><div>14</div></div><div><span>exit</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># configure PC2</span></div></div><div><div><div>17</div></div><div><span>start</span><span> </span><span>PC2</span></div></div><div><div><div>18</div></div><div><span>telnet</span><span> </span><span>localhost</span><span> </span><span>3007</span></div></div><div><div><div>19</div></div><div><span>en</span></div></div><div><div><div>20</div></div><div><span>conf</span><span> </span><span>t</span></div></div><div><div><div>21</div></div><div><span>interface</span><span> </span><span>f0/0</span></div></div><div><div><div>22</div></div><div><span>ip</span><span> </span><span>add</span><span> </span><span>1.1.1.2</span><span> </span><span>255.0.0.0</span></div></div><div><div><div>23</div></div><div><span>no</span><span> </span><span>shutdown</span><span> </span><span># 开启接口（Cisco 默认接口是 shutdown 的）</span></div></div><div><div><div>24</div></div><div><span>exit</span></div></div><div><div><div>25</div></div><div><span>no</span><span> </span><span>cdp</span><span> </span><span>run</span><span> </span><span># CDP 是 Cisco 的邻居发现协议，一般在“PC 模拟设备”上没必要运行。关闭它可以减少不必要的控制流量</span></div></div><div><div><div>26</div></div><div><span>exit</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>以 PC2 为例：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004160355049.png" alt="" /></p><p>配置完后，网络的拓扑如下：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004160521712.png" alt="" /></p><p>此时 PC1 和 PC2 互 Ping，Ping 通即说明成功：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004160640414.png" alt="" /></p><p>启用并配置剩余的节点：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># start and configure SW2</span></div></div><div><div><div>2</div></div><div><span>start</span><span> </span><span>SW2</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># start and configure PC1</span></div></div><div><div><div>5</div></div><div><span>start</span><span> </span><span>PC1</span></div></div><div><div><div>6</div></div><div><span>telnet</span><span> </span><span>localhost</span><span> </span><span>3008</span></div></div><div><div><div>7</div></div><div><span>en</span></div></div><div><div><div>8</div></div><div><span>conf</span><span> </span><span>t</span></div></div><div><div><div>9</div></div><div><span>interface</span><span> </span><span>f0/0</span></div></div><div><div><div>10</div></div><div><span>ip</span><span> </span><span>add</span><span> </span><span>1.1.1.3</span><span> </span><span>255.0.0.0</span></div></div><div><div><div>11</div></div><div><span>no</span><span> </span><span>shutdown</span><span> </span><span># 开启接口（Cisco 默认接口是 shutdown 的）</span></div></div><div><div><div>12</div></div><div><span>exit</span></div></div><div><div><div>13</div></div><div><span>no</span><span> </span><span>cdp</span><span> </span><span>run</span><span> </span><span># CDP 是 Cisco 的邻居发现协议，一般在“PC 模拟设备”上没必要运行。关闭它可以减少不必要的控制流量</span></div></div><div><div><div>14</div></div><div><span>exit</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>配置完后拓扑结构如下：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004164135550.png" alt="" /></p><p>此时 PC1 和 PC3 互 Ping，Ping 通即说明成功：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251004164254999.png" alt="" /></p></section></section>
<section><h2>实验二：路由器的简单配置<a href="#实验二路由器的简单配置"><span>#</span></a></h2><section><h3>1.说明<a href="#1说明"><span>#</span></a></h3><p>本实验用到的网络拓扑结构如下，配置见 <code>net/bupt_RT.net</code>：</p><p><img src="https://webp-pic.yokumi.cn/2025/10/20251017171613698.png" alt="" /></p></section><section><h3>2. 配置步骤<a href="#2-配置步骤"><span>#</span></a></h3><ol>
<li>在 Dynamips 中启动：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>start</span><span> </span><span>R1</span></div></div><div><div><div>2</div></div><div><span>start</span><span> </span><span>R2</span></div></div><div><div><div>3</div></div><div><span>start</span><span> </span><span>PC1</span></div></div><div><div><div>4</div></div><div><span>start</span><span> </span><span>PC2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>配置 R1 和 R2 间的连接</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># R1</span></div></div><div><div><div>2</div></div><div><span>telnet</span><span> </span><span>localhost</span><span> </span><span>3001</span></div></div><div><div><div>3</div></div><div><span>R1</span><span>&gt;</span><span>en</span></div></div><div><div><div>4</div></div><div><span>R1#conf</span><span> </span><span>t</span></div></div><div><div><div>5</div></div><div><span>R1(config</span><span>)#interface s1/1</span></div></div><div><div><div>6</div></div><div><span>R1(config-if</span><span>)#clock rate 115200</span></div></div><div><div><div>7</div></div><div><span>R1(config-if</span><span>)#ip add 1.1.1.1 255.0.0.0</span></div></div><div><div><div>8</div></div><div><span>R1(config-if</span><span>)#encapsulation PPP</span></div></div><div><div><div>9</div></div><div><span>R1(config-if</span><span>)#no shutdown</span></div></div><div><div><div>10</div></div><div><span># 注意这里会观察到 s1/1 未启动，这是因为点对点(PPP)链路需要两端串口均配置完毕，等 R2 配置完就能观察到输出 s1/1 正确启动了</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># R2</span></div></div><div><div><div>13</div></div><div><span>telnet</span><span> </span><span>localhost</span><span> </span><span>3002</span></div></div><div><div><div>14</div></div><div><span>R2</span><span>&gt;</span><span>en</span></div></div><div><div><div>15</div></div><div><span>R2#conf</span><span> </span><span>t</span></div></div><div><div><div>16</div></div><div><span>R2(config</span><span>)#interface s1/0</span></div></div><div><div><div>17</div></div><div><span>R2(config-if</span><span>)#ip add 1.1.1.2 255.0.0.0</span></div></div><div><div><div>18</div></div><div><span>R2(config-if</span><span>)#encapsulation PPP</span></div></div><div><div><div>19</div></div><div><span>R2(config-if</span><span>)#no shutdown</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span># 这里可以先测试一下 R1 和 R2 链路是否能 Ping 通</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>配置 R1 与 PC1，R2 与 PC2 连接</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># PC1</span></div></div><div><div><div>2</div></div><div><span>telnet</span><span> </span><span>localhost</span><span> </span><span>3003</span></div></div><div><div><div>3</div></div><div><span>PC1</span><span>&gt;</span><span>en</span></div></div><div><div><div>4</div></div><div><span>PC1#cont</span></div></div><div><div><div>5</div></div><div><span>PC1(config</span><span>)#interface f0/0</span></div></div><div><div><div>6</div></div><div><span>PC1(config-if</span><span>)#ip add 2.1.1.2 255.0.0.0</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># R1</span></div></div><div><div><div>9</div></div><div><span>R1(config</span><span>)#interface f0/0</span></div></div><div><div><div>10</div></div><div><span>R1(config-if</span><span>)#ip add 2.1.1.1 255.0.0.0</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># PC2</span></div></div><div><div><div>13</div></div><div><span>telnet</span><span> </span><span>localhost</span><span> </span><span>3004</span></div></div><div><div><div>14</div></div><div><span>PC2</span><span>&gt;</span><span>en</span></div></div><div><div><div>15</div></div><div><span>PC2#cont</span></div></div><div><div><div>16</div></div><div><span>PC2(config</span><span>)#interface f0/0</span></div></div><div><div><div>17</div></div><div><span>PC2(config-if</span><span>)#ip add 3.1.1.2 255.0.0.0</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span># R2</span></div></div><div><div><div>20</div></div><div><span>R2(config</span><span>)#interface f0/0</span></div></div><div><div><div>21</div></div><div><span>R2(config-if</span><span>)#ip add 3.1.1.1 255.0.0.0</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span># 此时可以测试一下 PC1 和 R1，PC2 和 R2 之间的链路是否能 Ping 通</span></div></div><div><div><div>24</div></div><div><span># 需要注意的是此时 PC1 和 R2，PC1 和 PC2 之间还是无法 Ping 通的，因为还没有配置路由表</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>配置路由表</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># R1</span></div></div><div><div><div>2</div></div><div><span>R1(config</span><span>)#ip route 3.0.0.0 255.0.0.0 serial 1/1</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># R2</span></div></div><div><div><div>5</div></div><div><span>R2(config</span><span>)#ip route 2.0.0.0 255.0.0.0 serial 1/0</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># PC1</span></div></div><div><div><div>8</div></div><div><span>PC1(config</span><span>)#ip route 0.0.0.0 0.0.0.0 F 0/0</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># PC2</span></div></div><div><div><div>11</div></div><div><span>PC2(config</span><span>)#ip route 0.0.0.0 0.0.0.0 F 0/0</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 实验结果<a href="#3-实验结果"><span>#</span></a></h3><ol>
<li>Ping 一下 PC1 和 PC2</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PC1&gt;ping 3.1.1.2</span></div></div><div><div><div>2</div></div><div><span>PC2&gt;ping 2.1.1.2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img src="https://webp-pic.yokumi.cn/2025/10/20251017195207164.png" alt="" /></p><p><strong>注</strong>：如果是第一次 Ping，你基本可以重复复现第一次失败，即（4/5），这是因为当主机/路由器第一次向目标 IP 发送报文时，若本地 ARP 缓存没有目标 IP 对应的 MAC 地址，会先发出 ARP 请求（广播）等待 ARP 回复。第一个 ICMP echo 请求要等到 ARP 完成才能被正确封装并发送出去，因此第一个请求常常“超时/丢失”。随后 ARP 被学到，后面的包就能成功到达。</p><p>你可以通过在第一次 Ping 前后分别运行 <code>show ip arp</code> 查看 ARP 解析表验证这一点。</p><ol>
<li>查看一下路由表</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/10/20251017195501281.png" alt="" /></p></section></section>
<section><h2>常用 Dynagen 命令速查表<a href="#常用-dynagen-命令速查表"><span>#</span></a></h2>

<table><thead><tr><th><strong>命令</strong></th><th><strong>作用</strong></th><th><strong>示例</strong></th></tr></thead><tbody><tr><td>list</td><td>显示当前拓扑中所有设备及状态</td><td>list</td></tr><tr><td>start &lt;设备名&gt;</td><td>启动指定设备</td><td>start SW1</td></tr><tr><td>start /all</td><td>启动所有设备</td><td>start /all</td></tr><tr><td>stop &lt;设备名&gt;</td><td>停止指定设备</td><td>stop SW1</td></tr><tr><td>stop /all</td><td>停止所有设备</td><td>stop /all</td></tr><tr><td>console &lt;设备名&gt;</td><td>连接到设备的控制台（telnet 到对应端口）</td><td>console SW1</td></tr><tr><td>reload &lt;设备名&gt;</td><td>重启指定设备（相当于reload命令）</td><td>reload PC1</td></tr><tr><td>telnet &lt;地址&gt; &lt;端口&gt;</td><td>连接到地址对应的设备控制台</td><td>telnet localhost 3001</td></tr></tbody></table></section>
<section><h2>常用 Dynamips 命令速查表<a href="#常用-dynamips-命令速查表"><span>#</span></a></h2>

<table><thead><tr><th><strong>命令</strong></th><th><strong>作用</strong></th><th><strong>示例</strong></th></tr></thead><tbody><tr><td></td><td>用户模式</td><td>提示符形如：<code>PC1&gt;</code></td></tr><tr><td>en</td><td>特权模式</td><td>提示符形如：<code>PC1#</code></td></tr><tr><td>conf t</td><td>全局配置模式</td><td>提示符形如：<code>PC1(config)#</code></td></tr><tr><td>interface fx/y 或 interface sx/y</td><td>接口配置模式</td><td>提示符形如：<code>PC1(config-if)#</code></td></tr><tr><td>exit</td><td>退出当前配置模式返回上一级</td><td></td></tr><tr><td>(<code>PC1#</code>) hostname othername</td><td>改名</td><td>提示符变为：<code>othername#</code></td></tr><tr><td>(<code>PC1#</code>) show ip route</td><td>显示当前的路由表信息</td><td></td></tr><tr><td>(<code>PC1#</code>) show ip arp</td><td>显示当前 ARP 解析信息</td><td></td></tr><tr><td>(<code>PC1(config)#</code>) no cdp run</td><td>CDP 是 Cisco 的邻居发现协议，一般在“PC 模拟设备”上没必要运行。关闭它可以减少不必要的控制流量</td><td></td></tr><tr><td>(<code>PC1(config)#</code>) ip route 0.0.0.0 0.0.0.0 f0/0</td><td>配置默认路由，如果路由表中没有匹配到其他更具体路由，就把包交给 FastEthernet 0/0接口发出去。</td><td></td></tr><tr><td>(<code>R1(config)#</code>) ip route 3.0.0.0 255.0.0.0 serial 1/1</td><td>配置静态路由，如果要去往 3.0.0.0/8 网络的数据包，就从 Serial 1/1 接口发出去。</td><td></td></tr><tr><td>(<code>PC1(config-if)#</code>) ip add &lt;ip addr&gt; &lt;mask&gt;</td><td>配置接口的 IP 地址</td><td>ip add 2.1.1.1 255.0.0.0</td></tr><tr><td>no &lt;instruction&gt;</td><td>撤回配置错误的指令</td><td>no ip add 2.1.1.1 255.0.0.0</td></tr><tr><td>(<code>R1(config-if)#</code>) clock rate &lt;clockrate&gt;</td><td>配置路由器 DCE 串口的时钟速率，单位为 kbps（<strong>DTE 端不要配置</strong>）</td><td>clock rate 115200</td></tr><tr><td>(<code>R1(config-if)#</code>) encapsulation PPP</td><td>配置路由器的串口的数据链路层协议（默认为 HDLC）</td><td>encapsulation PPP</td></tr><tr><td>(<code>PC1(config-if)#</code>) no shutdown</td><td>启动当前接口</td><td></td></tr></tbody></table></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/introduction-to-python-package-manager-uv/</id>
      <title type="text">Python 套件管理器 uv 使用介绍</title>
      <published>2025-09-05T11:56:00.000Z</published>
      <updated>2025-09-05T11:56:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/introduction-to-python-package-manager-uv/"/>
      <summary type="text">Python 生态得益于大数据时代和大模型时代蓬勃发展，相应的包管理工具也不断涌现，这篇文章简单记录一下个人上手 Astral 团队开发的 uv 的一些基操和使用一段时间后的体验。当然其实官方文档[^1]已经很详细了。 下表转载自 Shoukai Huang 大佬的博客[^2] 整理的横向对比：</summary>
      <content type="html"><![CDATA[<section><h2>写在前面<a href="#写在前面"><span>#</span></a></h2><p>Python 生态得益于大数据时代和大模型时代蓬勃发展，相应的包管理工具也不断涌现，这篇文章简单记录一下个人上手 <a href="https://astral.sh/" target="_blank">Astral</a> 团队开发的 <a href="https://github.com/astral-sh/uv" target="_blank">uv</a> 的一些基操和使用一段时间后的体验。当然其实官方文档<sup><a href="#user-content-fn-1">1</a></sup>已经很详细了。</p><div><div><div></div><div>Warning</div></div><div><p>如有错误，欢迎指正！</p></div></div></section>
<section><h2>1. What’s uv?<a href="#1-whats-uv"><span>#</span></a></h2><div><div><div></div><div>Note</div></div><div><p>An extremely fast Python package manager. —— 开发者原话</p></div></div><ul>
<li>一款现代化的 Python <strong>包管理暨打包</strong>（packing）工具；</li>
<li><strong>高性能</strong>，安装依赖速度比传统工具提升几十倍（得益于 Rust）；</li>
<li>一大特色，或者说最大优点是<strong>对 Python 版本的管理</strong>，不必再搭配 <code>pyenv</code> 等工具；</li>
<li><strong>跨平台支持</strong>，适用多种开发环境</li>
<li><strong>热 Cache 机制设计</strong>，二次构建快</li>
</ul></section>
<section><h2>2. Why uv ?<a href="#2-why-uv-"><span>#</span></a></h2><p>下表转载自 <a href="https://apframework.com/blog/essay/2025-06-07-Python-UV" target="_blank">Shoukai Huang 大佬的博客</a><sup><a href="#user-content-fn-2">2</a></sup> 整理的横向对比：</p>

<table><thead><tr><th>功能特性</th><th>UV</th><th>pip+virtualenv</th><th>Conda</th><th>Poetry</th></tr></thead><tbody><tr><td>实现语言</td><td>Rust</td><td>Python</td><td>Python</td><td>Python</td></tr><tr><td>速度</td><td>比pip快10-100倍</td><td>基准线</td><td>慢于pip</td><td>快于pip</td></tr><tr><td>内存使用</td><td>非常高效</td><td>较高</td><td>高</td><td>中等</td></tr><tr><td>环境管理</td><td>内置</td><td>需要单独工具</td><td>内置</td><td>内置</td></tr><tr><td>依赖解析</td><td>快速、现代解析器</td><td>基础</td><td>全面</td><td>现代解析器</td></tr><tr><td>非Python包支持</td><td>否</td><td>否</td><td>是</td><td>否</td></tr><tr><td>锁文件</td><td>是</td><td>否(基本requirements.txt)</td><td>是</td><td>是</td></tr><tr><td>项目结构化</td><td>是</td><td>否</td><td>否</td><td>是</td></tr><tr><td>包发布</td><td>是</td><td>是(需要twine)</td><td>是</td><td>是</td></tr><tr><td>兼容性</td><td>与现有pip生态系统兼容</td><td>标准Python工具</td><td>自有生态系统</td><td>更固执己见的方法</td></tr><tr><td>错误处理</td><td>清晰的错误信息</td><td>基本</td><td>良好</td><td>良好</td></tr><tr><td>资源占用</td><td>最小</td><td>中等</td><td>重</td><td>中等</td></tr><tr><td>科学计算焦点</td><td>否</td><td>否</td><td>是</td><td>否</td></tr><tr><td>跨平台一致性</td><td>是</td><td>有限</td><td>优秀</td><td>良好</td></tr></tbody></table><p>总而言之：</p><ul>
<li>uv vs. pip + virtualenv
<ul>
<li>快，跨环境</li>
</ul>
</li>
<li>uv vs. Conda
<ul>
<li>太臃肿了 Conda 我说，而且环境一多比较慢</li>
</ul>
</li>
<li>uv vs. Poetry
<ul>
<li>常用指令少且简洁，相比之下 Poetry 的学习成本就有点偏高了</li>
<li>内建了对 Python 的版本管理功能</li>
<li>兼容 pip, pip-tools</li>
</ul>
</li>
<li>uv vs. pyenv
<ul>
<li>并非竞品，pyenv 只是用于管理多个 Python 版本的工具（一般是 Poetry + pyenv 组合），与 uv 的不同是以源码编译的方式安装 Python</li>
</ul>
</li>
</ul><p>如果你还在使用 pip，那我觉得针对大型项目，亦或是中小型项目，不妨跳出舒适圈，试试更舒适（纯主观）的 uv 吧？</p></section>
<section><h2>3. How to use uv?<a href="#3-how-to-use-uv"><span>#</span></a></h2><section><h3>3.1 Installation<a href="#31-installation"><span>#</span></a></h3><p>以下内容在 macOS 系统上进行。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># On macOS and Linux.</span></div></div><div><div><div>2</div></div><div><span>curl</span><span> </span><span>-LsSf</span><span> </span><span>https://astral.sh/uv/install.sh</span><span> | </span><span>sh</span></div></div><div><div><div>3</div></div><div><span># or use Homebrew on macOS</span></div></div><div><div><div>4</div></div><div><span>brew</span><span> </span><span>install</span><span> </span><span>uv</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用 Python 的第三方库 <a href="https://pypi.org/project/uv/" target="_blank">pypi</a> 安装当然也没问题：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pip</span><span> </span><span>install</span><span> </span><span>uv</span></div></div><div><div><div>2</div></div><div><span># Or pipx.</span></div></div><div><div><div>3</div></div><div><span>pipx</span><span> </span><span>install</span><span> </span><span>uv</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如需更新：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>uv</span><span> </span><span>self</span><span> </span><span>update</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/09/20250912200457001.png" alt="安装成功！" /><figcaption>安装成功！</figcaption></figure><p></p></section><section><h3>3.2 Use<a href="#32-use"><span>#</span></a></h3><p>常用指令：</p><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/09/20250912204916225.png" alt="uv help" /><figcaption>uv help</figcaption></figure><p></p><p>以搭建一个 Python 项目环境为例：</p><ol>
<li>
<p><code>uv python install</code> 安装 Python （系统已安装跳过即可）；</p>
</li>
<li>
<p><code>uv python list</code> 查看已安装的 Python 版本；</p>
</li>
</ol><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/09/20250912205353507.png" alt="Python List" /><figcaption>Python List</figcaption></figure><p></p><ol>
<li><code>uv init</code> 在新项目的根目录下执行，初始化新项目；</li>
</ol><p>可以观察到其贴心的给你生成了以下文件，并自动使用 git 进行版本管理：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.</span></div></div><div><div><div>2</div></div><div><span>├── .git</span></div></div><div><div><div>3</div></div><div><span>├── .gitignore</span></div></div><div><div><div>4</div></div><div><span>├── .python-version</span></div></div><div><div><div>5</div></div><div><span>├── main.py</span></div></div><div><div><div>6</div></div><div><span>├── pyproject.toml</span></div></div><div><div><div>7</div></div><div><span>└── README.md</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>创建虚拟环境；</li>
</ol><p>你可以直接 <code>uv run main.py</code> 执行示例文件，uv 会自动为你创建虚拟环境：</p><p></p><figure><img src="https://webp-pic.yokumi.cn/2025/09/20250912211745661.png" alt="uv run" /><figcaption>uv run</figcaption></figure><p></p><p>通过 <code>uv venv --python=3.10</code> 手动创建并指定版本当然也没问题，然后激活 <code>source .venv/bin/activate</code> 一下。</p><ol>
<li>安装依赖；</li>
</ol><p>直接看下文的速查表吧。</p></section><section><h3>3.3 速查表<a href="#33-速查表"><span>#</span></a></h3><section><h4>3.3.1 环境管理<a href="#331-环境管理"><span>#</span></a></h4>

<table><thead><tr><th>操作</th><th>UV 命令</th></tr></thead><tbody><tr><td>创建虚拟环境</td><td><code>uv venv</code></td></tr><tr><td>创建指定 Python 版本的虚拟环境</td><td><code>uv venv --python=3.12</code></td></tr><tr><td>激活虚拟环境</td><td><code>source .venv/bin/activate</code></td></tr></tbody></table></section><section><h4>3.3.2 包管理<a href="#332-包管理"><span>#</span></a></h4>

<table><thead><tr><th>操作</th><th>UV 命令</th></tr></thead><tbody><tr><td>安装单个包</td><td><code>uv pip install pandas</code></td></tr><tr><td>从 requirements.txt 安装</td><td><code>uv pip install -r requirements.txt</code></td></tr><tr><td>安装当前项目</td><td><code>uv pip install -e .</code></td></tr><tr><td>安装开发依赖</td><td><code>uv pip install -e ".[dev]"</code></td></tr><tr><td>生成锁文件</td><td><code>uv pip compile requirements.in -o requirements.txt</code></td></tr><tr><td>升级单个包</td><td><code>uv pip install --upgrade pandas</code></td></tr></tbody></table></section><section><h4>3.3.3 项目管理<a href="#333-项目管理"><span>#</span></a></h4>

<table><thead><tr><th>操作</th><th>UV 命令</th></tr></thead><tbody><tr><td>添加依赖</td><td><code>uv add pandas</code></td></tr><tr><td>移除依赖</td><td><code>uv remove pandas</code></td></tr><tr><td>同步环境</td><td><code>uv sync</code></td></tr><tr><td>升级特定包</td><td><code>uv sync --upgrade-package pandas</code></td></tr><tr><td>升级所有包</td><td><code>uv lock --upgrade</code></td></tr><tr><td>运行脚本</td><td><code>uv run python main.py</code></td></tr><tr><td>构建项目</td><td><code>uv build</code></td></tr><tr><td>查看项目依赖关系树</td><td><code>uv tree</code></td></tr></tbody></table><p>其中，管理依赖可以通过 <code>group</code> 参数来区分开发和生产环境，例如：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>uv</span><span> </span><span>add</span><span> </span><span>--group</span><span> </span><span>dev</span><span> </span><span>pandas</span></div></div><div><div><div>2</div></div><div><span>uv</span><span> </span><span>add</span><span> </span><span>--group</span><span> </span><span>production</span><span> </span><span>requests</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>3.3.4 工具管理<a href="#334-工具管理"><span>#</span></a></h4><p><code>uvx</code>，即 <code>uv tool run</code> 的简写，顾名思义是用于调用 Python 工具，是 <code>pipx</code> 的替代品。</p></section></section></section>
<section><h2>4. 参考资料<a href="#4-参考资料"><span>#</span></a></h2></section>
<section><h2>Footnotes<a href="#footnote-label"><span>#</span></a></h2>
<ol>
<li>
<p>uv 官方文档, <a href="https://docs.astral.sh/uv/" target="_blank">https://docs.astral.sh/uv/</a> <a href="#user-content-fnref-1">↩</a></p>
</li>
<li>
<p>UV：下一代Python包管理工具的全面指南, <a href="https://apframework.com/blog/essay/2025-06-07-Python-UV" target="_blank">https://apframework.com/blog/essay/2025-06-07-Python-UV</a> <a href="#user-content-fnref-2">↩</a></p>
</li>
</ol>
</section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/hikasa-sakura-talk-live-vol-2-5-personal-repo/</id>
      <title type="text">日笠佐仓闲话免谈 talk live vol. 2.5 个人 repo</title>
      <published>2025-08-23T12:49:00.000Z</published>
      <updated>2025-08-23T12:49:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/hikasa-sakura-talk-live-vol-2-5-personal-repo/"/>
      <summary type="text">第一次接触到“佐仓绫音”这个名字应该源起至《Charlotte》，大法配的友利奈绪，当时还在可惜为什么不做成季度番，看完意难平遂补了声优广播节目（今年也是夏洛特广播十周年🥹，本人没抢到隔壁 PPP 的票，不过感觉看完昼场的樱已经心满意足了，b 站还刷到夜场有人当场把 afterglow 法批换成 ppp 去赶场然后…</summary>
      <content type="html"><![CDATA[<p>第一次接触到“佐仓绫音”这个名字应该源起至《Charlotte》，大法配的友利奈绪，当时还在可惜为什么不做成季度番，看完意难平遂补了声优广播节目（今年也是夏洛特广播十周年🥹，本人没抢到隔壁 PPP 的票，不过感觉看完昼场的樱已经心满意足了，b 站还刷到夜场有人当场把 afterglow 法批换成 ppp 去赶场然后被樱出警的，也是很有效果了），没想到成了罪恶的 dd nsy 的开始（笑）。想当年还是内山和松冈五五开，不知道 25 年了还有人看内山樱吗hhhhh。</p>
<p>写一点 repo，看的时候太兴奋了以至于看完已经快大脑空空了，昼场位置绝佳，樱打招呼以及展示周边和打太极环节真的就在眼前几米，后悔没填线夜场了。</p>
<p>先放点群友的花篮图，太好看了。</p>
<p><img src="https://webp-pic.yokumi.cn/2025/08/20250824212431349.JPG" alt="" />
<img src="https://webp-pic.yokumi.cn/2025/08/20250824212438607.JPG" alt="" />
<img src="https://webp-pic.yokumi.cn/2025/08/20250824212444210.JPG" alt="" /></p>
<p>开场稍微晚了几分钟（中途还有迟到的观众结果被两人点名还说欢迎光临的，这下羡慕迟到了），不过两人最后聊到 14&lt;15&gt; 左右才结束，聊嗨了属于是，现场观众也很给力和有活，乐得不行，谢谢两位和群友们，感觉很久没笑这么开心了。</p>
<p>开场两人身着汉服出场（后来读信环节，hrk解释了一下是魏晋时期的），美得无敌，<a href="https://weibo.com/u/7881190063" target="_blank">主办方微博</a>和<a href="https://x.com/aoni_official/status/1959256076274258225" target="_blank">青二</a>都有返图。</p>
<p></p><figure><img src="https://webp-pic.yokumi.cn/2025/08/20250824213441034.png" alt="青二的返图" /><figcaption>青二的返图</figcaption></figure><p></p>
<p>仔细观察一下发饰和演出的时候不太一样，另外好像这个窗户外面就是警察局，聊天的时候还提了一下hhhh。</p>
<p>然后是拷打观众日语水平，得知现场人均 N1/N2 水平后就放开聊了，hrk 最清闲的一集，有时看她想说话但是说到一半完全插不上嘴，两人高强度输出，还问了有没有霓虹金来看，我看一层大概有六七个举手的。</p>
<p>阳子太太比阿樱早到沪国，主办方陪同吃了一顿，阳子大胃袋说是，学会了一句“鱼好吃”的中文。</p>
<p>佐仓由于档期安排，凌晨 3 点多才落地浦东，佐餐落地后吃的第一餐是全家的荔枝三明治（一开始没理解，以为是两个东西）和罗森的酱油猪肉饭团：</p>
<p></p><figure><img src="https://webp-pic.yokumi.cn/2025/08/20250824224416687.png" alt="佐餐同款饭团说是" /><figcaption>佐餐同款饭团说是</figcaption></figure><p></p>
<p>二遍：此事在之后发的 ins 上亦有记载。全家买的，还买了一堆小零食。好像今天一点多的飞机回日本，工作狂魔了属于是，希望阿樱注意身体。</p>
<p>另外还提到了看到包装上的热量吓了一跳（日本一般是大卡，国内是千焦，差了 4 倍）。</p>
<p>接下来到了喜闻乐见的观众周边展示环节，太有活了，看到一个穿着“我的日语全是从女声优学的”的衣服的（典），以及邦的各种周边和娃娃（佐仓还q了一句好多娃娃自己都没见到过），穿法批的，一排还有一个拿着自制的“我是佐仓绫音的狗的”团扇的，还有一个一排粉毛群友被佐仓说像佐久间大介君的，等等，毕竟大法配的角色太多了。</p>
<p>感觉献宝环节持续了得有几十分钟，观众都非常给力。哦对，有一个一排拿望远镜的老哥被佐仓问是不是来看她们的毛孔的了。</p>
<p>来信环节，有打鼓求建议的，也有社畜“感觉自己成了工作机器”求鼓励的。对于打鼓，两位给出的建议竟是先组个乐队，然后就开始统计观众的乐器水平，最后得出结论是现场的观众能凑 20 支乐队，不过至于乐队间的成员关系…此事在邦多利中亦有记载（我看着hrk也是个顶级串子）。另一个给出的回答是一个日本搞笑艺人的梗，这边真不懂，然后问大家都看谁，结果全是 nsyc 平常没空看别的，我喊了个马池口还被听见了，佐仓：马池口是 nsy 不是搞笑艺人！（并非不是，笑），甚至还有拷打翻译环节，不过阳子高情商圆场hhhh。</p>
<p>还有就是推荐马面裙的，佐仓说想买阿迪达斯的一件盘扣来着，结果真有观众拿出来了（押题是真的充分，笑）。</p>
<p>还有一位女粉写了想看两位跳舞，结果最后两位直接开始打太极，自己打了一遍（阿樱完全在抄阳子hhhh） + 看教程打了一遍，有模有样，对的对的。</p>
<p>答题环节，第一个问题比较典，老婆在日文里是老婆婆的意思，中文里是什么意思，佐仓拔得头筹，原因是之前有一个人在 ins 用中文给樱发私信说“可爱”“喜欢你”，后面突然变成了“老婆”，还以为是粉转黑了，后来去查了中文以为是“好看的小姐姐”类似的意思，翻译提醒后才知道。</p>
<p>原来樱真的会看私信啊！这下不得不发了。</p>
<p>还有个问题是猜“水泥“对应的日语单词，阳子在观众的提醒下猜对了。</p>
<p>后面还有小色狼环节，就是我前面提到的要放开聊之前，提到了旁边就是公安局，吓得佐仓阳子马上终止话题：别搞的我们回不去日本了（乐）。不过夜场好像放的更开了。</p>
<p>最后是一个奶茶排序，分别是喜茶（椰椰芒芒）、沪上阿姨、霸王茶姬和蜜雪冰城（珍珠奶茶），喜茶甚至姗姗来迟，两位觉得每一杯都很好喝，特别是后来合影环节，staff 来收奶茶时佐仓还在护食（可爱捏），霸王茶姬樱觉得茶味有点重。盲猜点的都是全糖hhhh。</p>
<p>佐仓一下就排出了喜茶和雪王是最贵的和最便宜，中间两杯在反复试探观众后也排对了，不愧是专业的不喝水人士。</p>
<p>输了的阳子太太的惩罚游戏是要用中文模仿一句当下很火的中文流行梗，昼场是“你是谁？！请支持 XXX”（不过我寻思这不是小高和刚的吗，严格来说不算中文梗？）。</p>
<p>你是谁？！请支持 YODAN！</p>
<p>合影，散场感言。两位向观众席告别了好久。</p>
<p>活动评价的话，比一般 nsy fmt 强太多了，得益于台本的缘故，而且两位是真的能说且有梗，去之前还担心翻译会不会打断对谈，去完发现完全不坐牢，虽然有些是后知后觉，感觉总体来说完全不懂日语都能连蒙带猜听懂大半，就像阳子在来之前说的：”大家看到我们说不定就很幸福了“。</p>
<p>我永远不会忘记823了，目前为止声优 event 的顶点！当一辈子的佐佐人。</p>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/modular-development-for-large-scale-projects-based-on-python/</id>
      <title type="text">浅谈大型项目的模块化开发(Based on Python)</title>
      <published>2025-08-05T09:12:00.000Z</published>
      <updated>2025-08-05T09:12:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/modular-development-for-large-scale-projects-based-on-python/"/>
      <summary type="text">最近在进行大型项目开发时，自行实现的一些公共模块，包括工具库、API 客户端等，都被扔在一个文件夹下，需要时由该项目的控制代码进行调用，不过因为水平之菜，经过测试没什么问题的代码 PR 上去之后经常会出现需要反复修改的情况。而且另一方面，这些公共模块具有一定的可移植性，但只被一个项目使用，没什么性价比，肯定是需要进一…</summary>
      <content type="html"><![CDATA[<section><h1>写在前面<a href="#写在前面"><span>#</span></a></h1><p>最近在进行大型项目开发时，自行实现的一些公共模块，包括工具库、API 客户端等，都被扔在一个文件夹下，需要时由该项目的控制代码进行调用，不过因为水平之菜，经过测试没什么问题的代码 PR 上去之后经常会出现需要反复修改的情况。而且另一方面，这些公共模块具有一定的可移植性，但只被一个项目使用，没什么性价比，肯定是需要进一步考虑能够在多个项目实现复用的。</p><p>一种好的管理方式（至少是大部分大型开源项目都在使用的方式）是<strong>将公共模块单独管理，模块可以独立更新，不必和主项目的提交历史绑在一起</strong>，也利好多人协作开发。具体方法包括 Git 的 <strong>Submodule</strong> 和打包为 <strong>Python Package</strong> 进行导入两种。</p></section>
<section><h1>一、Git Submodule<a href="#一git-submodule"><span>#</span></a></h1><section><h2>1.1 简介<a href="#11-简介"><span>#</span></a></h2><p>Git submodule 是 git 原生引用和管理第三方资源的方法。</p><p>假如你的项目结构如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Main Project</span></div></div><div><div><div>2</div></div><div><span>└── src/</span></div></div><div><div><div>3</div></div><div><span>└── module/</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>└── module_1/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果用 Git submodule 实现对 <code>module_1</code> 的管理，那么主项目 Git 仓库显然作为<strong>父仓库 (Superproject)</strong>，而 <code>module_1</code> 就作为<strong>子模块 (Submodule)</strong>，父仓库只记录子模块的 <strong>Git 仓库地址</strong> 和当前的 <strong>提交哈希</strong>，不会直接存储子模块的代码历史。</p></section><section><h2>1.2 操作步骤<a href="#12-操作步骤"><span>#</span></a></h2><p>食用方法也很简单，子模块和父模块都先建立一个单独的 Git 仓库，<code>git add/commit/push</code> 这些我就不细说了，现在假设两者的仓库都已经建立了，接下来就是 <code>git submodule</code> 的操作了：</p><ol>
<li><strong>在父仓库中添加子模块</strong>：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>add</span><span> </span><span>&lt;子模块的仓库地址&gt;</span><span> </span><span>&lt;子模块在父模块中的相对路径&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>e.g.</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>add</span><span> </span><span>https://github.com/user/module_1.git</span><span> </span><span>module/module_1</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 等价于</span></div></div><div><div><div>4</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>add</span><span> </span><span>https://github.com/user/module_1.git</span><span> </span><span>module/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>那么父仓库具体是如何记录这一信息的呢？在执行上述操作后，可以看到在父仓库根目录会生成一个 <code>.gitmodules</code> 文件，内容形如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[submodule "module/module_1"]</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>path = module/module_1</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>url = https://github.com/user/module_1.git</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也可以通过以下命令查看子模块信息：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>status</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>foreach</span><span> </span><span>"git status"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>初始化子模块</strong>：当协作者或其他用户克隆你的父仓库，即主项目时，默认子模块的目录是空的，需要手动拉取子模块代码：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>init</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>update</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当然，Git 也提供了在克隆时就一步到位的选项：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>--recurse-submodules</span><span> </span><span>&lt;父仓库地址&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>更新子模块</strong>：当子模块仓库有新提交时，你可以进入子模块目录拉取最新代码（当然，回退版本也同理）：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>module/module_1</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>pull</span><span> </span><span>origin</span><span> </span><span>main</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 如果是回退，则执行</span></div></div><div><div><div>5</div></div><div><span>git</span><span> </span><span>checkout</span><span> </span><span>&lt;子模块需要回退到的某个commit或分支&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们前面提到过，两者的提交历史是独立的，父仓库只记录子模块的当前提交哈希，子模块仓库的更新并不会同步到父仓库中，所以父仓库还需要手动提交：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>../..</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>add</span><span> </span><span>module/module_1</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>"Update submodule module_1 to latest version"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这时候，父仓库记录的才是子模块的最新提交哈希。</p><ol>
<li><strong>删除子模块</strong>：删除步骤稍复杂，需要手动清理：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>deinit</span><span> </span><span>-f</span><span> </span><span>module/module_1</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>rm</span><span> </span><span>-f</span><span> </span><span>module/module_1</span></div></div><div><div><div>3</div></div><div><span>rm</span><span> </span><span>-rf</span><span> </span><span>.git/modules/module/module_1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>1.3 好处<a href="#13-好处"><span>#</span></a></h2><p>Git Submodule 将公共库抽离成单独 Git 仓库，父项目通过 Submodule 管理这些模块，保持版本可控。如需自动拉取子模块代码，也可以在 Github Action 等 CI/CD 工具中执行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>submodule</span><span> </span><span>update</span><span> </span><span>--init</span><span> </span><span>--recursive</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>从而自动进行构建。</p></section></section>
<section><h1>二、Python Package<a href="#二python-package"><span>#</span></a></h1><section><h2>2.1 简介<a href="#21-简介"><span>#</span></a></h2><p>Python Package 也是一种常见方式，经常会用到的 Numpy 等就是 Package。这些第三方库能直接通过 <code>pip</code> 安装，分发、复用于各种项目中，虽然你用起来大道至简，但是这是 Package 的维护者在背后为你负重前行，也就是“<strong>打包</strong>”的过程，具体的打包过程可以从内到外分为三层，下图源自 Python Packaging 的官方文档，非常直观：</p><p><img src="https://webp-pic.yokumi.cn/2025/08/20250805185554914.png" alt="" /></p><ol>
<li><strong>.py 独立模块（Standalone Modules）</strong>：可以直接导入，但无法通过 pip install 方式分发；</li>
<li><strong>sdist 源码分发包（Source Distribution Package）</strong>：通过 <code>setup.py</code>、<code>pyproject.toml</code> 等安装元数据，打包成压缩文件（.tar.gz、.zip）；</li>
<li><strong>wheel 预编译包（Built Distribution Package）</strong>：属于 <strong>binary package</strong>（二进制分发），简单来说就是<strong>预编译好的轮子</strong>，可以直接安装，例如 <code>pip install my_package.whl</code> ；</li>
</ol></section><section><h2>2.2 操作步骤<a href="#22-操作步骤"><span>#</span></a></h2><p>以下以一个简单的例子为例，简单过一下打包步骤：</p><p>假如原始的代码结构如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>my_package/</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>└── src/</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>└── module_a/</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>├── __init__.py</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>└── module_a.py</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>└── module_b/</span></div></div><div><div><div>7</div></div><div><span><span>      </span></span><span>├── __init__.py</span></div></div><div><div><div>8</div></div><div><span><span>          </span></span><span>└── module_b.py</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>准备项目信息</strong>：如果 Package 想要发布，还需要添加一些基本的信息，一般通过 <code>setup.py</code> 或 <code>pyproject.toml</code> ；</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>my_project/</span></div></div><div><div><div>2</div></div><div><span>├── LICENSE   # 授权</span></div></div><div><div><div>3</div></div><div><span>├── setup.py   # 安装设定</span></div></div><div><div><div>4</div></div><div><span>├── pyproject.toml   # 安装设定</span></div></div><div><div><div>5</div></div><div><span>├── README.md</span></div></div><div><div><div>6</div></div><div><span>├── src/   # 包源码</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>└── my_package/</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>├── __init__.py</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>└── ...</span></div></div><div><div><div>10</div></div><div><span>└── test/   # 测试程序</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>编写元数据</strong>：一个基本的 <code>setup.py</code> 和 <code>pyproject.toml</code> 示例如下：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>from</span><span> setuptools </span><span>import</span><span> setup, find_packages</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>setup</span><span>(</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>name</span><span>=</span><span>"my_package"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>version</span><span>=</span><span>"0.1.0"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>description</span><span>=</span><span>"A useful Python package"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>author</span><span>=</span><span>"Your Name"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>author_email</span><span>=</span><span>"you@example.com"</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>packages</span><span><span>=</span><span>find_packages</span><span>(</span></span><span>where</span><span>=</span><span>"src"</span><span>),</span></div></div><div><div><div>10</div></div><div><span>    </span><span>package_dir</span><span><span>=</span><span>{</span></span><span>""</span><span>: </span><span>"src"</span><span>},</span></div></div><div><div><div>11</div></div><div><span>    </span><span>install_requires</span><span><span>=</span><span>[</span></span></div></div><div><div><div>12</div></div><div><span>        </span><span>"numpy&gt;=1.19.0"</span><span>,</span></div></div><div><div><div>13</div></div><div><span>        </span><span>"requests"</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>],</span></div></div><div><div><div>15</div></div><div><span>    </span><span>python_requires</span><span>=</span><span>'&gt;=3.7'</span><span>,</span></div></div><div><div><div>16</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[</span><span>build-system</span><span>]</span></div></div><div><div><div>2</div></div><div><span>requires</span><span> = [</span><span>"setuptools&gt;=61.0"</span><span>]</span></div></div><div><div><div>3</div></div><div><span>build-backend</span><span> = </span><span>"setuptools.build_meta"</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>[</span><span>project</span><span>]</span></div></div><div><div><div>6</div></div><div><span>name</span><span> = </span><span>"my_package"</span></div></div><div><div><div>7</div></div><div><span>version</span><span> = </span><span>"0.1.0"</span></div></div><div><div><div>8</div></div><div><span>authors</span><span> = [</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>{ </span><span>name</span><span>=</span><span>"Your Name"</span><span>, </span><span>email</span><span>=</span><span>"you@example.com"</span><span> },</span></div></div><div><div><div>10</div></div><div><span>]</span></div></div><div><div><div>11</div></div><div><span>description</span><span> = </span><span>"A useful Python package"</span></div></div><div><div><div>12</div></div><div><span>readme</span><span> = </span><span>"README.md"</span></div></div><div><div><div>13</div></div><div><span>requires-python</span><span> = </span><span>"&gt;=3.7"</span></div></div><div><div><div>14</div></div><div><span>classifiers</span><span> = [</span></div></div><div><div><div>15</div></div><div><span>    </span><span>"Programming Language :: Python :: 3"</span><span>,</span></div></div><div><div><div>16</div></div><div><span>    </span><span>"License :: OSI Approved :: MIT License"</span><span>,</span></div></div><div><div><div>17</div></div><div><span>    </span><span>"Operating System :: OS Independent"</span><span>,</span></div></div><div><div><div>18</div></div><div><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>基本要素是类似的，也可以混用，现代打包一般采用后者，当然也有很多其他工具，这两种属于是最主流的了。</p><ol>
<li><strong>构建包</strong>：这一步是可选的，但是一般如果想要发布都会进行 build，提供更快速的安装；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>python</span><span> </span><span>-m</span><span> </span><span>build</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.3 发布到 PyPI<a href="#23-发布到-pypi"><span>#</span></a></h2><p><a href="https://pypi.org/" target="_blank">PyPI</a>，即 Python Package Index，是 Python 官方的第三方软件存储库，账号注册就不赘述了，下面简单讲一下上传步骤：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>pip install twine</span></div></div><div><div><div>2</div></div><div><span>twine upload dist/*</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上传成功后，别人就可以直接使用 <code>pip install my_package</code> 来安装你发布的 Package 了。</p><p>很多 Package 一般都自动构建并上传，你可以随便找一个开源项目并在里面看到：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.github/</span></div></div><div><div><div>2</div></div><div><span>└── workflows/</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>└── publish.yml</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>完全可以学学它们的 <code>publish.yml</code> 以及项目结构，这里给出一个简单的示例：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>name</span><span>: </span><span>Publish Python Package</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>on</span><span>:</span></div></div><div><div><div>4</div></div><div><span>  </span><span>push</span><span>:</span></div></div><div><div><div>5</div></div><div><span>    </span><span>tags</span><span>:</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>- </span><span>'v*'</span><span>   </span><span># 当你创建 v1.0.0 这种 tag 时自动触发</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>jobs</span><span>:</span></div></div><div><div><div>9</div></div><div><span>  </span><span>build-and-publish</span><span>:</span></div></div><div><div><div>10</div></div><div><span>    </span><span>runs-on</span><span>: </span><span>ubuntu-latest</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>steps</span><span>:</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Checkout code</span></div></div><div><div><div>14</div></div><div><span>      </span><span>uses</span><span>: </span><span>actions/checkout@v4</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Set up Python</span></div></div><div><div><div>17</div></div><div><span>      </span><span>uses</span><span>: </span><span>actions/setup-python@v5</span></div></div><div><div><div>18</div></div><div><span>      </span><span>with</span><span>:</span></div></div><div><div><div>19</div></div><div><span>        </span><span>python-version</span><span>: </span><span>'3.11'</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Install build dependencies</span></div></div><div><div><div>22</div></div><div><span>      </span><span>run</span><span>: </span><span>|</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>python -m pip install --upgrade pip</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>pip install build twine</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Build package</span></div></div><div><div><div>27</div></div><div><span>      </span><span>run</span><span>: </span><span>python -m build</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Publish to PyPI</span></div></div><div><div><div>30</div></div><div><span>      </span><span>env</span><span>:</span></div></div><div><div><div>31</div></div><div><span>        </span><span>TWINE_USERNAME</span><span>: </span><span>${{ secrets.PYPI_USERNAME }}</span></div></div><div><div><div>32</div></div><div><span>        </span><span>TWINE_PASSWORD</span><span>: </span><span>${{ secrets.PYPI_PASSWORD }}</span></div></div><div><div><div>33</div></div><div><span>      </span><span>run</span><span>: </span><span>|</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>twine upload dist/*</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>Token 需要你自行在 PyPI 中获取并在 Github 中的项目仓库 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> Settings <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> Secrets and variables <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> Actions <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 点击 New repository secret ，创建 <code>PYPI_USERNAME</code> 和 <code>PYPI_PASSWORD</code>，<strong>注意千万不要显式写在工作流中</strong>！</p><p>完成后，当提交代码并推送 Tag 时：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>tag</span><span> </span><span>v1.0.0</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>origin</span><span> </span><span>v1.0.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>GitHub Actions 会自动运行 workflow，构建 wheel 和 sdist 包，并上传到 PyPI。</p><p>也可以替换为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>on</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>release</span><span>:</span></div></div><div><div><div>3</div></div><div><span>    </span><span>types</span><span>: [</span><span>published</span><span>]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样就是在发布 Release 后自动触发。</p></section></section>
<section><h1>参考资料<a href="#参考资料"><span>#</span></a></h1></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/build-your-image-hosting-with-cloudflare-r2-webp-cloud-picgo/</id>
      <title type="text">使用 Cloudflare R2 + WebP Cloud + PicGo 搭建你的图床</title>
      <published>2025-07-29T06:20:00.000Z</published>
      <updated>2025-07-29T06:20:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/build-your-image-hosting-with-cloudflare-r2-webp-cloud-picgo/"/>
      <summary type="text">一开始使用了 jsDelivr 加速 Github 图床，但是实际使用中发现有时国内到 jsDelivr 的网络质量仍不稳定，或是存在 DNS 污染，并且使用 Github 作为图床容易污染 commit 记录。于是打算放弃这种依赖纯 CDN 的方式，转向 OSS ，加上刚好有优化博客访问的需求，以及赛博活菩萨 Cl…</summary>
      <content type="html"><![CDATA[<section><h1>写在前面<a href="#写在前面"><span>#</span></a></h1><p>一开始使用了 jsDelivr 加速 Github 图床，但是实际使用中发现有时国内到 jsDelivr 的网络质量仍不稳定，或是存在 DNS 污染，并且使用 Github 作为图床容易污染 commit 记录。于是打算放弃这种依赖纯 CDN 的方式，转向 OSS ，加上刚好有优化博客访问的需求，以及赛博活菩萨 Cloudflare 提供的 10GB/月的免费对象存储服务，最终参考了几篇教程，冻手！</p></section>
<section><h1>一、Cloudflare R2 有关配置<a href="#一cloudflare-r2-有关配置"><span>#</span></a></h1><section><h2>1.1 账号注册相关<a href="#11-账号注册相关"><span>#</span></a></h2><p>Cloudflare 的<a href="https://dash.cloudflare.com/" target="_blank">账号注册</a>我就不多说了，如果要开启 R2 服务，在注册完毕后移步<strong>左侧栏的“R2 对象存储”</strong>，需要有信用卡或者通过 Palpal（支持国内信用卡/借记卡），Cloudflare 对国内是非常友好的，支持银联和国内手机号，所以这一步基本没什么难度。只要不超出限额就不会扣费，免费容量个人使用的话狠狠造一般都超不了。</p></section><section><h2>1.2 创建 Bucket<a href="#12-创建-bucket"><span>#</span></a></h2><ol>
<li>点击“+创建 Bucket”，设置有关内容，区域根据实际需求选择，这里与之后的 WebP Cloud 保持一致，均选择美西节点。注意实际位置不一定完全保证 Cloudflare 就把该桶分配到该区域。</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204551394.png" alt="" /></p><ol>
<li>创建成功；</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204523945.png" alt="" /></p><p>现在可以直接往里面上传文件了，对于写作博客这种需求，不可能每次手动打开在线页面往里面扔东西，这里通过继续配置 <a href="https://developers.cloudflare.com/r2/api/s3/api/" target="_blank">S3 API</a> ，使之支持通过 PicGo 工具自动上传至 Bucket（见后文）。</p></section><section><h2>1.3 配置域名<a href="#13-配置域名"><span>#</span></a></h2><ol>
<li>首先，你需要启用 Bucket 的公共开发 URL，启用后你上传的图片就有公网链接了，设置 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 公共开发 URL <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 点击“启用” <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 键入“allow”以确认；</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204332454.png" alt="" /></p><ol>
<li>启用成功后会显示一个 <code>r2.dev</code> 结尾的公网地址，至于速率限制，如果你有自定义域名则不必担心；</li>
</ol></section><section><h2>1.4 自定义域名（可选）<a href="#14-自定义域名可选"><span>#</span></a></h2><p>它提供的公网地址太长且杂乱无章不方便记忆，如果你有自己的域名也可以在设置 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 自定义域 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 点击“添加” <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 输入你的自定义域名 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 等待 DNS 解析完成。</p><p>举个例子，比如你的域名是 <code>xiaoming.com</code> ，那么自定义域名可以设置为其下的如 <code>test-r2.xiaoming.com</code> 。</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204314317.png" alt="" /></p><p>等待一段时间后就能生效了，最终的效果应该如下，即公开访问已启用并且公网地址为你的自定义域：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204256753.png" alt="" /></p></section><section><h2>1.5 配置 S3 API<a href="#15-配置-s3-api"><span>#</span></a></h2><p>R2 提供的 S3 API 为客户端工具提供了方便的上传/删除操作，配置步骤如下：</p><ol>
<li>回到概述页面，点击“{}API” <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 管理 API 令牌；</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204229508.png" alt="" /></p><ol>
<li>创建 API 令牌，注意这里选择 Account API 或者 User API 都可以，没有很大区别；</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204208999.png" alt="" /></p><ol>
<li>输入名称，权限选择“<strong>对象读和写</strong>”，指定你刚才创建的存储桶，保证数据安全性；</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204056382.png" alt="" /></p><ol>
<li>创建后会显示令牌凭据，出于安全原因，这些令牌值不会再次显示，<strong>务必做好保存，关闭该页面后无法再查看只能重新创建！</strong></li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204037349.png" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204015805.png" alt="" /></p></section></section>
<section><h1>二、PicGo 客户端配置<a href="#二picgo-客户端配置"><span>#</span></a></h1><section><h2>2.1 插件安装<a href="#21-插件安装"><span>#</span></a></h2><p>PicGo 本身不支持 S3 对象存储图床，这里通过 <a href="https://github.com/wayjam/picgo-plugin-s3" target="_blank">PicGo Amazon S3 上传插件</a>解决，直接在 GUI 界面搜索“S3”即可，<strong>如果你遇到搜索插件不显示的情况，请参考<a href="https://github.com/Molunerfinn/PicGo/issues/1325" target="_blank">ISSUE</a></strong>。</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729203947405.png" alt="" /></p><p>如果你安装 s3 没有成功，可以考虑安装 s3-lls ，笔者也没有安装成功，所以以下使用 s3-lls 进行演示，区别是不大的。</p></section><section><h2>2.2 插件配置<a href="#22-插件配置"><span>#</span></a></h2><p>安装成功后，进入“图床设置”，会发现新增了 Amazon S3 选项，点击进入配置：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729203926668.png" alt="" /></p><p>逐个填入内容，对应关系如下：</p><ul>
<li>应用密钥 ID <span><span>↔\leftrightarrow</span><span><span><span></span><span>↔</span></span></span></span> 访问密钥 ID</li>
<li>应用密钥 <span><span>↔\leftrightarrow</span><span><span><span></span><span>↔</span></span></span></span> 机密访问密钥</li>
<li>自定义节点 <span><span>↔\leftrightarrow</span><span><span><span></span><span>↔</span></span></span></span> 为 S3 客户端使用管辖权地特定的终结点</li>
<li>桶 <span><span>↔\leftrightarrow</span><span><span><span></span><span>↔</span></span></span></span> 桶名</li>
<li>文件路径随你自己喜好，我使用的是：<code>{year}/{month}/{fileName}.{extName}</code></li>
<li>自定义域名：Cloudflare 默认分配的或者你的自定义域名</li>
<li>权限：<code>private</code> ；</li>
</ul><p>其余不填或保持默认即可。</p><p>配置完成后，你可以随便上传一张图片测试一下。也可以从 Cloudflare 后台验证上传结果：，顺便验证文件的存储路径是否符合自己的要求，如果你和我一样设置，那么图片的链接应该是：<code>https://自定义域名/2025/07/文件名.png</code> 。</p><div><div><div></div><div>Warning</div></div><div><p><strong>注</strong>：虽然 PicGo 有将图片从相册中删除的功能，但事实上并不会对 Cloudflare 图床中存储的内容进行实际删除。</p></div></div></section><section><h2>2.3 安装/配置/上传 FAQ<a href="#23-安装配置上传-faq"><span>#</span></a></h2><p>笔者在上述过程中事实上也遇到了很多问题，不过最终还是成功解决了。</p><ol>
<li><strong>安装问题</strong>：包括 PicGo 下载已损坏，插件列表搜索无结果，插件安装不成功等。</li>
</ol><p>个人感觉问题主要集中在 Mac 且为苹果系列的 CPU 上。具体请查阅 PicGo 官方的  <a href="https://github.com/Molunerfinn/PicGo/blob/dev/FAQ.md" target="_blank">FAQ</a> 以及被关闭的 <a href="https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed" target="_blank">issues</a>。</p><ol>
<li><strong>插件配置问题</strong>：picgo-plugin-s3 的作者提供了详细的<a href="https://github.com/wayjam/picgo-plugin-s3/issues/28" target="_blank">配置示例</a>；</li>
<li><strong>图片上传问题</strong>：包括图片上传不成功，路径错误等；</li>
</ol><p>笔者在这部分遇到的问题主要是 Nodejs 的一些库的版本问题，升级至对应版本即可解决。并且遇到的问题在 ISSUE 里也没有，所以此处不给出统一解答。如遇上传失败，请截图日志文件，尝试自己解决或者善用 AI 工具，也可以在<a href="https://github.com/wayjam/picgo-plugin-s3/issues" target="_blank">ISSUE</a>查找是否有类似问题。我如果看到评论区留言也会及时答复。</p></section></section>
<section><h1>三、WebP Cloud 配置（可选）<a href="#三webp-cloud-配置可选"><span>#</span></a></h1><section><h2>3.1 WebP Cloud 介绍<a href="#31-webp-cloud-介绍"><span>#</span></a></h2><p><a href="https://webp.se/" target="_blank">WebP Cloud</a>简单来说这是一个类 CDN 的图片代理 SaaS 服务，可以把图片转换为 WebP/AVIF 格式，减小图像体积，加快网页中图片的加载速度。</p><ol>
<li>你的网站不直接提供原图 URL，而是把 URL 交给 WebP Cloud；</li>
<li>WebP Cloud 服务器代理请求原图 → 转换为 WebP/AVIF → 进行缓存；</li>
<li>用户访问时从其分布式节点快速获得优化后的图片；</li>
<li>类似 CDN，但专注于图片优化分发，而非全站加速；</li>
</ol><p>首先通过 Github 授权登陆该平台，这里就不演示了。其中，免费版本每日提供 3000 个请求额度（相当于能够代理 3000 次图片访问请求），<strong>每日签到还有额外额度领</strong>！并提供 200M 的图片缓存，这对个人用户完全够用了，而且升级订阅也不算太贵，可以说非常良心了。</p><p>如果超出了请求额度也不用担心，访客对博客中图片的访问会被 301 转发到源站图片地址（即你图片的 Cloudflare 公网地址），不经 WebP Cloud 服务压缩，但依然可用；</p><p>如果超过 200M 的缓存则会按照 LRU 算法清理。所以不必担心。</p><p>具体而言，你可以根据 WebP Cloud 仪表盘的每日统计，选择合适自己的订阅。</p></section><section><h2>3.2 创建代理<a href="#32-创建代理"><span>#</span></a></h2><p>在主页找到并点击“创建代理”：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729205423107.png" alt="" /></p></section><section><h2>3.3 配置代理<a href="#33-配置代理"><span>#</span></a></h2><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729205457666.png" alt="" /></p><ul>
<li>代理区域：和 Cloudflare 统一，这里选择美西节点；</li>
<li>代理名称：随便取；</li>
<li>源站地址：与你的 Cloudflare 的自定义域名保持一致；</li>
</ul><p>创建完成后，点击刚才创建的内容，进入基本信息页：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729205544257.png" alt="" /></p><p>其中，<strong>代理地址</strong>就是我们图片的代理地址，比如原来的 Cloudflare 链接 <code>https://Cloudflare自定义域名/图片地址</code> 经过代理变为 <code>https://WebP代理地址/图片地址</code> 。</p><p>这里的代理地址同样支持使用自定义域名，在你的域名提供商处手动添加一下 DNS 记录即可。</p></section><section><h2>3.4 配置 PicGo<a href="#34-配置-picgo"><span>#</span></a></h2><p>不需要改别的，只需要将 S3 配置中的自定义域名改为上图的代理地址即可！</p></section><section><h2>3.5 图片压缩配置<a href="#35-图片压缩配置"><span>#</span></a></h2><p>默认图片压缩比率为 80% 。可自行设置</p><div><div><div></div><div>Warning</div></div><div><p>在配置完成后，你可能会发现访问图片链接后返回的类型仍然是 PNG ，如果确保上述操作无误，一种可能的原因是图片本身太小，转换为 WebP 后的体积大于等于原图，所以直接返回了 PNG 。</p></div></div></section><section><h2>3.6 安全性配置<a href="#36-安全性配置"><span>#</span></a></h2><section><h3>3.6.1 防止外部网页嵌入<a href="#361-防止外部网页嵌入"><span>#</span></a></h3><p>为防止图片直接或通过 <code>&lt;img&gt;</code> 标签被嵌入外部网站，WebP Cloud 提供了自定义 CORS 头部和 Referer 头限制的选项，具体原理可以参考<a href="https://docs.webp.se/webp-cloud/security" target="_blank">官方文档</a>。简单来说，你可以把代理 CORS 标头和代理允许的 referer 来源设置为你的博客域名，比如我的 <code>blog.yokumi.cn</code> ，这样外部网页嵌入你的图片将返回 403 或 CORS 错误。</p></section><section><h3>3.6.2 防止 CDN 渗透<a href="#362-防止-cdn-渗透"><span>#</span></a></h3><p>你可以将 User-Agent 设置为自定义的值，这样，User-Agent 可以作为一个预共享密钥，确保源地址只能被 WebP Cloud 访问，从而消除由于猜测源地址而导致的 CDN 渗透问题。</p></section></section></section>
<section><h1>写在后面<a href="#写在后面"><span>#</span></a></h1><p>至此，Cloudflare R2 + WebP Cloud + PicGo 就大功告成啦！</p></section>
<section><h1>参考资料<a href="#参考资料"><span>#</span></a></h1></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/the-apothecary-diaries-season-2-finale-personal-reflections/</id>
      <title type="text">《药屋少女的呢喃》第二季完结个人杂谈</title>
      <published>2025-07-27T02:22:00.000Z</published>
      <updated>2025-07-27T02:22:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/the-apothecary-diaries-season-2-finale-personal-reflections/"/>
      <summary type="text">相比于第一季，每集之间较为独立，基本都是一个个推理小故事，前后之间有点关联但不算多，主要作为序章来交代后宫的整体情况，对人物的整体刻画也不是很细，有点后宫柯南的味道。第二季在剧情安排上明显更好，虽然前半部分基本沿用了之前的小故事，看似每集之间关系不大，但当到中期进入主线后，你会发现前面几集的伏笔都被一一回收了，ep1…</summary>
      <content type="html"><![CDATA[<div><div><div></div><div>Warning</div></div><div><p>虽然 7 月 4 号已经完结了，不过我是后来才补完的。这一季依然很吸引我，遂水一篇博客。以下内容存在剧透（不过已经完结挺久了）。</p></div></div>
<p>相比于第一季，每集之间较为独立，基本都是一个个推理小故事，前后之间有点关联但不算多，主要作为序章来交代后宫的整体情况，对人物的整体刻画也不是很细，有点后宫柯南的味道。第二季在剧情安排上明显更好，虽然前半部分基本沿用了之前的小故事，看似每集之间关系不大，但当到中期进入主线后，你会发现前面几集的伏笔都被一一回收了，ep19还是ep20一集里甚至交代了过多的内容，导致观感上有些云里雾里，不过仔细一想还是能联系的上的。</p>
<p>主要人物（子翠等）身份的揭露也属于意料之外但联系前面几集的内容又在情理之中的感觉。并且通过这种方式更好地塑造了人物形象，你可能发现第二季中，无论是主角猫猫、壬氏，以及子翠等配角人物，形象都是多面且丰满的，猫猫不再只是一味地探案，也开始能洞察人的情感，而子翠前后身份的转变带来的反差，<em><strong>“笼中鸟何时飞”</strong></em>，在城墙上的最后一舞向观者展现了最真实的自己。</p>
<blockquote><p>最后猫猫送给子翠的发簪也是意外地救下了子翠（没死是对的，子翠你带我走吧😭），也算是大团圆结局了（毕竟恶意已经全部推给神美了）。不过最后子翠变玉藻，小兰也出宫找到了工作，而猫猫则回到她忠诚的花街，<em><strong>当时只道是寻常，不知三小只还会有重逢的机会吗？</strong></em></p></blockquote>
<p>如果要说观感不太好的地方，猫猫被带到城寨后，主线的进度事实上就靠“造反到起兵镇压”这种偏宏大的叙事推动了，和探案相比，确实略显平淡，造反但缺少权谋，显得被很轻易就镇压了，子昌最后给人的感觉有点“可怜之人必有可恨之处”，而神美则是所有恶意的承受者了（不过确实抽象，罪有应得）。有关前朝的交代感觉也有点语焉不详，关系理不太清楚。从网上的评价来看，偏日式风格的神话故事、宫廷传说，以及日式特有的霸凌和恶役角色等和风元素夹在中华风的宫廷剧中，确实是不少观众诟病的点。不过说到这个，就不得不提动画制作组一开始以中华风作为宣传结果在🇰🇷网友的压力下又被迫改成“东洋风”的事了😆。</p>
<p>虽然设定上的文化差异等确实对观感产生了影响，但第二季无论是剧情节奏、人物塑造、伏笔的回收上都是非常戳我的。至于感情线，我倒是觉得没亲上是最好的（最好永远别亲上，毕竟感觉亲完不知道之后要如何围绕猫猫展开了），我也不磕CP，你最好也别喂我糖精，<em><strong>猫猫的回避性人格给人感觉淡淡的，看透一切知道世间的悲哀却仍是平静也是很戳我的点</strong></em>。</p>
<blockquote><p><em>“想要娶猫猫的人，似乎无意让她成为嫔妃。”</em></p></blockquote>
<p>期待第三季捏，有空也会补一下小说，最后放一张最好的三人组！</p>
<p><img src="https://webp-pic.yokumi.cn/2025/07/20250729204648421.png" alt="" /></p>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/agentic-or-workflow/</id>
      <title type="text">Workflow 还是 Agentic？</title>
      <published>2025-07-21T02:42:00.000Z</published>
      <updated>2025-07-21T02:42:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/agentic-or-workflow/"/>
      <summary type="text">Workflow（工作流），简单来说，一系列对 LLM 的调用，旨在通过预定的一系列步骤来解决特定问题。可以理解为用 DAG（有向无环图）把 LLM 节点连起来。市面上一些可视化拖拽式 Agent 开发平台，例如 Coze、Dify 等，基本上都是基于 Workflow 的设计思路来实现的。</summary>
      <content type="html"><![CDATA[<section><h2>Workflow<a href="#workflow"><span>#</span></a></h2><p>Workflow（工作流），简单来说，一系列对 LLM 的调用，旨在通过预定的一系列步骤来解决特定问题。可以理解为用 DAG（有向无环图）把 LLM 节点连起来。市面上一些可视化拖拽式 Agent 开发平台，例如 Coze、Dify 等，基本上都是基于 Workflow 的设计思路来实现的。</p><ul>
<li>整个控制流还是由人为定义，人需要先对解决该问题的流程有一个清晰的认识，才能设计出合理的 Workflow。</li>
<li>适合于流程相对固定的问题，此外如果 LLM 自身的能力不够强大，无法通过单次调用规划并解决多步复杂问题，那么 Workflow 也是一个不错的选择。</li>
</ul><section><h3>Parallelization workflows<a href="#parallelization-workflows"><span>#</span></a></h3><p>Workflow 的一个重要优势是它可以很方便地实现并行化（parallelization）。例如，在一个 Workflow 中，如果有多个步骤之间没有依赖关系，那么这些步骤就可以同时执行，一般做法是将可并行节点拆分为多个 SubAgent 来并行处理，最终将结果汇总再进行下一步。</p></section><section><h3>Chaining workflows<a href="#chaining-workflows"><span>#</span></a></h3><p>链式调用（chaining），即将多步串联成完整的 Workflow，适合于流程相对固定的问题，让 LLM 能专注于每一步的具体实现。</p></section><section><h3>Routing workflows<a href="#routing-workflows"><span>#</span></a></h3><p>路由（routing），即根据不同的输入或条件，动态选择不同的 Workflow 路径来处理问题。这种方式适合于流程较为复杂且具有分支逻辑的问题，可以让 LLM 根据实际情况灵活地选择最合适的处理路径。</p></section></section>
<section><h2>Agentic<a href="#agentic"><span>#</span></a></h2><p>给 LLM 提供目标和工具，自行探索并实现目标。</p><ul>
<li>人对实现目标的具体流程没有预设，完全交由 LLM 来规划和执行；</li>
<li>Agent 自行设计流程来实现目标，更加灵活；</li>
<li>Agent 并不能盲目地持续干活，需要能够有观察环境的工具，据此进行决策并调整行为；</li>
</ul><p>一个相当经典的 Agent Loop (ReAct) 的流程如下：</p><span><span><span>Reasoning→Action→Observation→Reasoning again→...\text{Reasoning} \rightarrow \text{Action} \rightarrow \text{Observation} \rightarrow \text{Reasoning again} \rightarrow ...</span><span><span><span></span><span><span>Reasoning</span></span><span></span><span>→</span><span></span></span><span><span></span><span><span>Action</span></span><span></span><span>→</span><span></span></span><span><span></span><span><span>Observation</span></span><span></span><span>→</span><span></span></span><span><span></span><span><span>Reasoning again</span></span><span></span><span>→</span><span></span></span><span><span></span><span>...</span></span></span></span></span><p>简单来说，边想边做，单步迭代。</p><p>有时也会加上 Reflection（反思）来让 Agent 进行自我评估和改进，对动作执行结果进行检查。</p><p>还有一种稍微复杂一点的 Agent Loop 是 Plan-and-Execute，顾名思义就是先规划（Plan）再执行（Execute），在规划阶段，Agent 会根据当前的环境状态和目标来制定一个详细的计划，明确每一步需要执行的动作和使用的工具；在执行阶段，Agent 会按照规划好的步骤来完成任务，同时在执行过程中不断监控环境的变化，并根据需要进行调整。</p><p>相比于 ReAct，Plan-and-Execute 将规划和执行完全分开。</p></section>
<section><h2>Workflow vs. Agentic<a href="#workflow-vs-agentic"><span>#</span></a></h2><ul>
<li>同：
<ul>
<li>都需要工具来执行更小的子任务；</li>
</ul>
</li>
<li>异：
<ul>
<li>Workflow 的控制流由人预设，Agentic 的控制流由 LLM 自行规划；</li>
<li>Workflow 适合于流程相对固定的问题，对单个子任务更专注，一般完成效果更好，而 Agentic 能够提供更灵活的体验；</li>
</ul>
</li>
</ul><p><strong>Thinking:</strong></p><ul>
<li>个人认为随着模型能力的提升，Agentic 的优势会越来越明显，毕竟它能够更好地适应复杂多变的环境和任务需求，而 Workflow 则可能会因为流程设计不合理而导致效率低下或者无法完成任务，并且用户期望的场景就是大白话的自然语言描述需求，Agent 自主规划完成目标，而不是用户需要先把实现目标的流程设计好再让模型去执行，大道至简这一块。</li>
<li>不过在一些特定场景下，例如流程相对固定且对效率或可靠性要求较高的任务，Workflow 仍然是一个不错的选择。</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/wsl2-setup-and-configuration-guide/</id>
      <title type="text">WSL2 折腾记录</title>
      <published>2025-07-12T02:50:00.000Z</published>
      <updated>2025-07-12T02:50:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/wsl2-setup-and-configuration-guide/"/>
      <summary type="text">WSL2（Windows Subsystem for Linux version 2）是微软推出的一项功能，它允许你在 Windows 10 和 Windows 11 上运行一个真正的 Linux 内核，并使用几乎完整的 Linux 用户空间工具和程序，而无需安装虚拟机或双系统。</summary>
      <content type="html"><![CDATA[<section><h1>写在前面<a href="#写在前面"><span>#</span></a></h1><p>WSL2（<strong>Windows Subsystem for Linux version 2</strong>）是微软推出的一项功能，它允许你在 <strong>Windows 10 和 Windows 11</strong> 上运行一个真正的 Linux 内核，并使用几乎完整的 Linux 用户空间工具和程序，而无需安装虚拟机或双系统。相较于一代，WSL 2 在文件系统、IO 性能比 WSL1 有很大提升。如果你不想装虚拟机，或者不想花时间折腾双系统，那么 WSL2 将会是你的不二之选。</p></section>
<section><h1>安装 WSL2 以及 Linux 发行版本<a href="#安装-wsl2-以及-linux-发行版本"><span>#</span></a></h1><p>下面的操作默认你的系统是 Windows 10 or Windows 11 。如低于此版本请参考<a href="https://learn.microsoft.com/zh-cn/windows/wsl/" target="_blank">微软官方文档</a>或查阅其他网络资料。</p><ol>
<li>用管理员权限打开命令行工具，输入：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--install</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>注意此命令会安装 WSL2 及其默认的 Linux 发行版（Ubuntu），如需更改，可以使用：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--install</span><span> </span><span>-d</span><span> </span><span>&lt;Distribution</span><span> </span><span>Name&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>发行版名称可以输入以下命令查看：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--list</span><span> </span><span>--online</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>输入形如：</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211042801.png" alt="" /></p></section>
<section><h1>设置 Linux 用户名和密码<a href="#设置-linux-用户名和密码"><span>#</span></a></h1><p>在上一步的安装结束后，WSL2 会自动启动 Linux 并提示你输入用户名和密码。</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211026469.png" alt="" /></p><p>注意：</p><ul>
<li>创建<strong>用户名</strong>和<strong>密码</strong>后，该帐户将是分发版的默认用户，并将在启动时自动登录；</li>
<li>此帐户将被视为 Linux 管理员，能够运行 <code>sudo</code> 管理命令；</li>
<li>如需更改密码，请启动 Linux 并输入 <code>passwd</code> ；</li>
<li>如忘记密码，请打开 PowerShell 并输入 <code>wsl -d &lt;Distribution Name&gt; -u root</code> 进入目标发行版的根目录，再使用 <code>passwd &lt;username&gt;</code> 更新对应用户的密码；更新完毕后，输入 <code>exit</code> 关闭；</li>
</ul></section>
<section><h1>迁移安装位置（可选）<a href="#迁移安装位置可选"><span>#</span></a></h1><p>WSL2 默认安装在 C 盘（系统盘），如果你容量不足，可以考虑迁移到非系统盘，步骤如下：</p><ol>
<li>确保 Linux 发行版本处于关闭状态；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>-l</span><span> </span><span>-v</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输出形如：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211006112.png" alt="" /></p><ol>
<li>导出系统镜像；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 以压缩包的形式导出系统镜像</span></div></div><div><div><div>2</div></div><div><span>wsl</span><span> </span><span>--export</span><span> </span><span>Ubuntu-20.04</span><span> </span><span>E:</span><span>\W</span><span>SLinux</span><span>\U</span><span>buntu-20.04.tar</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中，<code>E:\WSLinux</code> 请替换你自己的目标位置；</p><ol>
<li>注销原有的 Linux 发行版本；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--unregister</span><span> </span><span>Ubuntu-20.04</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可以通过 <code>wsl -l -v</code> 检查是否成功注销；</p><ol>
<li>重新导入系统；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--import</span><span> </span><span>Ubuntu-20.04</span><span> </span><span>E:</span><span>\W</span><span>SLinux</span><span>\ </span><span>E:</span><span>\W</span><span>SLinux</span><span>\U</span><span>buntu-20.04.tar</span><span> </span><span>--version</span><span> </span><span>2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>请将上述命令中的内容替换为你的名称和路径；其中 <code>--version 2</code> 表示使用 WSL2 。</p><ol>
<li>修改默认用户；</li>
</ol><p>在重启 Linux 后，系统默认使用 root 账户登录，如需切换为你刚刚创建的用户，请在 Linux 中输入：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ubuntu2004.exe</span><span> </span><span>config</span><span> </span><span>--default-user</span><span> </span><span>yokumi</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意：</p><ul>
<li><code>ubuntu-2004.exe</code> 请替换为对应发行版名称，比如如果你的发行版名称是 Ubuntu-22.04，对应的可执行文件名称为 <code>ubuntu-2204.exe</code> ；</li>
<li><code>--default-user</code> 请替换为你自己的用户名。</li>
<li>输入后不会输出反馈信息，重新启动 Linux 后可以验证；</li>
</ul></section>
<section><h1>更新和升级软件包<a href="#更新和升级软件包"><span>#</span></a></h1><p>WSL2 自带的 Linux 发行版的大部分包的版本都较旧，用包管理器升级即可：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span><span> &amp;&amp; </span><span>sudo</span><span> </span><span>apt</span><span> </span><span>upgrade</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h1>Docker 远程容器配置<a href="#docker-远程容器配置"><span>#</span></a></h1><section><h2>1. 安装和配置<a href="#1-安装和配置"><span>#</span></a></h2><ol>
<li>安装 Docker Desktop ；</li>
<li>调整 Docker Settings ：</li>
</ol><p>找到 Settings -&gt; General ，启用 <code>Use the WSL 2 based engine</code> ，如下图：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729210938479.png" alt="" /></p><p>找到 Resource -&gt; WSL integration ，勾选对应的 Linux 发行版本：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729210919082.png" alt="" /></p><p>勾选并应用；</p><ol>
<li>验证 Docker 生效；</li>
</ol><p>重启 Linux 后，输入 <code>docker -v</code> ，以及运行简单的内置 Docker 映像：<code>docker run hello-world</code> ，观察到类似以下输出，说明安装成功，并正常工作：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729210845203.png" alt="" /></p><p>注意，如遇网络问题，请自行寻找魔法或者 Docker 镜像！</p></section><section><h2>2. 迁移 Docker Desktop 和 Docker Desktop Data<a href="#2-迁移-docker-desktop-和-docker-desktop-data"><span>#</span></a></h2><p>和迁移 Linux 发行版是一致的，此处不再演示。</p><div><div><div></div><div>Warning</div></div><div><p>较新版本（应该是 2023+）的 Docker 中，提供了快速迁移的设置 Resource -&gt; Advanced ，如下图：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729210757397.png" alt="" /></p><p>实测直接选择自定义路径即可，并且重启 Linux 后无其他问题。同时，高版本的 Docker 将数据和服务整合进一个子系统中，运行 <code>wsl -v</code> 只会看到 <code>docker-desktop</code> 。</p></div></div></section><section><h2>3. 回收 Docker 环境空间<a href="#3-回收-docker-环境空间"><span>#</span></a></h2><p>Docker 用久了会产生很多镜像文件。直接删除 Image 可能对回收存储空间并无大用，因为 <code>ext4.vhdx</code> 的磁盘空间会根据加载的数据自动增长，但是无法自动回收。需要进行手动回收。</p><ol>
<li>停止 Linux ：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--shutdown</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>运行 diskpart 释放空间：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>diskpart</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 选择你对应虚拟机文件地址执行释放空间程序</span></div></div><div><div><div>4</div></div><div><span>select</span><span> </span><span>vdisk</span><span> </span><span>file</span><span>=</span><span>"E:\WSLinux\docker-desktop-data\ext4.vhdx"</span></div></div><div><div><div>5</div></div><div><span>attach</span><span> </span><span>vdisk</span><span> </span><span>readonly</span></div></div><div><div><div>6</div></div><div><span>compact</span><span> </span><span>vdisk</span></div></div><div><div><div>7</div></div><div><span>detach</span><span> </span><span>vdisk</span></div></div><div><div><div>8</div></div><div><span>exit</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>实现远程连接<a href="#实现远程连接"><span>#</span></a></h1><p>考虑以下场景：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Host A   -----&gt;   Host B   -----&gt;   WSL(at Host B)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h2>1. 配置 WSL 上的 SSH 端口<a href="#1-配置-wsl-上的-ssh-端口"><span>#</span></a></h2><p>WSL2 不需要折腾 WSL 自带的虚拟网卡了。WSL 中 <code>openssh-server</code> 默认的 22 端口会和 Windows 的 OpenSSH 服务冲突，导致 WSL 中的 SSH 服务无法启动。</p><p>这里采用更改 WSL 的 SSH 端口的方案。（当然，你更改 Windows 的 SSH 端口并配置防火墙规则也是可以的）</p><ol>
<li>编辑 WSL 的 SSH 配置文件；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>vim</span><span> </span><span>/etc/ssh/sshd_config</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>找到以下几项，去掉前面的注释 <code>#</code> 并修改即可：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Port 2222</span></div></div><div><div><div>2</div></div><div><span>PermitRootLogin yes</span></div></div><div><div><div>3</div></div><div><span>PubkeyAuthentication yes</span></div></div><div><div><div>4</div></div><div><span>PasswordAuthentication yes</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>作为本地桥接的主机，并不需要很多安全需求，很多 SSH 的安全功能反而会带来麻烦。这里直接允许 ROOT 用户登录。</p><ol>
<li>开启并设置 SSH 服务开机自启动；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>enable</span><span> </span><span>ssh</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>start</span><span> </span><span>ssh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>本机（Host B）测试连接；</li>
</ol><p>首先通过以下命令获取你的 WSL 的 IP 地址：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ip</span><span> </span><span>addr</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后在 Windows 命令行输入：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>-p</span><span> </span><span>2222</span><span> </span><span>root@&lt;wsl</span><span> </span><span>ipaddr&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果成功连接，则说明现在本机已经能连接 WSL 的 SSH 服务。</p><p>但是这并不意味着远程主机（Host A）也能直接通过该 IP 地址建立连接，仔细观察该 IP 地址即可发现其是私有地址（一般是 172 开头的），显然无法出网。</p></section><section><h2>2. 配置端口转发<a href="#2-配置端口转发"><span>#</span></a></h2><p>为了能够从远程主机连接到你的 WSL Linux 子系统，你需要确保 WSL 具有公共 IP 地址，需要设置镜像网络或者设置端口转发以将流量路由到 WSL 子系统的局域网 IP 地址。</p><p>假设 WSL 的 SSH 服务在 <code>172.28.240.1:2222</code> ，你可以让 Windows 把端口（默认为 22 ）转发过去：</p><ol>
<li>在 Windows Powershell（管理员）中运行：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>netsh interface portproxy add v4tov4 listenport</span><span>=</span></span><span>22</span><span><span> listenaddress</span><span>=</span></span><span>0.0</span><span>.</span><span>0.0</span><span><span> connectport</span><span>=</span></span><span>2222</span><span><span> connectaddress</span><span>=</span></span><span>172.28</span><span>.</span><span>240.1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>可以通过以下命令查看当前设置的所有转发规则：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>netsh interface portproxy show all</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>如需删除，可通过以下命令：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>netsh interface portproxy delete v4tov4 listenport</span><span>=</span></span><span>22</span><span><span> listenaddress</span><span>=</span></span><span>0.0</span><span>.</span><span>0.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>在远程主机（Host A）上测试连接：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>-p</span><span> </span><span>22</span><span> </span><span>root@&lt;Host</span><span> </span><span>B</span><span> </span><span>的公网</span><span> </span><span>IP</span><span> </span><span>地址&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当然，使用局域网 IP 地址也可以（适用于没有公网 IP 地址，比如校园网环境的情况，两台主机在同一局域网下即可）。</p><p>注意，如果你没有使用 Windows 的默认端口 22 ，请自行添加防火墙规则！</p></section><section><h2>3. 配置镜像网络（可选）<a href="#3-配置镜像网络可选"><span>#</span></a></h2><p>WSL2 在 2.0.0 版本引入了以下网络新特性，可以通过配置让 WSL 网络与 Windows 网卡 <strong>桥接</strong>，而不是 NAT ，这样，<strong>WSL 的 IP 会与 Windows 在同一网段</strong>，外部设备（如 Host A）可以直接访问 WSL 的服务，非常方便！</p><ol>
<li>更新 WSL 2 ；</li>
</ol><p>由于新特性还是 pre-release 上的内容，需要通过以下参数获取更新：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--update</span><span> </span><span>--pre-release</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意，如仍无法使用，可能是 Windows 的版本问题，可参考启动 WSL 后输出的信息。</p><ol>
<li>创建配置文件；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>notepad</span><span> </span><span>C:</span><span>\U</span><span>sers</span><span>\&lt;</span><span>你的用户名&gt;</span><span>\.</span><span>wslconfig</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>添加以下内容：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[wsl2]</span></div></div><div><div><div>2</div></div><div><span>networkingMode</span><span><span>=</span><span>mirrored</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>重启 WSL ：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wsl</span><span> </span><span>--shutdown</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>验证功能生效：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ip addr</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输出的 IP 地址和你的本机地址一致。</p></section><section><h2>4. 配置 Windows 端口<a href="#4-配置-windows-端口"><span>#</span></a></h2><p>如果你在上述操作中，Windows 中设置的转发端口（端口转发方式）或 WSL 中设置的 SSH 服务端口（镜像网络方式，这种方式下，Windows 和 WSL 都使用同一个你设置的端口）不是默认端口 22 ，那么你需要额外进行以下操作：</p><ol>
<li>使用命令行添加防火墙规则：</li>
</ol><p>在 Windows Powershell（管理员）中执行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>netsh advfirewall firewall add rule name</span><span>=</span></span><span>"WSL SSH"</span><span><span> dir</span><span>=</span></span><span>in</span><span><span> action</span><span>=</span><span>allow protocol</span><span>=</span><span>TCP localport</span><span>=</span><span>&lt;你设置的对应端口&gt;</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或者通过图形化界面设置：</p><ol>
<li>
<p>图形界面配置：</p>
<ol>
<li>打开“控制面板” → 系统与安全 → Windows Defender 防火墙 → 高级设置；</li>
<li>进入“入站规则”，点击右侧新建规则：</li>
</ol>
<ul>
<li>类型：端口</li>
<li>端口号：2222（TCP）</li>
<li>允许连接</li>
<li>应用于所有网络类型（私有、公共、域）</li>
<li>命名：WSL SSH（或自定义的）</li>
</ul>
</li>
<li>
<p>在远程主机上进行测试；</p>
</li>
</ol><p>在远程主机（Host A）的终端输入：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>telnet</span><span> </span><span>&lt;Host</span><span> </span><span>B</span><span> </span><span>IP&gt;</span><span> </span><span>&lt;Your</span><span> </span><span>Port&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果连接成功，就说明防火墙放行了。</p><p>接下来，你可以通过：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>-p</span><span> </span><span>&lt;Your</span><span> </span><span>Port&gt;</span><span> </span><span>root@&lt;Host</span><span> </span><span>B</span><span> </span><span>IP&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输入密码后，成功实现远程连接！</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729210733699.png" alt="" /></p></section></section>
<section><h1>设置 WSL 的开机启动和后台运行<a href="#设置-wsl-的开机启动和后台运行"><span>#</span></a></h1><p>WSL2 的启动时间其实是较慢的。同时，WSL2 还会默认关闭不使用的实例，当你关闭了 WSL 的 Console 后，实例会自动关闭，虽然它的初衷是为了减少计算机资源占用，但是对于会频繁唤醒 WSL 的用户来说，确实会出现启动时间过长的情况。解决方法如下：</p><ol>
<li>WIN + R 输入并运行 <code>shell:startup</code> 打开启动目录；</li>
<li>新建一个文件名为 <code>wsl-startup.vbs</code> ；</li>
<li>使用记事本编辑文件，填入以下内容：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>set ws </span><span>=</span><span> wscript.CreateObject(</span></span><span>"wscript.shell"</span><span>)</span></div></div><div><div><div>2</div></div><div><span>ws.run </span><span>"wsl -d &lt;你的 Linux 发行版名称&gt;"</span><span>, </span><span>0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样当你系统启动，登录系统后，Windows 会开启 WSL 实例，它会永久等待输入，不会关闭。所以当你下次再使用WSL命令时，就不会遇到需要重新唤醒 WSL 的耗时。</p><p>关于 WSL 在后台运行占用资源的问题，事实上你可以通过任务管理器查看，其并不会占用多少内存，不过如果你还担心，你可以继续在配置文件中添加：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>notepad</span><span> </span><span>C:</span><span>\U</span><span>sers</span><span>\&lt;</span><span>你的用户名&gt;</span><span>\.</span><span>wslconfig</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[wsl2]</span></div></div><div><div><div>2</div></div><div><span>memory</span><span><span>=</span><span>4GB</span></span></div></div><div><div><div>3</div></div><div><span>processors</span><span><span>=</span><span>2</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/blog-beautification-record/</id>
      <title type="text">博客美化记录</title>
      <published>2025-07-03T05:06:00.000Z</published>
      <updated>2025-07-03T05:06:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/blog-beautification-record/"/>
      <summary type="text">众所周知， 一个好的前端/排版是继续做下去的动力（bushi） 。 难得有空，继续来建设一下博客，简单美化一下前端。 引入自定义的 JS/CSS/HTML 是继续下列美化操作的条件，可以通过以下主题配置（楼主使用的是 Fluid 主题，不同主题之间可能存在一些差异）。你可以在配置文件中找到：</summary>
      <content type="html"><![CDATA[<section><h1>写在前面<a href="#写在前面"><span>#</span></a></h1><p>众所周知， 一个好的前端/排版是继续做下去的动力（bushi） 。</p><p>难得有空，继续来建设一下博客，简单美化一下前端。</p></section>
<section><h1>自定义 JS/CSS/HTML<a href="#自定义-jscsshtml"><span>#</span></a></h1><p>引入自定义的 JS/CSS/HTML 是继续下列美化操作的条件，可以通过以下<strong>主题配置</strong>（楼主使用的是 <a href="https://hexo.fluid-dev.com/docs/guide/#%E8%87%AA%E5%AE%9A%E4%B9%89-js-css-html" target="_blank">Fluid 主题</a>，不同主题之间可能存在一些差异）。你可以在配置文件中找到：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 指定自定义 .js 文件路径，支持列表；路径是相对 source 目录，如 /js/custom.js 对应存放目录 source/js/custom.js</span></div></div><div><div><div>2</div></div><div><span># Specify the path of your custom js file, support list. The path is relative to the source directory, such as `/js/custom.js` corresponding to the directory `source/js/custom.js`</span></div></div><div><div><div>3</div></div><div><span>custom_js</span><span>:</span></div></div><div><div><div>4</div></div><div><span>- </span><span>/js/custom.js</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 指定自定义 .css 文件路径，用法和 custom_js 相同</span></div></div><div><div><div>7</div></div><div><span># The usage is the same as custom_js</span></div></div><div><div><div>8</div></div><div><span>custom_css</span><span>:</span></div></div><div><div><div>9</div></div><div><span>- </span><span>/css/custom.css</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>自行添加自定义的 JS/CSS/HTML 文件即可。</p></section>
<section><h1>全局字体<a href="#全局字体"><span>#</span></a></h1><ol>
<li>首先在 <code>custom.css</code> 中引入字体 CDN ；</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 字体 */</span></div></div><div><div><div>2</div></div><div><span>@import</span><span> </span><span>url</span><span>(</span><span>'https://chinese-fonts-cdn.deno.dev/packages/maple-mono-cn/dist/MapleMono-CN-Bold/result.css'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>然后修改主题配置文件的字体部分，例如：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 主题字体配置</span></div></div><div><div><div>2</div></div><div><span># Font</span></div></div><div><div><div>3</div></div><div><span>font</span><span>:</span></div></div><div><div><div>4</div></div><div><span>font_size</span><span>: </span><span>16px</span></div></div><div><div><div>5</div></div><div><span>font_family</span><span>: </span><span>'Maple Mono CN'</span></div></div><div><div><div>6</div></div><div><span>font_weight</span><span>: </span><span>400</span></div></div><div><div><div>7</div></div><div><span>letter_spacing</span><span>: </span><span>0.02em</span></div></div><div><div><div>8</div></div><div><span>code_font_size</span><span>: </span><span>85%</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h1>文章界面背景毛玻璃效果<a href="#文章界面背景毛玻璃效果"><span>#</span></a></h1><ol>
<li>在 source/css 目录下新建样式文件 <code>frostedglassbackground.css</code> ，内容如下：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#board</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>-webkit-backdrop-filter</span><span>: </span><span>blur</span><span>(</span><span><span>15</span><span>px</span></span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>backdrop-filter: </span><span>blur</span><span>(</span><span><span>15</span><span>px</span></span><span>);</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>#toc</span><span> {</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>padding: </span><span><span>10</span><span>px</span></span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>top: </span><span><span>4</span><span>rem</span></span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>background-color: </span><span>var</span><span>(</span><span>--board-bg-color</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>border-radius: </span><span><span>10</span><span>px</span></span><span>;</span></div></div><div><div><div>11</div></div><div><span>  </span><span>-webkit-backdrop-filter</span><span>: </span><span>blur</span><span>(</span><span><span>15</span><span>px</span></span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>backdrop-filter: </span><span>blur</span><span>(</span><span><span>15</span><span>px</span></span><span>);</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>引入自定义的 CSS 文件，操作略；</li>
<li>修改主题配置文件 <code>_config.fluid.yml</code> 的以下字段：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 主面板背景色</span></div></div><div><div><div>2</div></div><div><span># Color of main board</span></div></div><div><div><div>3</div></div><div><span>board_color</span><span>: </span><span>"#ffffff80"</span></div></div><div><div><div>4</div></div><div><span>board_color_dark</span><span>: </span><span>"#15172280"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h1>统一图片宽度<a href="#统一图片宽度"><span>#</span></a></h1><p>markdown 本身的图片渲染大小就是一大玄学，这里直接采用插入自定义样式文件匹配图片元素，将其固定为页面大小的 70% ，CSS 文件内容如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.markdown-body</span><span> </span><span>p</span><span> </span><span>&gt;</span><span> </span><span>img</span><span>,</span></div></div><div><div><div>2</div></div><div><span>.markdown-body</span><span> </span><span>p</span><span> </span><span>&gt;</span><span> </span><span>a</span><span> </span><span>&gt;</span><span> </span><span>img</span><span>,</span></div></div><div><div><div>3</div></div><div><span>.markdown-body</span><span> </span><span>figure</span><span> </span><span>&gt;</span><span> </span><span>img</span><span>,</span></div></div><div><div><div>4</div></div><div><span>.markdown-body</span><span> </span><span>figure</span><span> </span><span>&gt;</span><span> </span><span>a</span><span> </span><span>&gt;</span><span> </span><span>img</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>width: </span><span><span>70</span><span>%</span></span><span>; </span><span>/* 强制宽度为页面的70% */</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>height: </span><span>auto</span><span>; </span><span>/* 保持图片比例 */</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>display: </span><span>block</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>margin-left: </span><span>auto</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>margin-right: </span><span>auto</span><span>; </span><span>/* 图片居中 */</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>.page-content</span><span> </span><span>img</span><span><span>,</span><span> </span></span><span>.post-content</span><span> </span><span>img</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>width: </span><span><span>70</span><span>%</span></span><span>; </span><span>/* 强制宽度为页面的70% */</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>height: </span><span>auto</span><span>; </span><span>/* 保持图片比例 */</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>display: </span><span>block</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>margin-left: </span><span>auto</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>margin-right: </span><span>auto</span><span>; </span><span>/* 图片居中 */</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h1>Mac 样式代码块<a href="#mac-样式代码块"><span>#</span></a></h1><p>在 source/css 目录下新建样式文件 <code>mac.styl</code> ，内容如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.highlight</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>background: </span><span>#</span><span>011627</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>5</span><span>px</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>box-shadow: </span><span>0</span><span> </span><span><span>10</span><span>px</span></span><span> </span><span><span>30</span><span>px</span></span><span> </span><span>0</span><span> </span><span>rgba</span><span>(</span><span>0</span><span>, </span><span>0</span><span>, </span><span>0</span><span>, </span><span>.4</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>padding-top: </span><span><span>30</span><span>px</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&amp;</span><span>::before</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>background: </span><span>#</span><span>fc625d</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>border-radius: </span><span><span>50</span><span>%</span></span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>box-shadow: </span><span><span>20</span><span>px</span></span><span> </span><span>0</span><span> </span><span>#</span><span>fdbc40</span><span>, </span><span><span>40</span><span>px</span></span><span> </span><span>0</span><span> </span><span>#</span><span>35cd4b</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>content: </span><span>' '</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>height: </span><span><span>12</span><span>px</span></span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>left: </span><span><span>12</span><span>px</span></span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>margin-top: </span><span><span>-20</span><span>px</span></span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>position: </span><span>absolute</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>width: </span><span><span>12</span><span>px</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后在配置文件 <code>_config.fluid.yml</code> 中引入该自定义样式文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>custom_css</span><span>:</span></div></div><div><div><div>2</div></div><div><span>- </span><span>/css/custom.css</span></div></div><div><div><div>3</div></div><div><span>- </span><span>/css/mac.css</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div></div><div>Warning</div></div><div><p><strong>注意</strong>，不要写成 <code>/css/mac.styl</code> ，否则会被错误识别为 <code>/css/mac.styl.css</code> 。</p></div></div><section><h3>折叠框中的方式有一定 BUG，并且会和我的伪代码渲染冲突，如需参考还是尽量先参考上文的方法吧，楼主会在之后有空尽可能提供支持<a href="#折叠框中的方式有一定-bug并且会和我的伪代码渲染冲突如需参考还是尽量先参考上文的方法吧楼主会在之后有空尽可能提供支持"><span>#</span></a></h3><ol>
<li>安装 Hexo Shiki Plugin 插件：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 在博客根目录下执行</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-shiki-plugin</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>添加/修改该插件有关配置：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 在 Hexo 配置文件 config.yml 中</span></div></div><div><div><div>2</div></div><div><span># 以下部分一般是初始配置文件中就有的内容</span></div></div><div><div><div>3</div></div><div><span>syntax_highlighter</span><span>: </span><span># 留空，原本为 highlight.js</span></div></div><div><div><div>4</div></div><div><span>highlight</span><span>:</span></div></div><div><div><div>5</div></div><div><span>  </span><span>enable</span><span>: </span><span>false</span></div></div><div><div><div>6</div></div><div><span>prismjs</span><span>:</span></div></div><div><div><div>7</div></div><div><span>  </span><span>enable</span><span>: </span><span>false</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 添加该插件的配置</span></div></div><div><div><div>10</div></div><div><span>shiki</span><span>:</span></div></div><div><div><div>11</div></div><div><span>  </span><span>theme</span><span>: </span><span>one-dark-pro</span><span> </span><span># highlight-theme：one-dark-pro / github-light / github-dark / material-theme-palenight</span></div></div><div><div><div>12</div></div><div><span>  </span><span>line_number</span><span>: </span><span>true</span><span> </span><span># whether to show the line_number</span></div></div><div><div><div>13</div></div><div><span>  </span><span>beautify</span><span>: </span><span>true</span><span> </span><span># whether to add highlight tool true or false</span></div></div><div><div><div>14</div></div><div><span>  </span><span>highlight_copy</span><span>: </span><span>true</span><span> </span><span># copy button</span></div></div><div><div><div>15</div></div><div><span>  </span><span>highlight_lang</span><span>: </span><span>true</span><span> </span><span># show the code language</span></div></div><div><div><div>16</div></div><div><span>  </span><span>highlight_height_limit</span><span>: </span><span>360</span><span> </span><span># code-block max height,unit: px</span></div></div><div><div><div>17</div></div><div><span>  </span><span>is_highlight_shrink</span><span>: </span><span>false</span><span> </span><span># true: shrink the code blocks / false: expand the code blocks | none: expand code blocks and hide the button</span></div></div><div><div><div>18</div></div><div><span>  </span><span>copy</span><span>: </span><span># copy message</span></div></div><div><div><div>19</div></div><div><span>    </span><span>success</span><span>: </span><span>'Copy Success'</span></div></div><div><div><div>20</div></div><div><span>    </span><span>error</span><span>: </span><span>'Copy Error'</span></div></div><div><div><div>21</div></div><div><span>    </span><span>no_support</span><span>: </span><span>'Browser Not Support'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>修改主题配置文件 <code>_config_fluid.yml</code> ：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>code</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>copy_btn</span><span>: </span><span>false</span></div></div><div><div><div>3</div></div><div><span>  </span><span>language</span><span>:</span></div></div><div><div><div>4</div></div><div><span>  </span><span>enable</span><span>: </span><span>false</span></div></div><div><div><div>5</div></div><div><span>  </span><span>highlight</span><span>:</span></div></div><div><div><div>6</div></div><div><span>  </span><span>enable</span><span>: </span><span>false</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>上述操作主要都是为了禁用 Hexo 和主题原有的 Highlight 相关内容，避免造成冲突。</p><ol>
<li>引入图标文件：</li>
</ol><p>添加 Font Awesome 图标库，这里用的是 360 的 CDN，也可以选择其他的，在 <code>custom.css</code> 中添加即可：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>@import</span><span> </span><span>url</span><span>(</span><span>'https://lib.baomitu.com/font-awesome/6.1.2/css/all.min.css'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>仿知乎链接卡片<a href="#仿知乎链接卡片"><span>#</span></a></h1><ol>
<li>在 source/css 目录下新建样式文件 <code>linkcard.css</code> ，内容如下：</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>:root</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>--linkcard-bg-color</span><span>: </span><span>#</span><span>eeefef</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>[</span><span>data-user-color-scheme</span><span>=</span><span>"dark"</span><span>]</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>--linkcard-bg-color</span><span>: </span><span>#</span><span>242a38</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>.LinkCard</span><span><span>,</span><span> </span></span><span><span>.LinkCard</span><span>:hover</span></span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>text-decoration: </span><span>none</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>border: </span><span>none</span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>color: </span><span>inherit</span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div><span>.LinkCard</span><span> {</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>display: </span><span>block</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>margin: </span><span><span>1</span><span>em</span></span><span><span> </span><span>auto</span><span>;</span></span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>width: </span><span><span>60</span><span>%</span></span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>box-sizing: </span><span>border-box</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>12</span><span>px</span></span><span>;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>max-width: </span><span><span>100</span><span>%</span></span><span>;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>overflow: </span><span>hidden</span><span>;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>color: </span><span>inherit</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>text-decoration: </span><span>none</span><span>;</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>background: </span><span>var</span><span>(</span><span>--linkcard-bg-color</span><span>);</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>transition: transform </span><span><span>0.4</span><span>s</span></span><span><span> </span><span>ease-in</span><span>;</span></span></div></div><div><div><div>26</div></div><div><span>}</span></div></div><div><div><div>27</div></div><div><span>.ztext</span><span><span> { word-break: </span><span>break-word</span><span>; line-height: </span></span><span>1.6</span><span>; }</span></div></div><div><div><div>28</div></div><div><span>.LinkCard-content</span><span> {</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>display: </span><span>flex</span><span>;</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>align-items: </span><span>center</span><span>;</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>justify-content: </span><span>space-between</span><span>;</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>padding: </span><span><span>12</span><span>px</span></span><span>;</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>border-radius: </span><span>inherit</span><span>;</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>background-color: </span><span>var</span><span>(</span><span>--linkcard-bg-color</span><span>);</span></div></div><div><div><div>36</div></div><div><span>}</span></div></div><div><div><div>37</div></div><div><span>.LinkCard-text</span><span><span> { overflow: </span><span>hidden</span><span>; }</span></span></div></div><div><div><div>38</div></div><div><span>.LinkCard-title</span><span> {</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>display: </span><span>-webkit-box</span><span>;</span></div></div><div><div><div>40</div></div><div><span>    </span><span>-webkit-line-clamp</span><span>: </span><span>2</span><span>;</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>line-clamp: </span><span>2</span><span>;</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>overflow: </span><span>hidden</span><span>;</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>text-overflow: </span><span>ellipsis</span><span>;</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>max-height: </span><span>calc</span><span>(</span><span><span>16</span><span>px</span></span><span><span> </span><span>*</span><span> </span></span><span>1.25</span><span><span> </span><span>*</span><span> </span></span><span>2</span><span>);</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>font-size: </span><span><span>16</span><span>px</span></span><span>;</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>font-weight: </span><span>500</span><span>;</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>line-height: </span><span>1.25</span><span>;</span></div></div><div><div><div>48</div></div><div><span><span>    </span></span><span>color: </span><span>inherit</span><span>;</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div><div><div><div>50</div></div><div><span>.LinkCard-meta</span><span> {</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>display: </span><span>flex</span><span>;</span></div></div><div><div><div>52</div></div><div><span><span>    </span></span><span>margin-top: </span><span><span>4</span><span>px</span></span><span>;</span></div></div><div><div><div>53</div></div><div><span><span>    </span></span><span>font-size: </span><span><span>14</span><span>px</span></span><span>;</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>line-height: </span><span><span>20</span><span>px</span></span><span>;</span></div></div><div><div><div>55</div></div><div><span><span>    </span></span><span>color: </span><span>#</span><span>999</span><span>;</span></div></div><div><div><div>56</div></div><div><span><span>    </span></span><span>white-space: </span><span>nowrap</span><span>;</span></div></div><div><div><div>57</div></div><div><span>}</span></div></div><div><div><div>58</div></div><div><span>.LinkCard-imageCell</span><span> {</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>margin-left: </span><span><span>8</span><span>px</span></span><span>;</span></div></div><div><div><div>60</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>6</span><span>px</span></span><span>;</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div><div><div><div>62</div></div><div><span>.LinkCard-image</span><span> {</span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>display: </span><span>block</span><span>;</span></div></div><div><div><div>64</div></div><div><span><span>    </span></span><span>width: </span><span><span>60</span><span>px</span></span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>height: </span><span>auto</span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>66</div></div><div><span><span>    </span></span><span>border-radius: </span><span>inherit</span><span>;</span></div></div><div><div><div>67</div></div><div><span><span>    </span></span><span>margin-bottom: </span><span>0</span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>68</div></div><div><span>}</span></div></div><div><div><div>69</div></div><div><span><span>.LinkCard</span><span>:hover</span></span><span> {</span></div></div><div><div><div>70</div></div><div><span><span>    </span></span><span>transform: </span><span>translate3d</span><span>(</span><span>0</span><span>, </span><span><span>-2</span><span>px</span></span><span>, </span><span>0</span><span>);</span></div></div><div><div><div>71</div></div><div><span><span>    </span></span><span>box-shadow: </span><span>0</span><span> </span><span>0</span><span> </span><span><span>16</span><span>px</span></span><span> </span><span>0</span><span> </span><span>rgba</span><span>(</span><span>34</span><span>, </span><span>135</span><span>, </span><span>250</span><span>, </span><span>0.8</span><span>);</span></div></div><div><div><div>72</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>在 source/js 目录下新建自定义 js 文件：</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>window</span><span>.</span></span><span>onload</span><span> </span><span>=</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>var</span><span><span> </span><span>LinkCards</span><span> </span></span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>getElementsByClassName</span><span>(</span><span>'LinkCard'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>    </span><span>for</span><span> (</span><span>var</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>LinkCards</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>// 截断链接</span></div></div><div><div><div>5</div></div><div><span>        </span><span>var</span><span> </span><span>truncateLink</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>url</span><span>, </span><span>maxLength</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>            </span><span>if</span><span><span> (</span><span>url</span><span>.</span></span><span>length</span><span> </span><span>&lt;=</span><span><span> </span><span>maxLength</span><span>) </span></span><span>return</span><span><span> </span><span>url</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span>            </span><span>return</span><span><span> </span><span>url</span><span>.</span></span><span>slice</span><span>(</span><span>0</span><span><span>, </span><span>maxLength</span><span>) </span></span><span>+</span><span> </span><span>'...'</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>};</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>        </span><span>var</span><span><span> </span><span>LinkCard</span><span> </span></span><span>=</span><span><span> </span><span>LinkCards</span><span>[</span><span>i</span><span>];</span></span></div></div><div><div><div>11</div></div><div><span>        </span><span>var</span><span><span> </span><span>link</span><span> </span></span><span>=</span><span><span> </span><span>LinkCard</span><span>.</span></span><span>href</span><span>;</span></div></div><div><div><div>12</div></div><div><span>        </span><span>var</span><span><span> </span><span>title</span><span> </span></span><span>=</span><span><span> </span><span>LinkCard</span><span>.</span></span><span>innerText</span><span>;</span></div></div><div><div><div>13</div></div><div><span>        </span><span>// 如果没有指定logo地址，则使用默认的loading图片</span></div></div><div><div><div>14</div></div><div><span>        </span><span>var</span><span><span> </span><span>logourl</span><span> </span></span><span>=</span><span><span> </span><span>LinkCard</span><span>.</span></span><span>name</span><span> </span><span>?</span><span><span> </span><span>LinkCard</span><span>.</span></span><span>name</span><span> </span><span>:</span><span> </span><span>'/img/loading/loading1.gif'</span><span>;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>var</span><span><span> </span><span>displayLink</span><span> </span></span><span>=</span><span> </span><span>truncateLink</span><span><span>(</span><span>link</span><span>, </span></span><span>32</span><span>);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>LinkCard</span><span>.</span><span>innerHTML</span><span> </span><span>=</span></div></div><div><div><div>18</div></div><div><span>            </span><span>`&lt;span class="LinkCard-content"&gt;</span></div></div><div><div><div>19</div></div><div><span><span>                </span></span><span>&lt;span class="LinkCard-text"&gt;</span></div></div><div><div><div>20</div></div><div><span><span>                    </span></span><span>&lt;span class="LinkCard-title"&gt;</span><span>${</span><span>title</span><span>}</span><span>&lt;/span&gt;</span></div></div><div><div><div>21</div></div><div><span><span>                    </span></span><span>&lt;span class="LinkCard-meta"&gt;</span></div></div><div><div><div>22</div></div><div><span><span>                        </span></span><span>&lt;span style="display:inline-flex;align-items:center"&gt;</span></div></div><div><div><div>23</div></div><div><span><span>                            </span></span><span>&lt;svg class="Zi Zi--InsertLink" fill="currentColor" viewBox="0 0 24 24" width="17" height="17"&gt;</span></div></div><div><div><div>24</div></div><div><span><span>                                </span></span><span>&lt;path d="M6.77 17.23c-.905-.904-.94-2.333-.08-3.193l3.059-3.06-1.192-1.19-3.059 3.058c-1.489 1.489-1.427 3.954.138 5.519s4.03 1.627 5.519.138l3.059-3.059-1.192-1.192-3.059 3.06c-.86.86-2.289.824-3.193-.08zm3.016-8.673l1.192 1.192 3.059-3.06c.86-.86 2.289-.824 3.193.08.905.905.94 2.334.08 3.194l-3.059 3.06 1.192 1.19 3.059-3.058c1.489-1.489 1.427-3.954-.138-5.519s-4.03-1.627-5.519-.138L9.786 8.557zm-1.023 6.68c.33.33.863.343 1.177.029l5.34-5.34c.314-.314.3-.846-.03-1.176-.33-.33-.862-.344-1.176-.03l-5.34 5.34c-.314.314-.3.846.03 1.177z" fill-rule="evenodd"&gt;&lt;/path&gt;</span></div></div><div><div><div>25</div></div><div><span><span>                            </span></span><span>&lt;/svg&gt;</span></div></div><div><div><div>26</div></div><div><span><span>                        </span></span><span>&lt;/span&gt;</span></div></div><div><div><div>27</div></div><div><span><span>                        </span></span><span>&lt;a href="</span><span>${</span><span>link</span><span>}</span><span>" title="</span><span>${</span><span>link</span><span>}</span><span>" style="color:inherit;text-decoration:none;"&gt;</span><span>${</span><span>displayLink</span><span>}</span><span>&lt;/a&gt;</span></div></div><div><div><div>28</div></div><div><span><span>                    </span></span><span>&lt;/span&gt;</span></div></div><div><div><div>29</div></div><div><span><span>                </span></span><span>&lt;/span&gt;</span></div></div><div><div><div>30</div></div><div><span><span>                </span></span><span>&lt;span class="LinkCard-imageCell"&gt;</span></div></div><div><div><div>31</div></div><div><span><span>                    </span></span><span>&lt;img class="LinkCard-image" alt="logo" src="</span><span>${</span><span>logourl</span><span>}</span><span>" onerror="this.src='/img/loading/loading1.gif'"&gt;</span></div></div><div><div><div>32</div></div><div><span><span>                </span></span><span>&lt;/span&gt;</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>&lt;/span&gt;`</span><span>; </span><span>// 如果获取指定logo地址失败，则使用默认的loading图片</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>在主题配置文件中引入，此处演示略。</li>
<li>使用方法：</li>
</ol><p>在文章中直接插入：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>a</span><span> </span><span>href</span><span>=</span><span>"你的链接"</span><span> </span><span>name</span><span>=</span><span>"链接的ico地址"</span><span> </span><span>class</span><span>=</span><span>"LinkCard"</span><span>&gt;你的卡片标题&lt;/</span><span>a</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>使用效果：</li>
</ol><p><a href="https://youyeyejie.github.io/_posts/Fluid%E4%B8%BB%E9%A2%98%E7%BE%8E%E5%8C%96" name="https://youyeyejie.github.io/img/avatar/avatar.webp">Fluid主题美化</a></p><p><em><strong>这也是这文的部分内容参考的博客，感谢博主提供的思路，如有侵权，请联系我删除！使用致歉！</strong></em></p></section>
<section><h1>樱花特效<a href="#樱花特效"><span>#</span></a></h1><p>这里直接用来该<a href="https://api.vvhan.com/" target="_blank">网站</a>的 API 接口。并在自定义 js 文件中添加如下内容：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span><span>(</span><span>document</span><span>).</span></span><span>ready</span><span>(</span><span>function</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 判断是否为首页</span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span><span> (</span><span>location</span><span>.</span></span><span>pathname</span><span> </span><span>===</span><span> </span><span>'/'</span><span> </span><span>||</span><span><span> </span><span>location</span><span>.</span></span><span>pathname</span><span> </span><span>===</span><span> </span><span>'/index.html'</span><span> </span><span>||</span><span><span> </span><span>location</span><span>.</span></span><span>pathname</span><span> </span><span>===</span><span> </span><span>'/index'</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>// 加载樱花特效</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$</span><span>.</span><span>getScript</span><span>(</span><span>"https://api.vvhan.com/api/script/yinghua"</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我只在首页展示了该特效，所以在前面加了条件判断。</p></section>
<section><h1>高斯模糊<a href="#高斯模糊"><span>#</span></a></h1><section><h2>1. 设置方法<a href="#1-设置方法"><span>#</span></a></h2><ol>
<li>首先，引入自定义 CSS 文件：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 高斯模糊样式 */</span></div></div><div><div><div>2</div></div><div><span>.blur-text</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>filter: </span><span>blur</span><span>(</span><span><span>5</span><span>px</span></span><span>);</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>transition: filter </span><span><span>0.3</span><span>s</span></span><span><span> </span><span>ease</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>.blur-text</span><span>:hover</span></span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>filter: </span><span>none</span><span>;</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>你可以直接在文章中通过 HTML 标签插入，比如：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>span</span><span> </span><span>class</span><span>=</span><span>"blur-text"</span><span>&gt;这是被模糊的内容&lt;/</span><span>span</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>也可以创建 Hexo 标签插件：
<ol>
<li>在博客根目录创建 <code>scripts</code> 文件夹（如果不存在）；</li>
<li>创建一个新文件 <code>scripts/blur.js</code>，内容如下：</li>
</ol>
</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* 模糊文字标签插件</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* 使用方法: {% blur 这是被模糊的内容 %}</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span><span>hexo</span><span>.</span></span><span>extend</span><span>.</span><span>tag</span><span>.</span><span>register</span><span>(</span><span>'blur'</span><span>, </span><span>function</span><span><span>(</span><span>args</span><span>, </span><span>content</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>var</span><span><span> </span><span>text</span><span> </span></span><span>=</span><span><span> </span><span>args</span><span>.</span></span><span>join</span><span>(</span><span>' '</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>return</span><span> </span><span>'&lt;span class="blur-text"&gt;'</span><span> </span><span>+</span><span><span> </span><span>text</span><span> </span></span><span>+</span><span> </span><span>'&lt;/span&gt;'</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}, {</span><span>ends</span><span>:</span><span> </span><span>false</span><span>});</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/**</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>* 支持多行内容的模糊文字标签插件</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>* 使用方法:</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>* {% blur_block %}</span></div></div><div><div><div>14</div></div><div><span><span> </span></span><span>* 这是被模糊的内容</span></div></div><div><div><div>15</div></div><div><span><span> </span></span><span>* 可以包含多行</span></div></div><div><div><div>16</div></div><div><span><span> </span></span><span>* {% end_blur_block %}</span></div></div><div><div><div>17</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>18</div></div><div><span><span>hexo</span><span>.</span></span><span>extend</span><span>.</span><span>tag</span><span>.</span><span>register</span><span>(</span><span>'blur_block'</span><span>, </span><span>function</span><span><span>(</span><span>args</span><span>, </span><span>content</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span>  </span><span>return</span><span> </span><span>'&lt;div class="blur-text"&gt;'</span><span> </span><span>+</span><span><span> </span><span>hexo</span><span>.</span></span><span>render</span><span>.</span><span>renderSync</span><span>({</span><span>text</span><span>:</span><span><span> </span><span>content</span><span>, </span></span><span>engine</span><span>:</span><span> </span><span>'markdown'</span><span>}) </span><span>+</span><span> </span><span>'&lt;/div&gt;'</span><span>;</span></div></div><div><div><div>20</div></div><div><span>}, {</span><span>ends</span><span>:</span><span> </span><span>true</span><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2. 配置示例（新站不再动态渲染）<a href="#2-配置示例新站不再动态渲染"><span>#</span></a></h2><p>这段内容是正常显示的。</p><p>这段内容被模糊了，鼠标悬停或点击可以查看。</p><p>或者使用标签插件：</p></section></section>
<section><h1>文本黑幕<a href="#文本黑幕"><span>#</span></a></h1><p>与高斯模糊的原理是类似的。</p><section><h2>1. 使用方法<a href="#1-使用方法"><span>#</span></a></h2><ol>
<li>引入自定义 CSS 文件：</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 文本黑幕样式 */</span></div></div><div><div><div>2</div></div><div><span>.shady</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>display: </span><span>inline-block</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>padding: </span><span>0</span><span> </span><span><span>2</span><span>px</span></span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>2</span><span>px</span></span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>cursor: </span><span>pointer</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>.shady</span><span>::before</span></span><span> {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>content: </span><span>""</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>position: </span><span>absolute</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>top: </span><span>0</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>left: </span><span>0</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>width: </span><span><span>100</span><span>%</span></span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>height: </span><span><span>100</span><span>%</span></span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>background-color: </span><span>#</span><span>000</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>border-radius: </span><span><span>2</span><span>px</span></span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>transition: opacity </span><span><span>0.3</span><span>s</span></span><span><span> </span><span>ease</span><span>;</span></span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>z-index: </span><span>1</span><span>;</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span><span>.shady</span><span>:hover::before</span></span><span> {</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>opacity: </span><span>0</span><span>;</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>.shady</span><span> </span><span>&gt;</span><span> </span><span>*</span><span> {</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>z-index: </span><span>0</span><span>;</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>你可以直接在文章中通过 HTML 标签插入，比如：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>span</span><span> </span><span>class</span><span>=</span><span>"shady"</span><span>&gt;这是被黑幕遮挡的内容&lt;/</span><span>span</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>也可以创建 Hexo 标签插件：
<ol>
<li>在博客根目录创建 <code>scripts</code> 文件夹（如果不存在）；</li>
<li>创建一个新文件 <code>scripts/shady.js</code>，内容如下：</li>
</ol>
</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* 文本黑幕标签插件</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* 使用方法: {% shady 这是被黑幕遮挡的内容 %}</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span><span>hexo</span><span>.</span></span><span>extend</span><span>.</span><span>tag</span><span>.</span><span>register</span><span>(</span><span>'shady'</span><span>, </span><span>function</span><span><span>(</span><span>args</span><span>, </span><span>content</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>  </span><span>var</span><span><span> </span><span>text</span><span> </span></span><span>=</span><span><span> </span><span>args</span><span>.</span></span><span>join</span><span>(</span><span>' '</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>return</span><span> </span><span>'&lt;span class="shady"&gt;&lt;span&gt;'</span><span> </span><span>+</span><span><span> </span><span>text</span><span> </span></span><span>+</span><span> </span><span>'&lt;/span&gt;&lt;/span&gt;'</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}, {</span><span>ends</span><span>:</span><span> </span><span>false</span><span>});</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/**</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>* 支持多行内容的文本黑幕标签插件</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>* 使用方法:</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>* {% shady_block %}</span></div></div><div><div><div>14</div></div><div><span><span> </span></span><span>* 这是被黑幕遮挡的内容</span></div></div><div><div><div>15</div></div><div><span><span> </span></span><span>* 可以包含多行</span></div></div><div><div><div>16</div></div><div><span><span> </span></span><span>* {% end_shady_block %}</span></div></div><div><div><div>17</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>18</div></div><div><span><span>hexo</span><span>.</span></span><span>extend</span><span>.</span><span>tag</span><span>.</span><span>register</span><span>(</span><span>'shady_block'</span><span>, </span><span>function</span><span><span>(</span><span>args</span><span>, </span><span>content</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span>  </span><span>return</span><span> </span><span>'&lt;div class="shady"&gt;&lt;div&gt;'</span><span> </span><span>+</span><span><span> </span><span>hexo</span><span>.</span></span><span>render</span><span>.</span><span>renderSync</span><span>({</span><span>text</span><span>:</span><span><span> </span><span>content</span><span>, </span></span><span>engine</span><span>:</span><span> </span><span>'markdown'</span><span>}) </span><span>+</span><span> </span><span>'&lt;/div&gt;&lt;/div&gt;'</span><span>;</span></div></div><div><div><div>20</div></div><div><span>}, {</span><span>ends</span><span>:</span><span> </span><span>true</span><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2. 配置示例（新站不再动态渲染）<a href="#2-配置示例新站不再动态渲染-1"><span>#</span></a></h2><p>这是正常显示的文本。</p><p>这是一段被黑幕遮挡的内容，鼠标悬停可以查看。</p><p>或者使用标签插件：</p></section></section>
<section><h1>插入图表<a href="#插入图表"><span>#</span></a></h1><p>使用 EChart 工具，该工具比较成熟且能绘制的图表多样。</p><section><h2>1. 使用方法<a href="#1-使用方法-1"><span>#</span></a></h2><ol>
<li>安装 <code>hexo-tag-echarts</code> 插件：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-tag-echarts</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>在需要插入图表的文章开头添加以下 js 内容：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 通过jsDelivr的CDN引入echarts</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>script</span><span> </span><span>src</span><span>=</span><span>"https://cdn.jsdelivr.net/npm/echarts@4.8.0/dist/echarts.min.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>// 使用GL里的各种组件时需要添加，否则可不需要</span></div></div><div><div><div>4</div></div><div><span>&lt;</span><span>script</span><span> </span><span>src</span><span>=</span><span>"https://cdn.jsdelivr.net/npm/echarts-gl@1.1.1/dist/echarts-gl.min.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>注</strong>：这里不通过 js 文件直接引入是因为用到的地方比较少，没必要作为必要加载。（博客里塞的自定义小垃圾已经够多了，加上没做优化，尽可能还是少加载一些）</p><ol>
<li>插入图表内容：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{% echarts 400 '85%' %}</span></div></div><div><div><div>2</div></div><div><span>...</span></div></div><div><div><div>3</div></div><div><span>{% endecharts %}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>具体格式说明参考 <a href="https://echarts.apache.org/zh/index.html" target="_blank">Echart官网</a> ；</p></section><section><h2>2. 配置示例（新站不再动态渲染）<a href="#2-配置示例新站不再动态渲染-2"><span>#</span></a></h2><p>这里以折线图为例：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>option</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>title</span><span>:</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>text</span><span>:</span><span> </span><span>'Stacked Area Chart'</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>5</div></div><div><span>  </span><span>tooltip</span><span>:</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>trigger</span><span>:</span><span> </span><span>'axis'</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>axisPointer</span><span>:</span><span> {</span></div></div><div><div><div>8</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'cross'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>label</span><span>:</span><span> {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>backgroundColor</span><span>:</span><span> </span><span>'#6a7985'</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>14</div></div><div><span>  </span><span>legend</span><span>:</span><span> {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>data</span><span>:</span><span> [</span><span>'Email'</span><span>, </span><span>'Union Ads'</span><span>, </span><span>'Video Ads'</span><span>, </span><span>'Direct'</span><span>, </span><span>'Search Engine'</span><span>]</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span>  </span><span>toolbox</span><span>:</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>feature</span><span>:</span><span> {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>saveAsImage</span><span>:</span><span> {}</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>22</div></div><div><span>  </span><span>grid</span><span>:</span><span> {</span></div></div><div><div><div>23</div></div><div><span>    </span><span>left</span><span>:</span><span> </span><span>'3%'</span><span>,</span></div></div><div><div><div>24</div></div><div><span>    </span><span>right</span><span>:</span><span> </span><span>'4%'</span><span>,</span></div></div><div><div><div>25</div></div><div><span>    </span><span>bottom</span><span>:</span><span> </span><span>'3%'</span><span>,</span></div></div><div><div><div>26</div></div><div><span>    </span><span>containLabel</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>28</div></div><div><span>  </span><span>xAxis</span><span>:</span><span> [</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>30</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'category'</span><span>,</span></div></div><div><div><div>31</div></div><div><span>      </span><span>boundaryGap</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>32</div></div><div><span>      </span><span>data</span><span>:</span><span> [</span><span>'Mon'</span><span>, </span><span>'Tue'</span><span>, </span><span>'Wed'</span><span>, </span><span>'Thu'</span><span>, </span><span>'Fri'</span><span>, </span><span>'Sat'</span><span>, </span><span>'Sun'</span><span>]</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>35</div></div><div><span>  </span><span>yAxis</span><span>:</span><span> [</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>37</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'value'</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>],</span></div></div><div><div><div>40</div></div><div><span>  </span><span>series</span><span>:</span><span> [</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>42</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'Email'</span><span>,</span></div></div><div><div><div>43</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'line'</span><span>,</span></div></div><div><div><div>44</div></div><div><span>      </span><span>stack</span><span>:</span><span> </span><span>'Total'</span><span>,</span></div></div><div><div><div>45</div></div><div><span>      </span><span>areaStyle</span><span>:</span><span> {},</span></div></div><div><div><div>46</div></div><div><span>      </span><span>emphasis</span><span>:</span><span> {</span></div></div><div><div><div>47</div></div><div><span>        </span><span>focus</span><span>:</span><span> </span><span>'series'</span></div></div><div><div><div>48</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>49</div></div><div><span>      </span><span>data</span><span>:</span><span> [</span><span>120</span><span>, </span><span>132</span><span>, </span><span>101</span><span>, </span><span>134</span><span>, </span><span>90</span><span>, </span><span>230</span><span>, </span><span>210</span><span>]</span></div></div><div><div><div>50</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>52</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'Union Ads'</span><span>,</span></div></div><div><div><div>53</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'line'</span><span>,</span></div></div><div><div><div>54</div></div><div><span>      </span><span>stack</span><span>:</span><span> </span><span>'Total'</span><span>,</span></div></div><div><div><div>55</div></div><div><span>      </span><span>areaStyle</span><span>:</span><span> {},</span></div></div><div><div><div>56</div></div><div><span>      </span><span>emphasis</span><span>:</span><span> {</span></div></div><div><div><div>57</div></div><div><span>        </span><span>focus</span><span>:</span><span> </span><span>'series'</span></div></div><div><div><div>58</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>59</div></div><div><span>      </span><span>data</span><span>:</span><span> [</span><span>220</span><span>, </span><span>182</span><span>, </span><span>191</span><span>, </span><span>234</span><span>, </span><span>290</span><span>, </span><span>330</span><span>, </span><span>310</span><span>]</span></div></div><div><div><div>60</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>62</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'Video Ads'</span><span>,</span></div></div><div><div><div>63</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'line'</span><span>,</span></div></div><div><div><div>64</div></div><div><span>      </span><span>stack</span><span>:</span><span> </span><span>'Total'</span><span>,</span></div></div><div><div><div>65</div></div><div><span>      </span><span>areaStyle</span><span>:</span><span> {},</span></div></div><div><div><div>66</div></div><div><span>      </span><span>emphasis</span><span>:</span><span> {</span></div></div><div><div><div>67</div></div><div><span>        </span><span>focus</span><span>:</span><span> </span><span>'series'</span></div></div><div><div><div>68</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>69</div></div><div><span>      </span><span>data</span><span>:</span><span> [</span><span>150</span><span>, </span><span>232</span><span>, </span><span>201</span><span>, </span><span>154</span><span>, </span><span>190</span><span>, </span><span>330</span><span>, </span><span>410</span><span>]</span></div></div><div><div><div>70</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>71</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>72</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'Direct'</span><span>,</span></div></div><div><div><div>73</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'line'</span><span>,</span></div></div><div><div><div>74</div></div><div><span>      </span><span>stack</span><span>:</span><span> </span><span>'Total'</span><span>,</span></div></div><div><div><div>75</div></div><div><span>      </span><span>areaStyle</span><span>:</span><span> {},</span></div></div><div><div><div>76</div></div><div><span>      </span><span>emphasis</span><span>:</span><span> {</span></div></div><div><div><div>77</div></div><div><span>        </span><span>focus</span><span>:</span><span> </span><span>'series'</span></div></div><div><div><div>78</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>79</div></div><div><span>      </span><span>data</span><span>:</span><span> [</span><span>320</span><span>, </span><span>332</span><span>, </span><span>301</span><span>, </span><span>334</span><span>, </span><span>390</span><span>, </span><span>330</span><span>, </span><span>320</span><span>]</span></div></div><div><div><div>80</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>81</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>82</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'Search Engine'</span><span>,</span></div></div><div><div><div>83</div></div><div><span>      </span><span>type</span><span>:</span><span> </span><span>'line'</span><span>,</span></div></div><div><div><div>84</div></div><div><span>      </span><span>stack</span><span>:</span><span> </span><span>'Total'</span><span>,</span></div></div><div><div><div>85</div></div><div><span>      </span><span>label</span><span>:</span><span> {</span></div></div><div><div><div>86</div></div><div><span>        </span><span>show</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>87</div></div><div><span>        </span><span>position</span><span>:</span><span> </span><span>'top'</span></div></div><div><div><div>88</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>89</div></div><div><span>      </span><span>areaStyle</span><span>:</span><span> {},</span></div></div><div><div><div>90</div></div><div><span>      </span><span>emphasis</span><span>:</span><span> {</span></div></div><div><div><div>91</div></div><div><span>        </span><span>focus</span><span>:</span><span> </span><span>'series'</span></div></div><div><div><div>92</div></div><div><span><span>      </span></span><span>},</span></div></div><div><div><div>93</div></div><div><span>      </span><span>data</span><span>:</span><span> [</span><span>820</span><span>, </span><span>932</span><span>, </span><span>901</span><span>, </span><span>934</span><span>, </span><span>1290</span><span>, </span><span>1330</span><span>, </span><span>1320</span><span>]</span></div></div><div><div><div>94</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>95</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>96</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>
<section><h1>首页文章滑入动画<a href="#首页文章滑入动画"><span>#</span></a></h1><ol>
<li>在 source/js 目录下新建自定义 JS 文件 <code>scrollanimation.js</code> ，内容如下：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>cards</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelectorAll</span><span>(</span><span>'.index-card'</span><span>)</span></div></div><div><div><div>2</div></div><div><span>if</span><span><span> (</span><span>cards</span><span>.</span></span><span>length</span><span>) {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>document</span><span>.</span><span>querySelector</span><span>(</span><span>'.row'</span><span>).</span><span>setAttribute</span><span>(</span><span>'style'</span><span>, </span><span>'overflow: hidden;'</span><span>)</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>coefficient</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>documentElement</span><span>.</span><span>clientWidth</span><span> </span><span>&gt;</span><span> </span><span>768</span><span> </span><span>?</span><span> </span><span>.5</span><span> </span><span>:</span><span> </span><span>.3</span></div></div><div><div><div>5</div></div><div><span>    </span><span>const</span><span> </span><span>origin</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>documentElement</span><span>.</span><span>clientHeight</span><span> </span><span>-</span><span><span> </span><span>cards</span><span>[</span></span><span>0</span><span>].</span><span>getBoundingClientRect</span><span>().</span><span>height</span><span> </span><span>*</span><span><span> </span><span>coefficient</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>function</span><span> </span><span>throttle</span><span><span>(</span><span>fn</span><span>, </span><span>wait</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>        </span><span>let</span><span><span> </span><span>timer</span><span> </span></span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>9</div></div><div><span>        </span><span>return</span><span> </span><span>function</span><span> () {</span></div></div><div><div><div>10</div></div><div><span>            </span><span>const</span><span> </span><span>context</span><span> </span><span>=</span><span> </span><span>this</span><span>;</span></div></div><div><div><div>11</div></div><div><span>            </span><span>const</span><span> </span><span>args</span><span> </span><span>=</span><span> </span><span>arguments</span><span>;</span></div></div><div><div><div>12</div></div><div><span>            </span><span>if</span><span> (</span><span>!</span><span><span>timer</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span><span>                </span></span><span>timer</span><span> </span><span>=</span><span> </span><span>setTimeout</span><span>(</span><span>function</span><span> () {</span></div></div><div><div><div>14</div></div><div><span><span>                    </span></span><span>fn</span><span>.</span><span>apply</span><span><span>(</span><span>context</span><span>, </span><span>args</span><span>);</span></span></div></div><div><div><div>15</div></div><div><span><span>                    </span></span><span>timer</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>                </span></span><span>}, </span><span>wait</span><span>)</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>function</span><span> </span><span>handle</span><span>() {</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>cards</span><span>.</span><span>forEach</span><span><span>(</span><span>card</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>23</div></div><div><span><span>            </span></span><span>card</span><span>.</span><span>setAttribute</span><span>(</span><span>'style'</span><span>, </span><span>`--state: </span><span>${</span><span><span>(</span><span>card</span></span><span>.</span><span>getBoundingClientRect</span><span>()</span><span>.</span><span>top</span><span> </span><span>-</span><span> </span><span><span>origin</span><span>)</span></span><span> </span><span>&lt;</span><span> </span><span>0</span><span> </span><span>?</span><span> </span><span>1</span><span> </span><span>:</span><span> </span><span>0</span><span>}</span><span>;`</span><span>)</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>document</span><span>.</span><span>addEventListener</span><span>(</span><span>"scroll"</span><span>, </span><span>throttle</span><span><span>(</span><span>handle</span><span>, </span></span><span>100</span><span>));</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>在 source/css 目录下新建自定义样式文件 <code>scrollanimation.css</code> ，内容如下：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.index-card</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>transition: </span><span>all</span><span> </span><span><span>0.5</span><span>s</span></span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>transform: </span><span>scale</span><span>(</span><span>calc</span><span>(</span><span>1.25</span><span><span> </span><span>-</span><span> </span></span><span>0.25</span><span><span> </span><span>*</span><span> </span></span><span>var</span><span>(</span><span>--state</span><span>)));</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>opacity: </span><span>var</span><span>(</span><span>--state</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>margin-bottom: </span><span><span>2</span><span>rem</span></span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>.index-img</span><span> </span><span>img</span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>margin: </span><span><span>20</span><span>px</span></span><span> </span><span>0</span><span>;</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>在配置文件中引入自定义 css 和 js 文件，操作略；</li>
</ol></section>
<section><h1>网页元素悬停放大特效<a href="#网页元素悬停放大特效"><span>#</span></a></h1><ol>
<li>在 source/css 目录下新建自定义样式文件 <code>hover.css</code> ，内容如下：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 首页文章封面 */</span></div></div><div><div><div>2</div></div><div><span>.index-img</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>/* 动画时间 */</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>transition: </span><span><span>.4</span><span>s</span></span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span><span>.index-card</span><span>:hover</span></span><span> </span><span>.index-img</span><span> {</span></div></div><div><div><div>7</div></div><div><span>  </span><span>/* 放大倍数 */</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>transform: </span><span>scale</span><span>(</span><span>1.05</span><span>);</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 头像 */</span></div></div><div><div><div>12</div></div><div><span>.about-avatar</span><span> </span><span>img</span><span> {</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>transition: </span><span><span>.4</span><span>s</span></span><span>;</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div><span><span>.about-avatar</span><span>:hover</span></span><span> </span><span>img</span><span> {</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>transform: </span><span>scale</span><span>(</span><span>1.05</span><span>);</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>/* 标签云 */</span></div></div><div><div><div>20</div></div><div><span>.tagcloud</span><span> </span><span>a</span><span> {</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>transition: </span><span><span>.4</span><span>s</span></span><span>;</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div><span>.tagcloud</span><span> </span><span>a</span><span>:hover</span><span> {</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>transform: </span><span>scale</span><span>(</span><span>1.05</span><span>);</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你可以自由补充你希望悬停放大的网页元素。</p><ol>
<li>引入自定义样式文件，操作略；</li>
</ol></section>
<section><h1>标签页文字跟随焦点切换<a href="#标签页文字跟随焦点切换"><span>#</span></a></h1><p>原理是添加一个对页面可见性事件 <code>visibilitychange</code> 的监听。</p><ol>
<li>在 source/js 目录下新建自定义 JS 文件 <code>visibilitychange.js</code> ，内容如下：</li>
</ol><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>jQuery</span><span><span>(</span><span>document</span><span>).</span></span><span>ready</span><span>(</span><span>function</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>var</span><span><span> </span><span>b</span><span>, </span><span>c</span><span>, </span><span>a</span><span> </span></span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>title</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>var</span><span><span> </span><span>welcomeTimer</span><span> </span></span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>function</span><span> </span><span>d</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>        </span><span>if</span><span><span> (</span><span>document</span><span>[</span><span>b</span><span>]) {</span></span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>document</span><span>.</span><span>title</span><span> </span><span>=</span><span> </span><span>" 你切到别的标签页了 "</span><span>;</span></div></div><div><div><div>7</div></div><div><span>            </span><span>if</span><span><span> (</span><span>welcomeTimer</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>                </span><span>clearTimeout</span><span><span>(</span><span>welcomeTimer</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span><span>                </span></span><span>welcomeTimer</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>document</span><span>.</span><span>title</span><span> </span><span>=</span><span> </span><span>" 你在当前标签页 "</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>welcomeTimer</span><span> </span><span>=</span><span> </span><span>setTimeout</span><span>(</span><span>function</span><span>() {</span></div></div><div><div><div>14</div></div><div><span><span>                </span></span><span>document</span><span>.</span><span>title</span><span> </span><span>=</span><span><span> </span><span>a</span><span>;</span></span></div></div><div><div><div>15</div></div><div><span><span>            </span></span><span>}, </span><span>2000</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>document</span><span>.</span></span><span>hidden</span><span> </span><span>!==</span><span> </span><span>"undefined"</span><span>) {</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>b</span><span> </span><span>=</span><span> </span><span>"hidden"</span><span>;</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>c</span><span> </span><span>=</span><span> </span><span>"visibilitychange"</span><span>;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>document</span><span>.</span></span><span>mozHidden</span><span> </span><span>!==</span><span> </span><span>"undefined"</span><span>) {</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>b</span><span> </span><span>=</span><span> </span><span>"mozHidden"</span><span>;</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>c</span><span> </span><span>=</span><span> </span><span>"mozvisibilitychange"</span><span>;</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>document</span><span>.</span></span><span>webkitHidden</span><span> </span><span>!==</span><span> </span><span>"undefined"</span><span>) {</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>b</span><span> </span><span>=</span><span> </span><span>"webkitHidden"</span><span>;</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>c</span><span> </span><span>=</span><span> </span><span>"webkitvisibilitychange"</span><span>;</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>if</span><span> ((</span><span>typeof</span><span><span> </span><span>document</span><span>.</span></span><span>addEventListener</span><span> </span><span>!==</span><span> </span><span>"undefined"</span><span> </span><span>||</span><span> </span><span>typeof</span><span><span> </span><span>document</span><span>[</span><span>b</span><span>] </span></span><span>!==</span><span> </span><span>"undefined"</span><span>) </span><span>&amp;&amp;</span><span><span> </span><span>c</span><span>) {</span></span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>document</span><span>.</span><span>addEventListener</span><span><span>(</span><span>c</span><span>, </span><span>d</span><span>, </span></span><span>false</span><span>);</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>33</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>引入自定义 JS 文件，操作略；</li>
</ol></section>
<section><h1>后记<a href="#后记"><span>#</span></a></h1><p>持续更新中。没什么技术含量，主要是一些小构思。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/macbook-setup-guide-from-scratch/</id>
      <title type="text">从零开始的MacBook配置指南</title>
      <published>2025-06-19T01:39:00.000Z</published>
      <updated>2025-06-19T01:39:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/macbook-setup-guide-from-scratch/"/>
      <summary type="text">从这台正式加入我的三件套，完成了苹果全家桶的最后一块拼图，取代联想拯救者 Y7000P 2020H成为我的工作主力已经有近一年了（拯救者已经基本吃灰了）。故特此记录一下一些使用心得与软件推荐。不过使用的偏好与习惯因人而异，适合自己的才是最好，当然从 Win 转 Mac 操作系统，一般都得适应一段时间。</summary>
      <content type="html"><![CDATA[<p>从这台正式加入我的三件套，完成了苹果全家桶的最后一块拼图，取代<strong>联想拯救者 Y7000P 2020H</strong>成为我的工作主力已经有近一年了（拯救者已经基本吃灰了）。故特此记录一下一些使用心得与软件推荐。不过使用的偏好与习惯因人而异，适合自己的才是最好，当然从 Win 转 Mac 操作系统，一般都得适应一段时间。</p>
<div><div><div></div><div>Warning</div></div><div><p>本文中所涉及的软件等，本人不会提供任何破解版的安装方法或地址，并请不要在评论区留言涉及有关内容。如有能力，请尽量支持正版！</p></div></div>
<section><h1>一、基本设置<a href="#一基本设置"><span>#</span></a></h1><p>以下是个人的一些习惯性设置，选择性参考即可。</p><section><h2>1.1 键盘设置<a href="#11-键盘设置"><span>#</span></a></h2><p>关于输入法，哥们就是传统拼音 + 英文输入法，如果你习惯打五笔或者双拼，可以在<strong>系统偏好设置 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 键盘 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 输入法 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 编辑 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 点击左下角的加号，添加所需输入法</strong>。</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154907578.png" alt="" /></p><p>另外，推荐你在<strong>系统偏好设置 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 键盘 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 输入法 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 编辑 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 关闭“连按两下空格键插入句号”</strong>。</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154910830.png" alt="" /></p><p>其余设置如下，基本没改：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154917194.png" alt="" /></p></section><section><h2>1.2 触控板设置<a href="#12-触控板设置"><span>#</span></a></h2><p>MacBook 的优势之一其实就是它牛波一的触控板，用惯了之后你就不习惯用鼠标了（笑）。</p><ol>
<li>启用<strong>轻点来点按</strong>：系统偏好设置 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 触控板 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 光标与点按 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 启用轻点来点按；</li>
<li>更多手势设置如下：</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154921375.png" alt="" /></p><p>用惯双指和三指了你就会明白触控板的好了。</p></section></section>
<section><h1>二、日常软件<a href="#二日常软件"><span>#</span></a></h1><section><h2>2.1 安装网络工具<a href="#21-安装网络工具"><span>#</span></a></h2><p>在国内，安装软件的第一步，肯定是先安装上网工具。以下是一些推荐工具，有些安装地址就不放了，自己发挥主观能动性吧。</p><ul>
<li><strong>ClashX or Clash Pro（推荐！）</strong>：适用于 MacOS 的 Clash 系列；</li>
<li><strong>V2rayN</strong>：适合新手，上手容易吧；</li>
</ul><p>关于配置等问题，可以参考胖橙佬的文章 <a href="https://js2panda.com/clash-how-to" target="_blank">Clash 新手完美使用指南</a> ；</p></section><section><h2>2.2 浏览器<a href="#22-浏览器"><span>#</span></a></h2><ul>
<li><strong><a href="http://www.apple.com/cn/safari/" target="_blank">Safari</a></strong> —— 苹果自带的其实就挺好用的，只不过我更喜欢谷歌；</li>
<li><strong>Google</strong> ，可以直接通过 3.2 中的 HomeBrew Cask 直接安装：</li>
</ul><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>brew</span><span> </span><span>install</span><span> </span><span>--cask</span><span> </span><span>google-chrome</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>顺便推荐一些浏览器插件：</p><ul>
<li><strong><a href="https://chromewebstore.google.com/detail/%E7%AF%A1%E6%94%B9%E7%8C%B4/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">Tampermonkey</a></strong> —— 大名鼎鼎的油🐒插件；</li>
<li><strong><a href="https://chromewebstore.google.com/detail/%E6%B2%89%E6%B5%B8%E5%BC%8F%E7%BF%BB%E8%AF%91-%E7%BD%91%E9%A1%B5%E7%BF%BB%E8%AF%91%E6%8F%92%E4%BB%B6-pdf%E7%BF%BB%E8%AF%91-%E5%85%8D%E8%B4%B9/bpoadfkcbjbfhfodiogcnhhhpibjhbnh" target="_blank">沉浸式翻译</a></strong> —— 网页翻译，好用！</li>
<li><strong><a href="https://chromewebstore.google.com/detail/adguard-%E5%B9%BF%E5%91%8A%E6%8B%A6%E6%88%AA%E5%99%A8/bgnkhhnnamicmpeenaelnjfhikgbkllg" target="_blank">AdGuard</a></strong> —— 广告拦截器，还可以屏蔽一些暗链；</li>
<li><strong><a href="https://chromewebstore.google.com/detail/gitzip-for-github/ffabmkklhbepgcgfonabamgnfafbdlkn" target="_blank">GitZip</a></strong> —— 一个支持下载仓库内的指定文件的插件；</li>
<li><strong><a href="https://chromewebstore.google.com/detail/icloud-%E5%AF%86%E7%A0%81/pejdijmoenmkgeppbflobdenhhabjlaj" target="_blank">iCloud密码</a></strong> —— 你都用苹果了不用 iCloud ？</li>
</ul></section><section><h2>2.3 截图工具<a href="#23-截图工具"><span>#</span></a></h2><ul>
<li><strong><a href="https://pasteapp.io/" target="_blank">Paste</a></strong>：支持截图、滚动截图、标注和 OCR ，唯一缺点就是付费；</li>
</ul></section><section><h2>2.4 菜单栏管理<a href="#24-菜单栏管理"><span>#</span></a></h2><ul>
<li></li>
</ul></section></section>
<section><h1>三、开发配置<a href="#三开发配置"><span>#</span></a></h1><section><h2>3.1 HomeBrew<a href="#31-homebrew"><span>#</span></a></h2><p><a href="https://brew.sh/" target="_blank">Homebrew</a> 是 MacOS 上的包管理工具（类似 Windows 上的 Scoop），可以用来安装各种软件，甚至字体，并且<strong>无需像 Win 一样配置环境变量</strong>。伟大，无需多言。</p><p>官网提供的安装方式如下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>/bin/bash</span><span> </span><span>-c</span><span> </span><span>"$(</span><span>curl</span><span> </span><span>-fsSL</span><span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>安装完成后，推荐配置以下镜像源，有时候 HomeBrew 还是挺慢的，将以下内容添加进 <code>~/.zprofile</code> 即可：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>HOMEBREW_BREW_GIT_REMOTE</span><span>=</span><span>"https://mirrors.ustc.edu.cn/brew.git"</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>HOMEBREW_CORE_GIT_REMOTE</span><span>=</span><span>"https://mirrors.ustc.edu.cn/homebrew-core.git"</span></div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>HOMEBREW_BOTTLE_DOMAIN</span><span>=</span><span>"https://mirrors.ustc.edu.cn/homebrew-bottles"</span></div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>HOMEBREW_API_DOMAIN</span><span>=</span><span>"https://mirrors.ustc.edu.cn/homebrew-bottles/api"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>正确配置后，<code>HOMEBREW_API_DOMAIN</code> 会把包安装信息的地址设置成镜像地址，如下图：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154927889.png" alt="" /></p></section><section><h2>3.2 HomeBrew Cask<a href="#32-homebrew-cask"><span>#</span></a></h2><p>Homebrew Cask 允许你使用命令行安装 macOS 应用。比如你可以这样安装 Chrome：<code>brew install --cask google-chrome</code>。不过它是社区驱动的，提供的应用不一定是最新版本，目前 Homebrew Cask 已经和 Homebrew 深度集成，不需要单独安装了。</p></section><section><h2>3.3 终端配置 —— iTerm2<a href="#33-终端配置--iterm2"><span>#</span></a></h2><p>很多人觉得苹果的原生终端不好用且不好看（毕竟默认白色和个记事本一样），我也尝试了很多别的第三方终端工具，总体体验下来最终选择了 <strong><a href="https://iterm2.com/" target="_blank">iTerm2</a></strong> ，相比于原生终端提供了更多快捷的功能和特性。我使用的是 <a href="https://github.com/romkatv/powerlevel10k" target="_blank">powerlevel10k</a> 主题，效果如下：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154931725.png" alt="" /></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/journey-through-computer-networks-scenario-edition/</id>
      <title type="text">漫游计算机网络之情景篇</title>
      <published>2025-06-10T09:00:00.000Z</published>
      <updated>2025-06-10T09:00:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/journey-through-computer-networks-scenario-edition/"/>
      <summary type="text">PSTN ，即 Public Switched Telephone Network ，公用交换电话网。 交换局和交换局之间，使用光纤连接。 中继线上传输的是数字信号，其中用到了时分复用技术，包括： 用户家里的电话和电脑都通过电话线，其中，电脑的数字信号先经过 ADSL Modem 转换为模拟信号，然后和语音信号一起经…</summary>
      <content type="html"><![CDATA[<section><h1>QoS 的一个例子：保证转发<a href="#qos-的一个例子保证转发"><span>#</span></a></h1><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155048303.png" alt="" /></p><ol>
<li>所有的业务被划分为4个优先级；</li>
<li>通过 Traffic Policer 的令牌桶机制进行流量整形；</li>
<li>在经过 Traffic Policer 时，每个优先级下又被分类为3种优先级，或者说丢弃优先级，即丢包的可能性大小；</li>
<li>所以最终实际上有12种服务等级；</li>
<li>在每个 Packet 的 IP Header 的 <strong>ToS 字段(Type of Service 服务类型)</strong> 会携带 DSCP(6bits) + ECN(2bits)，其中：
<ul>
<li><strong>DSCP(Differentiated Services Code Point 服务等级)</strong>；</li>
<li><strong>ECN(Explicit Congestion Notification 拥塞控制)</strong>；</li>
</ul>
</li>
<li>路由器接收到被划分后的包，根据以下2种机制进行分组调度：
<ul>
<li><strong>WFQ(Weighted Fair Queuing 加权公平队列)</strong>：按照优先级进行公平调度；</li>
<li><strong>RED(Random Early Detection 随机早期检测)</strong>：采用主动丢弃机制，这里被丢弃的概率就是前面划分的丢弃优先级；</li>
</ul>
</li>
</ol></section>
<section><h1>PSTN 公用交换电话网<a href="#pstn-公用交换电话网"><span>#</span></a></h1><p>PSTN ，即 Public Switched Telephone Network ，公用交换电话网。</p><section><h2>1. 本地环路 Local Loop<a href="#1-本地环路-local-loop"><span>#</span></a></h2><ul>
<li>进行模拟信号传输；</li>
<li>使用<strong>双绞线</strong>，从家到端局；</li>
<li>设备包括 CodeC, Modem or ADSL Modem ；</li>
<li>早期只包括电话线（300 Hz ～ 3400 Hz），即模拟信号的传输；后期由于加入用户电脑等设备，需要传送数字信号，所以<strong>先通过 Modem ，再通过 CodeC</strong> ，如下图所示；</li>
</ul><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155055169.png" alt="" /></p>

<table><thead><tr><th><strong>比较点</strong></th><th><strong>Modem（拨号）</strong></th><th><strong>ADSL Modem</strong></th></tr></thead><tbody><tr><td>功能</td><td>拨号上网，将数字信号调制成适合带宽传输的模拟信号</td><td>拨号上网，将数字信号调制成适合带宽传输的模拟信号</td></tr><tr><td>传输介质</td><td>UTP 3 ，即普通电话线</td><td><strong>UTP 3 ，即普通电话线</strong></td></tr><tr><td>信道带宽</td><td>整个电话线频段 3100 Hz（需要引入滤波器，将带宽限制在语音范围）</td><td>1.1 MHz（无需滤波器）</td></tr><tr><td>工作层次</td><td>物理层</td><td>物理层</td></tr><tr><td>通信方式</td><td>全双工，以字符为单位</td><td>全双工，以字符为单位，PPP 协议</td></tr><tr><td>调制速率</td><td><strong>2400 Baud</strong></td><td><strong>4000 Baud</strong></td></tr><tr><td>调制技术</td><td>QAM</td><td>DMT-离散多音调制技术（QAM 的升级版）</td></tr><tr><td>信号</td><td>模拟信号</td><td>模拟信号</td></tr><tr><td>复用技术</td><td>无复用</td><td><strong>频分复用 FDM</strong></td></tr></tbody></table></section><section><h2>2. 中继 Trunk<a href="#2-中继-trunk"><span>#</span></a></h2><p>交换局和交换局之间，使用<strong>光纤</strong>连接。</p><p>中继线上传输的是数字信号，其中用到了时分复用技术，包括：</p>

<table><thead><tr><th><strong>项目</strong></th><th><strong>T1</strong></th><th><strong>E1</strong></th></tr></thead><tbody><tr><td>标准组织</td><td>Bell Labs（美国）</td><td>ITU-T（国际电信联盟）</td></tr><tr><td>使用地区</td><td>美国、日本、加拿大</td><td>欧洲、中国等大部分其他地区</td></tr><tr><td>传输速率</td><td>1.544 Mbps</td><td>2.048 Mbps</td></tr><tr><td>时隙数量</td><td>24 个时隙（channels）</td><td>32 个时隙</td></tr><tr><td>每个时隙速率</td><td>64 Kbps（语音标准）</td><td>64 Kbps（语音标准）</td></tr><tr><td>可传语音通道数</td><td>24 路</td><td>30/32 路（其中 2 个为控制与同步）</td></tr><tr><td>帧结构</td><td>每帧 193 bit（8×24 + 1）</td><td>每帧 256 bit（8×32）</td></tr></tbody></table></section><section><h2>3. 综述<a href="#3-综述"><span>#</span></a></h2><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155104936.png" alt="" />
<img src="https://webp-pic.yokumi.cn/2026/01/20260101155109031.png" alt="" /></p><p>用户家里的电话和电脑都通过电话线，其中，电脑的数字信号先经过 ADSL Modem 转换为模拟信号，然后和语音信号一起经过 Splitter ，将频段分离，经过用户线后，到达端局，和其他 DSL 用户的信号一起经过 DSLAM 集中汇集，再转发到互联网。</p></section></section>
<section><h1>数据包分片过程<a href="#数据包分片过程"><span>#</span></a></h1><p>数据链路层（以太网）、网络层（IP 协议）、传输层（TCP 协议）事实上都涉及分片的操作。</p><ol>
<li>应用层的数据交给传输层后，这部分数据即 TCP 的有效载荷；</li>
<li>TCP 是面向字节流的，故有效载荷不能超过 MSS Bytes ；TCP 首先对数据进行了一次分片，分成不同的 TCP 报文；</li>
<li>TCP 并对其进行封装，加上至少 20 Bytes 的 TCP Header ；</li>
<li>交给网络层后，网络层同样对其进行封装，加上 20 Bytes ～ 60 Bytes 的IP Header ；</li>
<li>这样，IP Header + TCP Header + Payload 已经完成封装，交给数据链路层，作为 Frame 的 Data 部分；</li>
<li>数据链路层为其加上 18 Bytes 的控制字段，封装成帧，最终在物理层中实际传输；</li>
</ol><p>我们知道，对于以太网，MTU = 1500 Bytes ，即最大传输单元（不包含控制字段），所以反推出 MSS 不能超过 1500 - 20 - 20 = 1460 Bytes ；</p><section><h2>问题：MSS 似乎并没有必要，只需 MTU 即可？<a href="#问题mss-似乎并没有必要只需-mtu-即可"><span>#</span></a></h2><p>并非如此，网络层是不可靠的，服务尽力而为，并没有像 TCP 一样高效的重传机制。不考虑 MTU ，让 IP 层直接负责对 TCP 报文的分片，会发生什么呢？</p><p>一个 TCP 报文，直接被 IP 层划分为多个 IP 包，传输过程中，如果其中 1 个 IP 包分片丢失，根据 IP 协议，就需要重传所有的 IP 包分片；</p><p>看起来好像没什么问题，但是 IP 协议哪里有重传这一说，重传是通过上层，即 TCP 协议来控制的，由于某个分片丢子，IP 包无法进行重组，自然不会交给上层，接收方的传输层根本不知道，所以发送方的传输层需要在超时后才能进行重传。那么效率是可想而知的低的。</p><p>引入 MSS 后，事实上确保了一个 TCP 报文不会被 IP 协议分片，即 1 个 IP 包对应 1 个 TCP 报文。往往在 TCP 建立连接的过程中，双方会协商好 MSS 的大小；</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/journey-through-computer-network-devices/</id>
      <title type="text">漫游计算机网络之设备篇</title>
      <published>2025-06-09T13:01:00.000Z</published>
      <updated>2025-06-09T13:01:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/journey-through-computer-network-devices/"/>
      <summary type="text">简单来说，网关是一种能连接两个不同网络的设备，它在网络层或更高层起作用，负责协议转换、地址转换、路由转发等功能。 你可以简单的理解为，所有网关都是路由器，不过路由器并不一定是网关。 NAT ，即 Network Address Translation ，网络地址转换。</summary>
      <content type="html"><![CDATA[<section><h1>网络层及以上<a href="#网络层及以上"><span>#</span></a></h1><section><h2>1. 网关 Gateway<a href="#1-网关-gateway"><span>#</span></a></h2><p>简单来说，<strong>网关是一种能连接两个不同网络的设备，它在网络层或更高层起作用，负责协议转换、地址转换、路由转发等功能。</strong></p><p>你可以简单的理解为，所有网关都是路由器，不过路由器并不一定是网关。</p><p>几种简单的类型包括：</p><ul>
<li><strong>默认网关（Default Gateway）</strong>：连接子网与外部网络的设备（通常是路由器）；
<ul>
<li>比如你在家里笔记本电脑需要通过家里的局域网访问互联网，你家的路由器的 IP 地址就是你的默认网关；</li>
</ul>
</li>
<li><strong>应用层网关（Application Gateway）</strong>：邮件网关，HTTP 代理服务器等；</li>
</ul></section><section><h2>2. 路由器 Router<a href="#2-路由器-router"><span>#</span></a></h2><p>不讲了。</p></section><section><h2>3. NAT<a href="#3-nat"><span>#</span></a></h2><section><h3>3.1 NAT 是什么？<a href="#31-nat-是什么"><span>#</span></a></h3><p>NAT ，即 Network Address Translation ，网络地址转换。是在<strong>路由器或防火墙</strong>上进行的技术，一般需要专门支持 NAT 的路由器，称之为 <strong>NAT Router</strong> 。</p></section><section><h3>3.2 NAT 有什么用？<a href="#32-nat-有什么用"><span>#</span></a></h3><p><strong>把私有 IP 地址转换为公有 IP 地址（或反之）</strong> 的技术，使得多个私有网络设备可以通过一个或少量公网地址访问互联网。</p><p><strong>注</strong>：也可能将 TCP/UDP 的端口号一起进行转换。</p><p>私有地址包括：</p><ul>
<li>10.0.0.0 ～ 10.255.255.255/8；</li>
<li>172.16.0.0 ~ 172.31.255.255/12；</li>
<li>192.168.0.0 ~ 192.168.255.255/16；</li>
</ul></section><section><h3>3.3 工作过程<a href="#33-工作过程"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155235703.png" alt="" /></p></section><section><h3>3.4 常见 NAT 类型<a href="#34-常见-nat-类型"><span>#</span></a></h3><ul>
<li>动态 NAT 表项：
<ul>
<li>当内部主机需要访问公网时才建立 NAT 表项；</li>
<li>动态创建和回收（连接关闭或长久未使用时回收）；</li>
<li>私有 IP 从公网 IP 池中动态选取一个进行映射，动态创建表项；</li>
</ul>
</li>
<li>静态 NAT 表项：
<ul>
<li>手动创建并配置表项，一个私有 IP 对应一个固定公网 IP ；</li>
<li>映射关系固定；</li>
</ul>
</li>
</ul></section><section><h3>3.5 评价<a href="#35-评价"><span>#</span></a></h3><ul>
<li>优点：
<ul>
<li>节省了 IPv4 地址；</li>
<li>增加了内网安全性；</li>
<li>更灵活；</li>
</ul>
</li>
<li>缺点：
<ul>
<li>作为网络层，更改了传输层的端口，打破了端到端的通信；</li>
<li>只是 IPv6 协议的过渡方案；</li>
</ul>
</li>
</ul></section></section></section>
<section><h1>数据链路层<a href="#数据链路层"><span>#</span></a></h1><section><h2>1. NIC 网卡<a href="#1-nic-网卡"><span>#</span></a></h2><p>网络接口卡，Network Interface Card ，简称网卡，是计算机或其他网络设备与网络进行通信的硬件接口。</p><section><h3>1.1 功能<a href="#11-功能"><span>#</span></a></h3><p>网卡负责实现物理层和数据链路层的协议功能，包括：</p><ol>
<li>发送与接收帧</li>
<li>实现 MAC 地址识别与封装</li>
<li>执行数据链路层和物理层的一些功能</li>
<li>提供驱动程序供操作系统使用</li>
</ol></section><section><h3>1.2 常见类型<a href="#12-常见类型"><span>#</span></a></h3><ul>
<li>以太网网卡：最常见，用于连接局域网，通过网线（RJ45）；</li>
<li>无线网卡：实现 Wi-Fi，无需网线，常见于笔记本；</li>
</ul></section><section><h3>1.3 网卡号<a href="#13-网卡号"><span>#</span></a></h3><p>网卡号，即<strong>网络接口卡（NIC）的硬件地址 —— 也就是 MAC 地址</strong>。</p><p>全球唯一，由网卡厂商在出厂时烧录。作用范围是同一局域网。</p></section></section><section><h2>2. Switch 交换机<a href="#2-switch-交换机"><span>#</span></a></h2><section><h3>2.1 功能<a href="#21-功能"><span>#</span></a></h3><p>交换机（Switch）是一种工作在 <strong>数据链路层</strong> 的网络设备，它根据每台主机的 <strong>MAC 地址</strong> 来决定将数据帧<strong>转发到哪个端口</strong>，从而高效地连接多个局域网设备。</p>

<table><thead><tr><th><strong>功能</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td><strong>帧转发</strong></td><td>根据 MAC 地址决定数据应发往哪个端口</td></tr><tr><td><strong>MAC 学习功能</strong></td><td>自动学习每台设备的 MAC 所在的端口，并建立 MAC 地址表</td></tr><tr><td><strong>广播隔离</strong></td><td>只将帧发往目的主机对应端口，而不是所有端口（不像 Hub）</td></tr><tr><td><strong>全双工通信</strong></td><td>支持同时收发，提高带宽效率</td></tr><tr><td><strong>冲突域隔离</strong></td><td>每个端口是独立冲突域，避免传统 Hub 的冲突问题</td></tr></tbody></table><p>相较于 Hub ，其优点主要就是支持全双工通信，并且隔离了冲突域。</p></section><section><h3>2.2 类型<a href="#22-类型"><span>#</span></a></h3><ul>
<li><strong>直通式（Cut-Through）</strong>：边接收数据帧边根据帧头的目的地址直接转发；
<ul>
<li>无法检查错误；</li>
<li>输入输出速率相同；</li>
<li>延迟低；</li>
</ul>
</li>
<li><strong>存储转发式（Store-and-Forward）</strong>：缓存整个数据帧，检查数据包是否正确，过滤掉碰撞的残帧和 CRC 错误帧，再根据目的 MAC 地址转发；
<ul>
<li>支持差错检测；</li>
<li>支持不同输入/输出端口的交换；</li>
<li>延迟高；</li>
</ul>
</li>
</ul></section><section><h3>2.3 交换机的转发表<a href="#23-交换机的转发表"><span>#</span></a></h3>

<table><thead><tr><th><strong>字段</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td><strong>MAC 地址</strong></td><td>设备的硬件地址，用于唯一标识局域网中的主机</td></tr><tr><td><strong>端口号（接口）</strong></td><td>MAC 地址所在的交换机端口号</td></tr><tr><td><strong>时间戳 / 老化计时器</strong></td><td>用于判断该项是否长期未被使用，控制老化删除</td></tr></tbody></table><p>有些高级交换机还会记录 VLAN ID，用于支持 VLAN 分布式转发。</p><p>当交换机的某个端口接收到一个以太网帧时，执行 MAC 学习过程：</p><ol>
<li>查看该帧的源 MAC 地址；</li>
<li>如果该 MAC 地址不在表中，或端口发生变化；</li>
<li>则在转发表中添加 / 更新该 MAC 对应的端口号与时间戳；</li>
</ol><p>这其实和网桥的学习过程是很像的。</p><p>为了防止转发表无限增大或信息过期，交换机会在：</p><ul>
<li>某个 MAC 地址在一段时间（比如 300 秒）没有出现；</li>
<li>该项对应的端口断开或设备下线；</li>
</ul><p>此时会触发：<strong>老化机制（Aging Mechanism）</strong>，删除这项。</p></section></section><section><h2>3. Bridge 网桥<a href="#3-bridge-网桥"><span>#</span></a></h2><p>网桥，用于连接多个局域网，并进行帧的存储和转发，延迟增加。即插即用。</p><p><strong>注</strong>：网桥隔离了冲突域，但并未隔离广播域，<strong>无法防止广播风暴</strong>！</p><p><strong>广播域 Broadcast Domain</strong>：站点发送广播帧时，收到该帧的站点与其处在同一个广播域。连接在一个交换机、网桥或集线器的所有站点同处一个广播域。</p><section><h3>3.1 学习网桥<a href="#31-学习网桥"><span>#</span></a></h3><section><h4>3.1.1 后向学习 Backward Learning<a href="#311-后向学习-backward-learning"><span>#</span></a></h4><p>用于构造转发表，主要涉及<strong>泛洪 Flooding</strong> 、<strong>丢弃 Discard</strong> 、<strong>转发 Forwarding</strong> 三种操作。将帧中的源 MAC 地址和输入端口的映射关系加入到转发表。</p></section><section><h4>3.1.2 生成树网桥<a href="#312-生成树网桥"><span>#</span></a></h4><p>断环，构造无环树。</p></section></section><section><h3>3.2 网桥 vs. 交换机<a href="#32-网桥-vs-交换机"><span>#</span></a></h3>

<table><thead><tr><th><strong>对比项</strong></th><th><strong>网桥（Bridge）</strong></th><th><strong>交换机（Switch）</strong></th></tr></thead><tbody><tr><td><strong>端口数量</strong></td><td>少（2~4 个）</td><td>多（通常为 24、48 个）</td></tr><tr><td><strong>学习实现方式</strong></td><td>主要基于<strong>软件</strong>实现，效率较低</td><td>使用 ASIC（专用集成电路）<strong>硬件</strong>转发，高效快速</td></tr><tr><td><strong>MAC 表容量</strong></td><td>小（适用于小型网络）</td><td>大（适用于大型复杂网络）</td></tr><tr><td><strong>处理</strong></td><td>一次只能处理一帧</td><td>可以并行处理多路</td></tr><tr><td><strong>转发机制</strong></td><td>软件查表+转发（存储转发）</td><td>硬件查表+线速转发（可以使用直通式）</td></tr><tr><td><strong>延迟和吞吐量</strong></td><td>较高延迟，低吞吐</td><td>低延迟，高吞吐（适合现代多业务网络）</td></tr><tr><td><strong>管理和功能</strong></td><td>简单，通常不支持 VLAN、QoS 等高级功能</td><td>丰富，支持 VLAN、QoS、端口镜像、生成树等</td></tr><tr><td><strong>协议支持</strong></td><td>支持生成树协议（STP）</td><td>支持生成树 + 多种扩展（RSTP、MSTP 等）</td></tr></tbody></table></section></section></section>
<section><h1>物理层<a href="#物理层"><span>#</span></a></h1><section><h2>1. Hub 集线器<a href="#1-hub-集线器"><span>#</span></a></h2><ul>
<li>物理上星型，逻辑上总线型，即冲突域仍包括和 Hub 连接的所有设备；</li>
<li>参数 <code>100 Base -T X</code> 含义：
<ul>
<li>100 ：100 Mbps，传输速率；</li>
<li>Base ：使用基带传输，不调制；</li>
<li>T ：双绞线，传输媒介；</li>
<li>X ：编码方式，一般采用 Manchester 编码；</li>
</ul>
</li>
</ul></section><section><h2>2. Repeater 中继器<a href="#2-repeater-中继器"><span>#</span></a></h2><p>一句话，就是<strong>接收、放大、再生信号，并将其转发到更远的物理距离</strong>，从而扩展网络的传输范围。</p>

<table><thead><tr><th><strong>功能</strong></th><th><strong>描述</strong></th></tr></thead><tbody><tr><td><strong>放大信号</strong></td><td>补偿信号在传输过程中的衰减（如电缆越长越弱）</td></tr><tr><td><strong>再生信号</strong></td><td>还原数字信号的波形，使其更“干净”，减少误码率</td></tr><tr><td><strong>扩展物理距离</strong></td><td>使网络电缆可超出单段长度限制，例如 Ethernet 的 100 米限制</td></tr></tbody></table><ul>
<li>半双工；</li>
<li>用于将两个总线型的以太网连接；</li>
<li>中继器只是“看到信号波形”，并进行电气层面的复制和放大，不识别具体内容；</li>
</ul><p><strong>冲突域 Collision Domain</strong>：两个站点不能同时发送数据，则这两个站点属于一个冲突域。<strong>连接在一个集线器（或者用中继器 repeater 连接的多个网段）的所有站点属于一个冲突域</strong>。</p></section><section><h2>3. Transponder 转发器<a href="#3-transponder-转发器"><span>#</span></a></h2><p>卫星通信中使用，监听地面的上行电波，经过放大后再以另一种频率的下行电波广播给地面天线。</p></section><section><h2>4. 数字通信系统的有关设备<a href="#4-数字通信系统的有关设备"><span>#</span></a></h2><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155247753.png" alt="" /></p><section><h3>4.1 模拟信号数字化 —— CodeC<a href="#41-模拟信号数字化--codec"><span>#</span></a></h3><p><strong>信源编/解码器 CodeC</strong>：模拟信号 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 数字信号；</p><p>采样、量化、编码，e.g. PCM（Pulse Code Modulation 脉码调制），采样率为 <span><span>8000/s8000/s</span><span><span><span></span><span>8000/</span><span>s</span></span></span></span> ，每秒采样 <span><span>88</span><span><span><span></span><span>8</span></span></span></span> bits ，数据率/带宽为 64 kbps；</p></section><section><h3>4.2 数字基带信号传输 —— 信道编码器<a href="#42-数字基带信号传输--信道编码器"><span>#</span></a></h3><p>和加密器一起，主要是给数字序列加一些冗余位等。</p></section><section><h3>4.3 带通信号传输 —— Modem 调制器<a href="#43-带通信号传输--modem-调制器"><span>#</span></a></h3><p>Modem ，调制器，用于将基带信号转换为带通信号。</p></section><section><h3>4.4 多路信号复用到同一信道 —— Multiplexer 多路复用器<a href="#44-多路信号复用到同一信道--multiplexer-多路复用器"><span>#</span></a></h3><p>多路复用器，使用多路复用技术，来提高信道利用率，包括：</p><ul>
<li>TDM 时分复用：
<ul>
<li>STDM 同步时分复用，e.g. PCM ；</li>
<li>ATDM 统计/异步时分复用，e.g. 介质访问控制；</li>
</ul>
</li>
<li>FDM 频分复用：e.g. ADSL 非对称数字用户线；</li>
<li>WDM 波分复用；</li>
<li>CDM 码分复用；</li>
</ul></section></section><section><h2>4.4 ADSL Modem<a href="#44-adsl-modem"><span>#</span></a></h2><p>详见<a href="/posts/journey-through-computer-networks-scenario-edition/">《漫游计算机网络之情景篇》</a>。</p></section><section><h2>4.5 DSLAM<a href="#45-dslam"><span>#</span></a></h2><p>DSLAM ，全称 Digital Subscriber Line Access Multiplexer ，即数字用户线接入复用器，属于<strong>局端设备</strong>。是电信运营商用来<strong>将多个 DSL 用户的信号集中接入、并转发到互联网的设备</strong>。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/journey-through-computer-networking-technologies/</id>
      <title type="text">漫游计算机网络之技术篇</title>
      <published>2025-06-09T05:50:00.000Z</published>
      <updated>2025-06-09T05:50:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/journey-through-computer-networking-technologies/"/>
      <summary type="text">其实完全可以放在《协议篇》里，确信。不过为方便对比分析，所以单开一篇 来水 了。 即答：数据链路层 and 传输层； ARQ 技术，即 Automatic Repeat Request ，自动重传请求。当物理层出现传输误码时，数据链路层需要对错误进行恢复。</summary>
      <content type="html"><![CDATA[<p>其实完全可以放在《协议篇》里，确信。不过为方便对比分析，所以单开一篇 来水 了。</p>
<section><h1>Retransmission 重传机制<a href="#retransmission-重传机制"><span>#</span></a></h1><section><h2>1. 概述<a href="#1-概述"><span>#</span></a></h2><ol>
<li>哪些层需要进行重传？</li>
</ol><p>即答：数据链路层 and 传输层；</p></section><section><h2>2. 数据链路层的重传<a href="#2-数据链路层的重传"><span>#</span></a></h2><p>ARQ 技术，即 Automatic Repeat Request ，自动重传请求。当物理层出现传输误码时，数据链路层需要对错误进行恢复。由于一般采用检错码，所以需要发送方对出错的数据进行重传，并且重传是发送方自主完成的。</p></section><section><h2>3. 传输层的重传<a href="#3-传输层的重传"><span>#</span></a></h2><p>本质上也用到了 ARQ ，但使用了一些机制用于优化重传。</p><section><h3>3.1 超时重传<a href="#31-超时重传"><span>#</span></a></h3><p>就是普通的重传，<strong>重传计时器 RTO 超时</strong>，重传对应的数据包。</p></section><section><h3>3.2 快速重传<a href="#32-快速重传"><span>#</span></a></h3><p>优化前者，当发送方连续收到 3 个重复的 ACK ，它就知道该包可能丢了，于是无需等待该包的 RTO 超时就直接进行重传。</p></section><section><h3>3.3 SACK 选择确认<a href="#33-sack-选择确认"><span>#</span></a></h3><p>接收方在 TCP Header 的选项字段添加 SACK ，告诉发送方自己已经接收到哪些数据，这样发送方就只需重传特定的数据。</p></section><section><h3>3.4 Duplicate SACK<a href="#34-duplicate-sack"><span>#</span></a></h3><p>简单来说，就是让发送方知道自己是否发送了重复的数据包。</p></section></section></section>
<section><h1>Timer 定时器管理<a href="#timer-定时器管理"><span>#</span></a></h1><section><h2>1. 概述<a href="#1-概述-1"><span>#</span></a></h2><ol>
<li>哪些层有定时器</li>
</ol><p>事实上，除了物理层之外，其上每一层都有定时器，只不过有些并不明确把它当作一个定时器，但是事实上也发挥了定时器的功能。</p></section><section><h2>2. 数据链路层的定时器<a href="#2-数据链路层的定时器"><span>#</span></a></h2><ol>
<li><strong>Frame Timer 帧计时器</strong>：每一个帧在发送时启动一个对应的重传计时器，若超时还没收到 ACK 则重传该帧；</li>
<li><strong>ACK Timer ACK 计时器</strong>：在收到帧后不直接回送 ACK ，如果在 ACK_Timer 超时前有数据需要发送，则捎带 ACK ，若超时，则单独发送一个 ACK 帧；用于延时确认，减少了单独的 ACK 帧数量，提高了网络效率</li>
</ol><p>无线通信中，虚拟载波监听技术还有 <strong>NAV 网络分配向量</strong> ，站点维护一个 NAV ，表示需要退避的时间。</p></section><section><h2>3. 网络层的计时器<a href="#3-网络层的计时器"><span>#</span></a></h2><p>比如 IPV4 包的 TTL 字段， ICMP 报文的超时反馈也涉及 TTL ，部分路由协议中的周期性广播等。</p></section><section><h2>4. 传输层（TCP 协议）的计时器<a href="#4-传输层tcp-协议的计时器"><span>#</span></a></h2><p>传输层的计时器以 TCP 协议为主，TCP 协议也是计时器最复杂的协议之一。</p><ul>
<li><strong>Retransmission Timer 重传计时器</strong>：即 RTO ，超时重传计时；</li>
<li><strong>Persistence Timer 持续计时器</strong>：用于周期性地进行窗口探测，防止窗口为 0 的死锁；</li>
<li><strong>Keepalive Timer 保活计时器</strong>：当另一方始终静默时，发送一个包试探其是否还处于通信状态，防止半开连接；</li>
<li><strong>Time-Waited Timer 等待时间计时器</strong>：
<ul>
<li>Time-waited State：当主动关闭连接的一方（即先发 FIN 的那一方）在完成 TCP 四次挥手后，会进入一个叫做 <code>TIME_WAIT</code> 的状态；</li>
<li>该状态通常会持续一段时间，其值一般为 <code>TIME_WAIT = 2 × MSL（Maximum Segment Lifetime）</code>，即最大报文生存时间的 2 倍；</li>
<li>作用一是确保对方正确收到了 ACK ，双方都正确关闭连接（为自己第一次发送的 ACK 丢失，对方重发 FIN ，自己重新回 ACK 预留时间）；</li>
<li>作用二是防止混入新的连接，确保连接已经完全关闭；</li>
</ul>
</li>
</ul><section><h3>4.1 RTO 计时器的设置<a href="#41-rto-计时器的设置"><span>#</span></a></h3><p>由于网络情况顺序万变，RTO 本来略大于 RTT 即可，但是 RTT 动态变化，所以 RTO 也需要进行相应调整。</p><section><h4>SRTT 平滑 RTT 计算<a href="#srtt-平滑-rtt-计算"><span>#</span></a></h4><p>SRTT = <span><span>α\alpha</span><span><span><span></span><span>α</span></span></span></span> <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> SRTT <span><span>++</span><span><span><span></span><span>+</span></span></span></span> <span><span>(1−α)(1 - \alpha)</span><span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>α</span><span>)</span></span></span></span> <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> New_Sample ，一般取 <span><span>α=78\alpha = \frac{7}{8}</span><span><span><span></span><span>α</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>8</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>7</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ；</p></section><section><h4>RTTVAR RTT 偏差计算<a href="#rttvar-rtt-偏差计算"><span>#</span></a></h4><p>RTTVAR = <span><span>β\beta</span><span><span><span></span><span>β</span></span></span></span> <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> RTTVAR + <span><span>(1−β)(1-\beta)</span><span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>β</span><span>)</span></span></span></span> <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> |SRTT - Sample| ，一般取 <span><span>β=34\beta = \frac{3}{4}</span><span><span><span></span><span>β</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>4</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>3</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ；</p></section><section><h4>RTO 计算<a href="#rto-计算"><span>#</span></a></h4><p>RTO = SRTT + 4 <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> RTTVAR ；</p></section></section></section></section>
<section><h1>信道共享算法<a href="#信道共享算法"><span>#</span></a></h1><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155111192.png" alt="" /></p><p>我们仅看时分复用的部分：</p><ul>
<li>静态信道分配算法（同步时分复用）：将信道划分为多个子信道使用，e.g. <strong>PCM</strong>；</li>
<li>动态信道分配算法（异步时分复用）：
<ul>
<li>受控多路访问控制：
<ul>
<li>轮询 Polling（中心式控制）；</li>
<li>令牌 Token（分布式控制）；</li>
</ul>
</li>
<li>随机多路访问控制：
<ul>
<li>ALOHA ；</li>
<li>CSMA ；</li>
<li>CSMA/CD ；</li>
<li>CSMA/CA ；</li>
</ul>
</li>
</ul>
</li>
</ul><section><h2>1. 评价信道共享算法的指标<a href="#1-评价信道共享算法的指标"><span>#</span></a></h2><p><strong>低负载情况下的时延</strong>和<strong>高负载下的吞吐量（或信道利用率）</strong>；</p><p><strong>注</strong>：低负载下适合用竞争的方法（即 1-persistent CSMA）；而 p 越小，负载高时，发送帧的随机化越好，冲突越小，吞吐量越高；</p></section><section><h2>2. ALOHA<a href="#2-aloha"><span>#</span></a></h2><section><h3>2.1 Pure ALOHA<a href="#21-pure-aloha"><span>#</span></a></h3><ul>
<li>任何时刻想发就发；</li>
<li>没收到 ACK ，则随机等待一段时间，再进行重传；</li>
<li>脆弱期 = <span><span>2ttrans2t_{trans}</span><span><span><span></span><span>2</span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>t</span><span>r</span><span>an</span><span>s</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
</ul><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155114564.png" alt="" /></p></section><section><h3>2.2 Slotted ALOHA 时隙 ALOHA<a href="#22-slotted-aloha-时隙-aloha"><span>#</span></a></h3><ul>
<li>只能在每个时隙的开始时刻发送数据；</li>
<li>需要统一的时钟管理；</li>
<li>脆弱期 = <span><span>ttranst_{trans}</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>t</span><span>r</span><span>an</span><span>s</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
</ul><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155119487.png" alt="" /></p></section></section><section><h2>3. CSMA 载波监听多路访问<a href="#3-csma-载波监听多路访问"><span>#</span></a></h2><ul>
<li>发前先听：
<ul>
<li>若信道空闲则以一定概率发送；</li>
<li>发信道正忙则等待直至空闲；</li>
<li>发出一段时间后没收到 ACK ，则重传；</li>
</ul>
</li>
<li>脆弱期 = <span><span>tpropt_{prop}</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>p</span><span>r</span><span>o</span><span>p</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
</ul><section><h3>3.1 常见类型<a href="#31-常见类型"><span>#</span></a></h3><ul>
<li>Nonpersistant CSMA 如果监听到信道正忙，那它会等待一个随机时间后在进行监听；
<ul>
<li>高负载时，有较高的吞吐量；</li>
</ul>
</li>
<li>Persistant CSMA 如果监听到信道正忙，会<strong>持续监听</strong>到信道上没有信号，然后以一定的概率 <span><span>pp</span><span><span><span></span><span>p</span></span></span></span> 发送；
<ul>
<li>1 - Persistant CSMA：<span><span>p=1p = 1</span><span><span><span></span><span>p</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span>，即监听到信道上没有信号就发送；
<ul>
<li>低负载时，能有较低的时延和较高的吞吐量；</li>
<li>高负载时，吞吐量较低；</li>
</ul>
</li>
<li>p - Persistant CSMA：以一定的概率 <span><span>pp</span><span><span><span></span><span>p</span></span></span></span> 发送；</li>
</ul>
</li>
</ul><p>简单来说，当 <span><span>pp</span><span><span><span></span><span>p</span></span></span></span> 较小时，高负载情况下，有较好的随机化，冲突概率小，表现更好。</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155123319.png" alt="" /></p></section></section><section><h2>4. CSMA/CD 带冲突检测的 CSMA<a href="#4-csmacd-带冲突检测的-csma"><span>#</span></a></h2><section><h3>4.1 特点<a href="#41-特点"><span>#</span></a></h3><ul>
<li><strong>半双工 Half Duplex</strong> ：即同一个站点同一时刻只能作为发送方或接收方；</li>
<li><strong>经典以太网</strong>使用；</li>
</ul></section><section><h3>4.2 基本步骤<a href="#42-基本步骤"><span>#</span></a></h3><ol>
<li>载波监听；</li>
<li><strong>发送方</strong>检测冲突；</li>
<li>冲突检测：当发送方检测到冲突时，停止传输，并发送一个 <strong>Jam Signal(强化信号)</strong>；</li>
<li><strong>backoff 退避</strong>：当发送方接收到 <strong>Jam 强化信号</strong>后，等待一个随机时间在恢复发送；（目的是避免两个发送方同时检测到冲突并进行退避后，监听到信道空闲又发送导致再一次冲突）</li>
</ol></section><section><h3>4.3 一些注意点<a href="#43-一些注意点"><span>#</span></a></h3><ul>
<li><strong>发送方</strong>来检测冲突；</li>
<li>如何检测？
<ul>
<li>通过一条收线，从总线上读取数据，比较是否与自己发送出去的数据相同；</li>
<li><strong>不适用于无线通信</strong>，原因：
<ul>
<li>无线通信收到的信号可能比原信号弱很多；</li>
<li>无线通信的信号分为不同的频段，站点并不一定能收到全部的频段；</li>
<li>隐藏终端和暴露终端问题；</li>
</ul>
</li>
</ul>
</li>
<li><strong>发现冲突的时间</strong> = <span><span>2tprop2t_{prop}</span><span><span><span></span><span>2</span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>p</span><span>r</span><span>o</span><span>p</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
<li>采用<strong>随机指数退避</strong>；
<ul>
<li>一般最多尝试 16 次，超过则认为失败；</li>
<li>指数达到 10 后不再增加；</li>
<li>基础退避时间 = 51.2 us ；</li>
</ul>
</li>
</ul></section></section><section><h2>5. CSMA/CA<a href="#5-csmaca"><span>#</span></a></h2><section><h3>5.1 无线通信中的问题<a href="#51-无线通信中的问题"><span>#</span></a></h3><section><h4>5.1.1 Hidden Terminal 隐藏终端<a href="#511-hidden-terminal-隐藏终端"><span>#</span></a></h4><p>隐藏终端问题是与正在通信的工作站距离较远时无法判断其正在通信，此时发数据会在接收站产生干扰，如下图：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155139248.png" alt="" /></p></section><section><h4>5.1.2 Exposed Terminal 暴露终端<a href="#512-exposed-terminal-暴露终端"><span>#</span></a></h4><p>暴露终端问题是与正在通信的工作站距离较近但与接收站点距离较远，此时发数据不会产生干扰，但是暴露终端会错误的认为存在干扰。</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155143633.png" alt="" /></p></section><section><h4>5.1.3 解决方法 —— MACA<a href="#513-解决方法--maca"><span>#</span></a></h4><p>MACA ，即 Multiple Access Collision Avoidance ，冲突避免多路访问。通过 <strong>RTS</strong> 和 <strong>CTS</strong> 帧来预约信道，也可以理解为是一个握手的过程。</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155145893.png" alt="" /></p><p>简单来说，暴露终端只会收到 RTS ，隐藏终端只会收到 CTS ，都会保持沉默。一种特殊情况是两个站点同时向同一个站点发送 RTS ，此时目的站点先回复先收到的 RTS ，另一个发送站点发现一定时间未收到 CTS ，则采用指数退避。</p><p><strong>注</strong>：MACA 只是早期提出的一种理论，基于 RTS / CTS 来避免碰撞，并非标准。</p></section></section><section><h3>5.2 CSMA/CA 的工作流程<a href="#52-csmaca-的工作流程"><span>#</span></a></h3><p>对于发送方：</p><ol>
<li>通过载波监听等待直到信道空闲，并在等待 <strong>DIFS</strong> 的时间（即在 DIFS 时间内信道空闲，才认为信道空闲，能发送数据）；</li>
<li>随机退避（0 ～ 15 个时间槽），如果在退避时间内信道变为忙碌，设备会停止退避并持续监听，等到信道再次空闲并经过 DIFS 时间后，重新启动退避计时器，直到退避时间结束且信道空闲才发送数据；</li>
<li>如果没有收到 ACK ，那么采用指数退避，加倍退避时间；</li>
</ol><p>对于接收方：</p><ol>
<li>如果正确接收到帧，则等待 <strong>SIFS</strong> 的时间后回复ACK；</li>
</ol><p><strong>注</strong>：<span><span>tSIFS&lt;tDIFSt_{SIFS} &lt; t_{DIFS}</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>S</span><span>I</span><span>F</span><span>S</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&lt;</span><span></span></span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>D</span><span>I</span><span>F</span><span>S</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，这能保证 <strong>ACK 的优先级更高</strong>；事实上，可以通过为不同类型的帧设置不同长度的 <strong>IFS 帧间隔</strong>，来实现<strong>优先级</strong>服务；</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155149342.png" alt="" /></p></section></section></section>
<section><h1>Error Control 差错控制<a href="#error-control-差错控制"><span>#</span></a></h1><section><h2>1. 哪些层需要差错控制？<a href="#1-哪些层需要差错控制"><span>#</span></a></h2><p>数据链路层 and 传输层；</p><p><strong>注</strong>：网络层中，IPv4 Header 虽然也有 Checksum 字段，但它只对头部做校验，确保路由器转发正确，并不校验数据部分，也不进行重传，只是尽力而为，所以没有差错控制。到 IPv6 直接把该字段舍去了。</p></section><section><h2>2. 数据链路层的差错控制<a href="#2-数据链路层的差错控制"><span>#</span></a></h2><section><h3>2.1 Error Detection 检错码<a href="#21-error-detection-检错码"><span>#</span></a></h3><p>仅加上有限的冗余位，用于接收方判断是否出错，但没有纠错能力。</p><ul>
<li>奇偶校验码（Parity check）；</li>
<li>校验和（Checksum）；</li>
<li>循环冗余校验码（CRC）；</li>
</ul><p>会计算 CRC ：</p><ul>
<li>看给出的多项式<span><span>G(x)G(x)</span><span><span><span></span><span>G</span><span>(</span><span>x</span><span>)</span></span></span></span>的最高位<span><span>rr</span><span><span><span></span><span>r</span></span></span></span>，则在数据位后添加<span><span>rr</span><span><span><span></span><span>r</span></span></span></span>个<span><span>00</span><span><span><span></span><span>0</span></span></span></span>后再进行除法；</li>
<li>这里的除法不同于常规的多项式除法，是<em><strong>模2除法，即按位异或</strong></em>；</li>
</ul></section><section><h3>2.2 Error Correction 纠错码<a href="#22-error-correction-纠错码"><span>#</span></a></h3><p>纠错码是指在发送端在发送的信息中采用相关算法加上足够多的冗余信息，使接收端不 仅可以判断接收的信息有误码，还可纠正出现的错误。</p><section><h4>2.2.1 纠正单比特错误的汉明编码<a href="#221-纠正单比特错误的汉明编码"><span>#</span></a></h4><ol>
<li>首先计算需要的校验位位数：</li>
</ol><ul>
<li><span><span>mm</span><span><span><span></span><span>m</span></span></span></span>：原始信息长度；</li>
<li><span><span>rr</span><span><span><span></span><span>r</span></span></span></span>：check bits，即需要增加的校验位长度；</li>
<li><span><span>nn</span><span><span><span></span><span>n</span></span></span></span>：码字长度，即<span><span>n=m+rn = m + r</span><span><span><span></span><span>n</span><span></span><span>=</span><span></span></span><span><span></span><span>m</span><span></span><span>+</span><span></span></span><span><span></span><span>r</span></span></span></span>；</li>
</ul><p>根据原始信息长度，可推出存在 <span><span>2m2^m</span><span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span></span></span></span></span></span></span></span> 合法信息，每一个合法性信息按照码字传输，会产生单比特错误的信息 <span><span>nn</span><span><span><span></span><span>n</span></span></span></span> 个，所以共存在 <span><span>(n+1)2m(n + 1)2^m</span><span><span><span></span><span>(</span><span>n</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span>)</span><span><span>2</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span></span></span></span></span></span></span></span> 个码字。</p><p>为确保能纠正单比特错误，需要保证 <span><span>(n+1)2m≤2n⇒m+r+1≤2r(n+1)2^m\le2^n \Rightarrow m + r + 1\le 2^r</span><span><span><span></span><span>(</span><span>n</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span>)</span><span><span>2</span><span><span><span><span><span><span></span><span><span>m</span></span></span></span></span></span></span></span><span></span><span>≤</span><span></span></span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span></span></span></span></span><span></span><span>⇒</span><span></span></span><span><span></span><span>m</span><span></span><span>+</span><span></span></span><span><span></span><span>r</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span>r</span></span></span></span></span></span></span></span></span></span></span> ，即合法的码字和不合法的码字不重叠；</p><ol>
<li>计算校验位（偶检验 or 奇检验）并在 2 的幂次位置插入校验位；</li>
</ol></section></section><section><h3>2.3 检错和纠错能力<a href="#23-检错和纠错能力"><span>#</span></a></h3><p>检错和纠错能力与汉明距离有关。</p><ul>
<li>汉明距离 = <span><span>d+1d + 1</span><span><span><span></span><span>d</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span> <span><span>⇒\Rightarrow</span><span><span><span></span><span>⇒</span></span></span></span> 可以检查出 <span><span>dd</span><span><span><span></span><span>d</span></span></span></span> 比特错误；</li>
<li>汉明距离 = <span><span>2d+12d + 1</span><span><span><span></span><span>2</span><span>d</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span> <span><span>⇒\Rightarrow</span><span><span><span></span><span>⇒</span></span></span></span> 可以纠正 <span><span>dd</span><span><span><span></span><span>d</span></span></span></span> 比特错误；</li>
</ul></section></section><section><h2>3. 传输层的差错控制<a href="#3-传输层的差错控制"><span>#</span></a></h2><p>主要就是 TCP 和 UDP 报头中的 Checksum 字段。</p></section></section>
<section><h1>Flow Control 流量控制<a href="#flow-control-流量控制"><span>#</span></a></h1><section><h2>1. 概述<a href="#1-概述-2"><span>#</span></a></h2><ol>
<li>哪些层需要流量控制？</li>
</ol><p>即答：数据链路层 and 传输层；</p><ol>
<li>流量控制的作用？</li>
</ol><p>即答：对于数据链路层，防止链路发送方淹没接收方；
对于传输层，防止发送主机淹没接收主机；</p><ol>
<li>传输层和数据链路层的流量控制有什么区别？</li>
</ol>

<table><thead><tr><th><strong>比较项</strong></th><th><strong>数据链路层流量控制</strong></th><th><strong>传输层流量控制</strong></th></tr></thead><tbody><tr><td><strong>作用范围</strong></td><td>相邻两个节点（点到点）</td><td>端到端（主机到主机）</td></tr><tr><td><strong>主要对象</strong></td><td>控制链路发送方别淹没接收方</td><td>控制发送主机别淹没接收主机</td></tr><tr><td><strong>典型协议/机制</strong></td><td>HDLC 和 PPP 协议的滑动窗口机制、Gbit 以太网Pause帧</td><td>TCP 滑动窗口</td></tr><tr><td><strong>依赖的上下文</strong></td><td>链路速度、网卡缓存</td><td>应用层速率、操作系统缓存</td></tr><tr><td><strong>是否感知整体路径</strong></td><td>只感知本地链路</td><td>感知整条路径的接收端</td></tr><tr><td><strong>是否跨路由器</strong></td><td>不跨路由器</td><td>会跨多跳路由器</td></tr><tr><td><strong>可靠性机制</strong></td><td>有时包含确认和重传</td><td>包含确认、重传、窗口管理</td></tr></tbody></table></section><section><h2>2. 数据链路层的流量控制<a href="#2-数据链路层的流量控制"><span>#</span></a></h2><section><h3>2.1 滑动窗口机制<a href="#21-滑动窗口机制"><span>#</span></a></h3><p>主要包括基本的停等协议，GBN 协议和 SR 协议。考点的话一般包括滑动窗口大小的计算、序列号所需比特数的计算以及对应协议效率的计算。</p>

<table><thead><tr><th>机制</th><th>停等协议</th><th>1-bit 滑动窗口</th><th>GBN 协议</th><th>SR 协议</th></tr></thead><tbody><tr><td>通信模式</td><td>Simplex</td><td>Duplex</td><td>Duplex</td><td>Duplex</td></tr><tr><td>Seq Bit</td><td>1</td><td>1</td><td>n</td><td>n</td></tr><tr><td><span><span>WTW_T</span><span><span><span></span><span><span>W</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></td><td>1</td><td>1</td><td>&gt;1</td><td>一般默认 = <span><span>2n−12^{n-1}</span><span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span><span>n</span><span>−</span><span>1</span></span></span></span></span></span></span></span></span></span></span></span></td></tr><tr><td><span><span>WRW_R</span><span><span><span></span><span><span>W</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></td><td>1</td><td>1</td><td>=<span><span>2n−12^n-1</span><span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span>1</span></span></span></span></td><td>= <span><span>2n−12^{n-1}</span><span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span><span>n</span><span>−</span><span>1</span></span></span></span></span></span></span></span></span></span></span></span></td></tr></tbody></table><p><strong>注</strong>：需要满足 <span><span>WT+WR≤2nW_T + W_R \le 2^n</span><span><span><span></span><span><span>W</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span>W</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>≤</span><span></span></span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span></span></span></span></span></span></span></span> ；</p></section><section><h3>2.2 协议效率计算<a href="#22-协议效率计算"><span>#</span></a></h3><ul>
<li>发送时间，或称传输实验，即数据装载到信道上的时间为 <span><span>ttranst_{trans}</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>t</span><span>r</span><span>an</span><span>s</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
<li>传播时延为 <span><span>tpropt_{prop}</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>p</span><span>r</span><span>o</span><span>p</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
<li>令 <span><span>α=tpropttrans\alpha = \frac{t_{prop}}{t_{trans}}</span><span><span><span></span><span>α</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>t</span><span>r</span><span>an</span><span>s</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>p</span><span>r</span><span>o</span><span>p</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ；</li>
<li>单个帧的有效传输时间即为 <span><span>ttranst_{trans}</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>t</span><span>r</span><span>an</span><span>s</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ，采用 ACK 确认需要花费的总时延为 <span><span>ttrans+2tpropt_{trans} + 2t_{prop}</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>t</span><span>r</span><span>an</span><span>s</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>2</span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>p</span><span>r</span><span>o</span><span>p</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ，即单独的 ACK 帧较小，传输时间可以忽略；</li>
<li>当采用 Piggybacking 机制时，由于 ACK 帧和数据一起发送，传输时间并不能忽略；</li>
</ul><ol>
<li>仅采用 ACK 确认：<span><span>U=min{100%,WT1+2α}U = min\{100\%, \frac{W_T}{1 + 2\alpha}\}</span><span><span><span></span><span>U</span><span></span><span>=</span><span></span></span><span><span></span><span>min</span><span>{</span><span>100%</span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>1</span><span>+</span><span>2</span><span>α</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span><span>W</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>}</span></span></span></span> ；</li>
<li>采用捎带确认时，<span><span>U=min{100%,WT2+2α}U = min\{100\%, \frac{W_T}{2 + 2\alpha}\}</span><span><span><span></span><span>U</span><span></span><span>=</span><span></span></span><span><span></span><span>min</span><span>{</span><span>100%</span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span><span>+</span><span>2</span><span>α</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span><span>W</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>}</span></span></span></span> ；</li>
<li>以上均未考虑有错信道；</li>
</ol></section><section><h3>2.3 G 比特以太网中的流量控制<a href="#23-g-比特以太网中的流量控制"><span>#</span></a></h3><p>进化到 G 比特以太网（全双工通信时）后，由于传输速率过快，接收方极短的 CPU 忙碌就会造成大量帧的堆积，占满缓冲区，接收方此时会向发送方发送一个 Pause 帧，发送方收到后，暂停数据发送。</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155154263.png" alt="" /></p></section></section><section><h2>3. TCP 的流量控制<a href="#3-tcp-的流量控制"><span>#</span></a></h2><p>TCP 的滑动窗口大小并不是帧的数量，而是面向字节的，以字节数为单位，题目中一般认为窗口大小 = n <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> MSS Bytes ；同时 TCP 的窗口大小是动态变化的，适用于多样和复杂的网络环境。</p><section><h3>问题：流量控制和拥塞控制有何区别？<a href="#问题流量控制和拥塞控制有何区别"><span>#</span></a></h3><p>首先，流量控制仅发生在发送方和接收方之间，<strong>流量控制是确保让发送方发送的数据不至于淹没接收方，能根据接收方的实际接收能力发送数据</strong>。</p><p>拥塞控制考虑整个网络的拥塞情况，网络拥堵，TCP 包无法到达，进行重传，进一步加重拥堵，所以在网络拥塞时，同样需要发送方调整发送速率，<strong>避免发送方的发送的数据填满整个网络，加重网络负担</strong>。</p></section><section><h3>3.1 工作机制<a href="#31-工作机制"><span>#</span></a></h3><p>窗口滑动的操作与数据链路层是类似的。关于动态调整窗口大小的过程，简单来说，当接收方收到 TCP 报文后，存入操作系统缓冲区，不过受限于上层的处理能力，并不一定一次性读取所有数据并前移接受窗口，如果当前接受窗口减小了，会在回复的 ACK 包中的 Window Size 字段指明接收方的处理能力，发送方在收到 ACK 包后，根据接受窗口的大小调整发送速率。</p></section><section><h3>3.2 隐患一、窗口关闭的死锁<a href="#32-隐患一窗口关闭的死锁"><span>#</span></a></h3><section><h4>3.2.1 病症<a href="#321-病症"><span>#</span></a></h4><p>试想如果接收方的应用层处理能力太逊了，窗口大小减着减着减到 0 了，此时接收方通知发送方窗口为 0 关闭，此时发送方不能再发送数据。</p><p>当然，当接收方窗口恢复时，它会发送 Window Update 包通知发送方可以继续发送数据，但是这个包并不需要发送方确认，如果丢了，发送方认为接收方窗口仍关闭，接收方认为发送方确实没数据要发，两边就死锁了。</p></section><section><h4>3.2.2 解决方案：窗口探测包<a href="#322-解决方案窗口探测包"><span>#</span></a></h4><p>一句话：<strong>Window Probe 包 + Persistence Timer 持续计时器</strong>！</p><p>在接收方窗口关闭之后，发送方会周期性（即 Persistence Timer 的时限）地向接收方发送一个 Window Probe 包，试探接收方是否在线，直到收到接收方的回复。</p></section></section><section><h3>3.3 隐患二、糊涂窗口综合症<a href="#33-隐患二糊涂窗口综合症"><span>#</span></a></h3><section><h4>3.3.1 病症<a href="#331-病症"><span>#</span></a></h4><ol>
<li>和上述情形类似，接收方应用层的处理能力有限，导致接收窗口不断减小，并不断通知发送方，使得发送方不断发送小包，浪费带宽。</li>
<li>也有可能发送方的应用层需要发送的数据就是小包。</li>
</ol></section><section><h4>3.3.2 解决方案：Nagle &amp;&amp; Clark 算法<a href="#332-解决方案nagle--clark-算法"><span>#</span></a></h4><ol>
<li><strong>发送方：Nagle 算法，即延迟发送小包</strong>
<ul>
<li>当满足以下条件才发送窗口数据：
<ul>
<li>收到之前发出的包的 ACK 帧；</li>
<li>缓存的小包大小达到发送窗口大小的一半；</li>
<li>缓存的小包大小超过 MSS ；</li>
</ul>
</li>
<li>需要接收方配合，不回复小包的 ACK ；</li>
</ul>
</li>
<li><strong>接收方：Clark 算法，即延迟通知窗口更新</strong>
<ul>
<li>当满足以下条件才通知窗口更新：
<ul>
<li>接受窗口的大小超过 MSS 长度；</li>
<li>接受窗口的大小超过一半，即一半空闲了；</li>
</ul>
</li>
</ul>
</li>
</ol></section></section></section></section>
<section><h1>Congestion Control 拥塞控制<a href="#congestion-control-拥塞控制"><span>#</span></a></h1><section><h2>1. 概述<a href="#1-概述-3"><span>#</span></a></h2><ol>
<li>哪些层需要拥塞控制？</li>
</ol><p>即答：网络层 and 传输层；</p><ol>
<li>拥塞控制的作用？</li>
</ol><p>即答：都是为了减轻网络的拥塞状态；</p><ol>
<li>网络层和传输层的拥塞控制有何区别？</li>
</ol>

<table><thead><tr><th><strong>比较项</strong></th><th><strong>网络层</strong></th><th><strong>传输层</strong></th></tr></thead><tbody><tr><td>控制位置</td><td>路由器</td><td>通信两端</td></tr><tr><td>控制对象</td><td>链路拥塞、队列</td><td>数据发送速率</td></tr><tr><td>控制手段</td><td>丢包、标记、负载均衡</td><td>慢启动、窗口调整等</td></tr><tr><td>控制范围</td><td>局部链路</td><td>整体连接</td></tr><tr><td>是否需要路由器支持</td><td>是</td><td>否（也不能否定吧，路由器标记 ECN 也是间接来通知两端的传输层）</td></tr></tbody></table><p>虽说控制范围不同，但本质上都作用于整个网络，防止整个网络过于拥塞而“雪崩”。</p></section><section><h2>2. 网络层的拥塞控制<a href="#2-网络层的拥塞控制"><span>#</span></a></h2><section><h3>2.1 概述<a href="#21-概述"><span>#</span></a></h3><p>网络层事实上的主要作用是 <strong>“感知和通知”</strong> 拥塞，不过也有控制手段，主要防止网络层中的链路或节点（主要是路由器）被数据淹没，手段要么及其简单粗暴（粗粒度的），要么就是间接调控。主要分为以下 5 种；</p><ol>
<li><strong>网络供给 Networking Provision</strong>；</li>
<li><strong>业务感知路由 Traffic aware-routing</strong>；</li>
<li><strong>准入控制 Admission control</strong>；</li>
<li><strong>流量限制 Traffic throttling</strong>；</li>
<li><strong>负载掉落 Load shedding</strong>；</li>
</ol><ul>
<li>前 2 种属于增加资源的操作，后 3 种属于减少负载的操作；</li>
<li>前 3 种属于预防性措施，后 2 种属于反应性措施；</li>
</ul></section><section><h3>2.2 流量限制/业务量调节 Traffic throttling<a href="#22-流量限制业务量调节-traffic-throttling"><span>#</span></a></h3><ol>
<li>检测拥塞：通过链路利用率、队列长度、丢包数量或估计队列延时；</li>
<li>通知拥塞：
<ol>
<li>路由器直接向发送方发送 <strong>Choke Packet 抑制分组</strong>；</li>
<li><strong>显示拥塞通知</strong>：IP 协议来操作！将 IP 包中的 ECN 字段置位，会间接通知传输层；</li>
<li><strong>隐式拥塞通知</strong>：详见 TCP 协议，通过超时 Timeout 来判断；</li>
</ol>
</li>
<li>响应拥塞：发送方通过调整发送窗口调整发送速率；</li>
</ol></section><section><h3>2.3 负载掉落 Load shedding<a href="#23-负载掉落-load-shedding"><span>#</span></a></h3><section><h4>2.3.1 RED 随机早期检测<a href="#231-red-随机早期检测"><span>#</span></a></h4><p>RED ，即 Random Early Detection ，顾名思义，就是<strong>路由器在完全失效前，就开始随机丢弃队列中的一部分分组</strong>。</p></section></section></section><section><h2>3. 传输层的拥塞控制<a href="#3-传输层的拥塞控制"><span>#</span></a></h2><section><h3>3.1 概述<a href="#31-概述"><span>#</span></a></h3><p>相较于前者，传输层的拥塞控制就比较细致了，并且它事实上也使用到了 IP 协议的部分功能，以下是几种典型机制。</p></section><section><h3>3.2 一些泛用的机制<a href="#32-一些泛用的机制"><span>#</span></a></h3><section><h4>3.2.1 最大最小公平性<a href="#321-最大最小公平性"><span>#</span></a></h4><ol>
<li>有什么用？</li>
</ol><p>用于拥塞控制中的资源分配问题。在数据流共享链路的情况下，不让“独占”或“饿死”的情况发生，最大可能的实现公平。</p><ol>
<li>基本思想？</li>
</ol><p>如果不能再增加某个流的带宽，除非减少另一个<strong>带宽小于或等于它的流</strong>的带宽，那就是最大最小公平。</p></section><section><h4>3.2.2 AIMD 加法增乘法减<a href="#322-aimd-加法增乘法减"><span>#</span></a></h4><ol>
<li>基本思想？</li>
</ol><p>网络进入拥塞是很容易的，但是从中恢复却很难，所以递增过程应该平缓，递减过程应该激进。</p><ol>
<li>怎么做？</li>
</ol><p>评价是理解下图即可：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155156444.png" alt="" /></p></section></section><section><h3>3.3 TCP 和 IP 层的联动<a href="#33-tcp-和-ip-层的联动"><span>#</span></a></h3><p>我在前面提到了路由器检测到拥塞后会将 IP 包中的 ECN 字段置位，接受端收到该 IP 包后，发现 ECN = 11 ，会将回送给发送端的 TCP 报文头的 <strong>ECE(ECN Echo 回声)</strong> 设置为有效，发送端收到后，会将发送给接收端的 TCP 报文头的 <strong>CWR(Congestion Window Reduce 拥塞窗口减小标志)</strong> 设置为有效，告诉接收方我已经响应拥塞了，并减小窗口。</p><p>一句话，接收方设置 ECE ，发送方设置 CWR 。</p></section><section><h3>3.4 TCP 的拥塞控制算法<a href="#34-tcp-的拥塞控制算法"><span>#</span></a></h3><p>TCP 通信中，在发送窗口 swnd 和 接收窗口 rwnd 的基础上，又新增了一个<strong>拥塞窗口 cwnd</strong> ，由发送方维护。</p><span><span><span>swnd=min{rwnd,cwnd}swnd = min\{rwnd,cwnd\}</span><span><span><span></span><span>s</span><span>w</span><span>n</span><span>d</span><span></span><span>=</span><span></span></span><span><span></span><span>min</span><span>{</span><span>r</span><span>w</span><span>n</span><span>d</span><span>,</span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span>}</span></span></span></span></span><p><strong>注</strong>：看清题目问的是发送窗口还是拥塞窗口怎么变。</p><p>需要注意以下提到的各种算法一般是结合使用的，即 <strong>TCP Reno</strong> 。</p><section><h4>3.4.1 慢启动 Slow Start<a href="#341-慢启动-slow-start"><span>#</span></a></h4><ol>
<li>初始化 <span><span>cwnd=1cwnd = 1</span><span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span> ；</li>
<li>指数增加，第 <span><span>ii</span><span><span><span></span><span>i</span></span></span></span> 个 RTT 时，<span><span>cwnd=2icwnd = 2^i</span><span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span></span><span>=</span><span></span></span><span><span></span><span><span>2</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span></span></span></span></span></span></span></span> ；</li>
<li><strong>当 <span><span>cwnd≥ssthreshcwnd \ge ssthresh</span><span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span></span><span>≥</span><span></span></span><span><span></span><span>ss</span><span>t</span><span>h</span><span>r</span><span>es</span><span>h</span></span></span></span> ，即超过慢启动门限时，转 3.3.2 拥塞避免算法，或者直接称其为 AI(Additive Increase 线性增长)</strong>；</li>
</ol></section><section><h4>3.4.2 拥塞避免算法 —— AI<a href="#342-拥塞避免算法--ai"><span>#</span></a></h4><ol>
<li>cwnd 达到慢启动门限时，每个 RTT 时间，线性增长，即 <span><span>cwnd+=1cwnd+=1</span><span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span>+</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span> ；</li>
<li>也可以这么理解，达到阈值后，每收到 1 个 ACK ，<span><span>cwnd+=1cwndcwnd += \frac{1}{cwnd}</span><span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span>+</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>c</span><span>w</span><span>n</span><span>d</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ；</li>
</ol><p>无论有无达到门限，在发生严重拥塞，即重传计时器超时时，都转入 3.3.2 ；</p></section><section><h4>3.4.3 拥塞发生 —— MD<a href="#343-拥塞发生--md"><span>#</span></a></h4><p>MD ，即 <strong>Multiplicative Decrease</strong> ，乘法减少，在拥塞发生时，需要迅速减小发送速率。</p><ol>
<li>重传计时器超时，<span><span>cwnd=1,ssthresh=12cwndcwnd = 1,ssthresh = \frac{1}{2}cwnd</span><span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span><span>,</span><span></span><span>ss</span><span>t</span><span>h</span><span>r</span><span>es</span><span>h</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>c</span><span>w</span><span>n</span><span>d</span></span></span></span> ；</li>
<li>重新进入慢启动状态；</li>
</ol></section><section><h4>3.4.4 快速恢复<a href="#344-快速恢复"><span>#</span></a></h4><p>快速恢复事实上是与快速重传配合使用的，首先回顾一下快速重传的概念：</p><p><strong>快速重传</strong>：当发送方连续收到 3 个重复的 ACK ，它就知道该包可能丢了，于是无需等待该包的 RTO 超时就直接进行重传。</p><p>TCP 认为连续收到 3 个重复的 ACK 时，可能只是中间的某个数据包丢了，所以并不太严重，于是执行以下操作：</p><ol>
<li><span><span>cwnd=12cwnd,ssthresh=cwndcwnd = \frac{1}{2}cwnd,ssthresh = cwnd</span><span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>c</span><span>w</span><span>n</span><span>d</span><span>,</span><span></span><span>ss</span><span>t</span><span>h</span><span>r</span><span>es</span><span>h</span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>w</span><span>n</span><span>d</span></span></span></span> ；</li>
<li>执行恢复过程，似乎并不要求；</li>
</ol><p>下图还是相当直观的，展示了上述 4 个算法是如何配合工作的：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155203596.png" alt="" /></p></section></section></section></section>
<section><h1>路由技术<a href="#路由技术"><span>#</span></a></h1><p>网络层的功能。由路由器进行，路由表的构建需要路由算法 + 路由协议，这里将一些基本的路由算法，或者也可以说路由技术。</p><section><h2>1. 最优化原则<a href="#1-最优化原则"><span>#</span></a></h2><p>一句话，最优路径上的任意两点间的路径也是最优的。</p></section><section><h2>2. 泛洪 Flooding 路由算法<a href="#2-泛洪-flooding-路由算法"><span>#</span></a></h2><p><strong>每一个入境分组将被路由器转发到除了它进来的那条路线之外的每一条输出线路上</strong>，但是由于扩散算法会产生大量的重复分组，需要改进扩散算法，避免重复的分组。</p><ul>
<li>优点是简单，无需路由表，并且所有路径都会尝试，具有鲁棒性；</li>
<li>缺点是产生大量重复的包；</li>
</ul><p>解决无限制扩散的方法有增设跳计数器和序列号；</p></section><section><h2>3. 距离矢量选路 DVR<a href="#3-距离矢量选路-dvr"><span>#</span></a></h2><p><strong>路由器周期性地向邻接路由器广播自己的路由表（包括与其他路由器的最短距离和下一跳使用的接口信息）</strong>。</p><p>存在无穷计数问题，即好消息迅速传遍整个网络，但对坏消息反应慢，只知道距离，不知道路径！</p><p><strong>采用 DVR 的协议？</strong> —— RIP 协议</p></section><section><h2>4. 链路状态选路 LSR<a href="#4-链路状态选路-lsr"><span>#</span></a></h2><p>路由器在网络拓扑结构变化时，向网络内的所有路由器广播自己的链路状态信息，或称自己学习到的网络拓扑结构。</p><section><h3>4.1 与 DVR 的区别？<a href="#41-与-dvr-的区别"><span>#</span></a></h3><ul>
<li>DVR 周期性广播，LSR 在网络拓扑结构改变时广播；</li>
<li>DVR 只向邻居广播，LSR 向整个网络内其他的路由器广播；</li>
</ul></section><section><h3>4.2 基本步骤<a href="#42-基本步骤-1"><span>#</span></a></h3><ol>
<li>发现邻居节点：
<ol>
<li>路由器向邻居路由器广播一个 <strong>Hello</strong> 包；</li>
<li>邻居收到 Hello 包后，回复 1 个包含自己网络地址（Route ID ，全网唯一）的包给路由器；</li>
</ol>
</li>
<li>测量线路开销：通过 <strong>Echo Packet</strong> 包测量 RTT ；</li>
<li>创建链路状态分组 LSP(Link-State Packet)：分组中包含所有它刚刚知道的链路状态信息，具体可见下图；</li>
<li>泛洪链路状态分组：采用刚刚提到的 Flooding 算法发送 LSP ；</li>
<li>计算最短路径：路由器根据网络拓扑采用 Dijkstra 算法计算出到每一个其他路由器的最短路径树；</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155208160.png" alt="" /></p><p><strong>注</strong>：其中 Seq 和 Age 字段就是用来解决 Flooding 算法无限泛滥的问题的。</p></section><section><h3>4.3 应用<a href="#43-应用"><span>#</span></a></h3><ul>
<li>IS-IS 协议；</li>
<li>OSPF 协议；</li>
</ul></section></section><section><h2>5. 层次路由 Hierarchical Routing<a href="#5-层次路由-hierarchical-routing"><span>#</span></a></h2><p>大型网络中使用，将网络划分为不同层次的区域，<strong>从域内选路到路由器，再从域间选路到域，有效减小了路由表的规模</strong>。不过存在次优路由问题。</p><p>层次等级：regions 区域 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> clusters 簇 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> zones 区 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> groups 群；</p></section></section>
<section><h1>QoS(Quality of Service 网络服务质量)<a href="#qosquality-of-service-网络服务质量"><span>#</span></a></h1><section><h2>1. QoS 参数<a href="#1-qos-参数"><span>#</span></a></h2><ul>
<li>Reliability：可靠性，包括错误率和丢包率；</li>
<li>Delay；</li>
<li>Jitter；</li>
<li>Bandwidth；</li>
</ul><p>一些例子：</p><ul>
<li>电子邮件系统：要求高可靠性，别的无所谓；</li>
<li>电话：抖动和时延，这种语音或者视频流错一点东西看不出来；</li>
<li>视频会议：抖动 + 时延 + 带宽，需要音视频流的实时传输；</li>
</ul></section><section><h2>2. 哪些层有 QoS ？<a href="#2-哪些层有-qos-"><span>#</span></a></h2><p>课内讲到的主要涉及数据链路层 and 网络层。</p></section><section><h2>3. 网络层的 QoS<a href="#3-网络层的-qos"><span>#</span></a></h2><section><h3>3.1 流量整形 Traffic Shaping<a href="#31-流量整形-traffic-shaping"><span>#</span></a></h3><p>目的：<strong>减少网络中的突发流量</strong>。</p><section><h4>3.1.1 漏桶 Leaky Bucket<a href="#311-漏桶-leaky-bucket"><span>#</span></a></h4><p>桶中保存的是数据，恒定输出速率，当桶满时会丢弃一些数据分组。</p></section><section><h4>3.1.2 令牌桶 Token Bucket<a href="#312-令牌桶-token-bucket"><span>#</span></a></h4><ul>
<li>路由器匀速产生令牌，如果令牌桶满则丢弃多余的令牌；</li>
<li>数据包到达路由器的队列后，需要消耗令牌进行发送，一个 Token 对应 一个 Packet；</li>
<li>令牌桶并没有数据队列的缓存限制，不会丢弃数据帧；</li>
<li>令牌桶能接受一定的突发流量，比如突发流量到达，可以一次性消耗所有令牌（跟令牌桶的大小有关）；</li>
</ul><section><h5>突发流量持续时间计算<a href="#突发流量持续时间计算"><span>#</span></a></h5><p>令：</p><ul>
<li>突发数据的持续时间为 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> sec；</li>
<li>令牌桶的容量为 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> Bytes；</li>
<li>令牌的产生速率为 <span><span>RR</span><span><span><span></span><span>R</span></span></span></span> Bytes/s；</li>
<li>最大的输出速率为 <span><span>MM</span><span><span><span></span><span>M</span></span></span></span> Bytes/s；</li>
</ul><p>则有：</p><span><span><span>M×S=B+R×SM\times S = B + R\times S</span><span><span><span></span><span>M</span><span></span><span>×</span><span></span></span><span><span></span><span>S</span><span></span><span>=</span><span></span></span><span><span></span><span>B</span><span></span><span>+</span><span></span></span><span><span></span><span>R</span><span></span><span>×</span><span></span></span><span><span></span><span>S</span></span></span></span></span><p>即：</p><span><span><span>S=BM−RS = \frac{B}{M-R}</span><span><span><span></span><span>S</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>M</span><span></span><span>−</span><span></span><span>R</span></span></span><span><span></span><span></span></span><span><span></span><span><span>B</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span></section></section></section><section><h3>3.2 分组调度 Packet Scheduling<a href="#32-分组调度-packet-scheduling"><span>#</span></a></h3><section><h4>3.2.1 公平队列<a href="#321-公平队列"><span>#</span></a></h4><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155210881.png" alt="" /></p></section><section><h4>3.2.2 公平加权队列<a href="#322-公平加权队列"><span>#</span></a></h4><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155218101.png" alt="" /></p></section></section></section><section><h2>4. IEEE 802.11 的 QoS<a href="#4-ieee-80211-的-qos"><span>#</span></a></h2><section><h3>4.1 帧间隔<a href="#41-帧间隔"><span>#</span></a></h3><p>简单来说，就是<strong>设置不同的帧间隔，来实现优先级服务</strong>！</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155220274.png" alt="" /></p></section><section><h3>4.2 TXOP 机会传输<a href="#42-txop-机会传输"><span>#</span></a></h3><p>默认情况下，站点竞争到信道 1 次，发送 1 个帧。但是这对发送速率快的站点并不公平。假设站点的带宽分别为 <span><span>c1,c2c_1,c_2</span><span><span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，则平均数据率为：</p><span><span><span>1cave=1c1+1c2⇒cave=11c1+1c2\frac{1}{c_{ave}} = \frac{1}{c_1} + \frac{1}{c_2} \Rightarrow c_{ave} = \frac{1}{\frac{1}{c_1} + \frac{1}{c_2}}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>c</span><span><span><span><span><span><span></span><span><span><span>a</span><span>v</span><span>e</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>⇒</span><span></span></span><span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span><span>a</span><span>v</span><span>e</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span></span><span><span><span><span><span><span></span><span><span><span><span>c</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>+</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span><span>c</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>而 TXOP 给予两个站点相同的发送机会，即竞争一次，获得一段传输时间，即</p><span><span><span>c=ci/nc = c_i / n</span><span><span><span></span><span>c</span><span></span><span>=</span><span></span></span><span><span></span><span><span>c</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>/</span><span>n</span></span></span></span></span><p>比如对于 6Mbps 和 54Mbps 的两个站点，采用TXOP后，按照 3Mbps 和 27Mbps 发送帧；</p></section></section></section>
<section><h1>Internetworking 网络互联<a href="#internetworking-网络互联"><span>#</span></a></h1><ol>
<li>互联网络的分组转发；</li>
<li>隧道（Tunneling）计数：当源和目标主机位于相同类型的网络中，中间的网络属于不同类型时使用隧道方案，用中间网络的协议封装源主机发送的数据报；</li>
<li>互联网络路由；</li>
<li>分段与重组：不同网络有不同的 MTU ；
<ol>
<li>发送主机或路由器进行分片；</li>
<li>目的主机进行重组；</li>
</ol>
</li>
</ol><p>互联的网络中，路由的架构如下：</p><ul>
<li>首先，整个网络被分为无数个自治系统（AS, Autonomous System），可以理解为单一技术下管理的多个路由器，一个 AS 内部需要一种路由协议，不同 AS 之间也需要一种路由协议；</li>
<li>AS 内部路由协议 —— <strong>内部网关协议 IGP（Interior Gateway Protocol）</strong>；
<ul>
<li><strong>IS-IS 协议</strong>；</li>
<li><strong>RIP 协议</strong>；</li>
<li><strong>OSPF 协议</strong>；</li>
</ul>
</li>
<li>AS 间的路由协议 —— <strong>外部网关协议 EGP（External Gateway Protocol）</strong>；
<ul>
<li><strong>BGP 协议</strong>；</li>
</ul>
</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/journey-through-computer-network-protocols/</id>
      <title type="text">漫游计算机网络之协议篇</title>
      <published>2025-06-09T00:20:00.000Z</published>
      <updated>2025-06-09T00:20:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/journey-through-computer-network-protocols/"/>
      <summary type="text">其实也是期末复习罢了，只不过取了一个花里胡哨的标题。 本文将自顶向下的梳理计算机网络中每层的协议。 应用层专注为用户提供特定的功能，一般一类服务就对应了一个协议，所以协议繁多。同时，应用层驱动下层工作，但不关心数据是如何传输的，工作在操作系统的用户态。</summary>
      <content type="html"><![CDATA[<p>其实也是期末复习罢了，只不过取了一个花里胡哨的标题。</p>
<p>本文将自顶向下的梳理计算机网络中每层的协议。</p>
<section><h1>应用层<a href="#应用层"><span>#</span></a></h1><p>应用层专注为用户提供特定的功能，一般一类服务就对应了一个协议，所以协议繁多。同时，应用层驱动下层工作，但不关心数据是如何传输的，工作在操作系统的用户态。</p><section><h2>1. 有什么协议？<a href="#1-有什么协议"><span>#</span></a></h2><p>DNS 、FTP 、HTTP 、SMTP 、DHCP 、Telnet 、BGP 、RIP 等。</p><p><em><strong>提醒自己用：DHCP 、 BGP 、 RIP 协议我在网络层中已经讲了，但是它们都是工作在应用层的协议！</strong></em></p><p><strong>补充</strong>：协议工作在哪一层其实并不太重要，有关说法也众说纷纭，把 BGP 和 RIP 划分到应用层的依据是他们分别使用了 TCP 和 UDP 协议，既然协议是为上一层提供服务的，那么他俩就应该是应用层协议。我这边也只是梳理一下方便记忆，还需要自行建立理解。</p></section><section><h2>2. 注意点<a href="#2-注意点"><span>#</span></a></h2><ul>
<li><strong>DNS：域名解析协议</strong>；
<ul>
<li>作用是提供域名和 IP 之间的映射关系；</li>
<li>传输层使用 <strong>UDP</strong> ；</li>
<li>采用 C/S 方式；</li>
</ul>
</li>
<li><strong>SMTP：简单邮件传输协议</strong>；
<ul>
<li>作用是将邮件从发送方客户端发送到邮件服务器，或由一个邮件服务器发送到另一个邮件服务器；是异步传输，收发并不同步；</li>
<li>传输层使用 <strong>TCP</strong> ；</li>
</ul>
</li>
<li><strong>POP3：邮局协议</strong>；
<ul>
<li>用于从邮件服务器上接收（拉取）邮件到客户端。只负责接收邮件；与 SMTP 协议一起工作，实现异步的邮件传输；</li>
<li>传输层同样使用 <strong>TCP</strong> ；</li>
</ul>
</li>
<li><strong>IMAP：互联网邮件访问协议</strong>；
<ul>
<li>比 POP 3 更强，支持多设备的同步管理；</li>
<li>传输层同样使用 <strong>TCP</strong> ；</li>
</ul>
</li>
<li><strong>HTTP：超文本传输协议</strong>；
<ul>
<li>传输层使用 <strong>TCP</strong> ；</li>
<li>采用 C/S 方式；</li>
</ul>
</li>
<li><strong>DHCP：动态主机配置协议</strong>；
<ul>
<li>用于在主机没有被分配 IP 地址时，向 DHCP Server 请求 IP 地址等网络配置信息；</li>
<li>传输层使用 <strong>UDP</strong> ；</li>
<li>采用 C/S 方式；</li>
</ul>
</li>
<li><strong>RIP：路由信息选路协议</strong>；
<ul>
<li>采用<strong>距离矢量选路 DVR</strong> 构建路由表；</li>
<li>传输层使用 <strong>UDP</strong> ；</li>
</ul>
</li>
<li><strong>BGP：边界网关协议</strong>；
<ul>
<li>采用<strong>路径矢量选路 PVR</strong> 构建路由表；</li>
<li>传输层使用 <strong>TCP</strong> ，可靠传输；</li>
</ul>
</li>
</ul></section><section><h2>3. 应用场景<a href="#3-应用场景"><span>#</span></a></h2><p><strong>电子邮件系统</strong>：</p><ol>
<li>邮件服务器和邮件服务器之间一定使用 <strong>SMTP</strong> ；</li>
<li>客户端发送邮件至邮件服务器，使用 <strong>SMTP / HTTP</strong> ；</li>
<li>客户端从邮件服务器接收（拉取）邮件，使用 <strong>POP3 / IMAP / HTTP</strong> ；</li>
</ol><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154936283.png" alt="" /></p><p><strong>访问网站的一个典型过程</strong>：</p><ol>
<li>浏览器分析 URL ；</li>
<li>浏览器通过 DNS 获得 IP 地址；</li>
<li>浏览器根据 IP 地址，和服务器建立 TCP 连接；</li>
<li>浏览器发送 HTTP Request 请求；</li>
<li>服务器发送一个响应的 Web Page 给浏览器；</li>
<li>释放 TCP 连接；</li>
<li>浏览器呈现前端页面；</li>
</ol><p>用到了 TCP + DNS + UDP（DNS 协议的传输层用到了 UDP 协议） + HTTP 协议；</p></section></section>
<section><h1>传输层<a href="#传输层"><span>#</span></a></h1><p>传输层提供的是端到端的可靠、高效、节省开销的服务，事实上提供的是<strong>进程(Process)到进程</strong>的通信。</p><section><h2>1. 有什么协议？<a href="#1-有什么协议-1"><span>#</span></a></h2><p>TCP、UDP 协议；</p><p>其中：</p><ul>
<li>TCP 协议为上层的 STMP 、IMAP 、POP3 、HTTP 、BGP 协议服务；</li>
<li>UDP 协议为上层的 DNS 、 DHCP 、RIP 协议服务；</li>
</ul></section><section><h2>2. TCP 协议<a href="#2-tcp-协议"><span>#</span></a></h2><p>TCP 协议提供端到端的、面向连接的、面向字节流的可靠传输服务；</p><ol>
<li><strong>什么是面向连接？</strong></li>
</ol><p>TCP 是面向连接的，这就意味着它只能“一对一”连接，两个主机之间通过 Socket ，即四元组（源地址，源端口，目的地址，目的端口）唯一确定连接。</p><ol>
<li><strong>什么是面向字节流？</strong></li>
</ol><p>应用层的消息可能会被 TCP 分成多个 TCP 报文，即 TCP 不保留消息的边界！</p><section><h3>2.1 TCP 报文格式<a href="#21-tcp-报文格式"><span>#</span></a></h3><ul>
<li><strong>TCP Header</strong>：20 Bytes + Option 字段！！！</li>
<li><strong>MSS</strong>：Maximum Segment Size，最大报文段长度，<strong>只包含 TCP Payload ，即数据部分，不包括 TCP Header</strong> ；</li>
<li><strong>伪头部</strong>：共 12 Bytes ，包括源 IP 地址、目的 IP 地址等；并不参与实际的传输，仅用于 TCP Checksum 的计算；</li>
</ul></section><section><h3>2.2 TCP 的连接建立<a href="#22-tcp-的连接建立"><span>#</span></a></h3><p>连接建立即 3 次握手：</p><ol>
<li>客户端向服务器发送 SYN 包，其中 Seq = x ；</li>
<li>服务器向客户端发送 SYN + ACK 包，其中 Seq = y ，ACK = x + 1 ；</li>
<li>客户端向服务器发送 ACK 包，其中 Seq = x + 1 , ACK = y + 1 ；</li>
</ol><p>需要注意的是，单独的 ACK 包并不占用 Seq 的字节，即对于下一个要发送的 TCP 报文，其 Seq 还是等于 x + 1 ；而 SYN 和 FIN 包虽然不携带数据，但需要占用 1 个字节；</p></section><section><h3>2.3 TCP 的连接释放<a href="#23-tcp-的连接释放"><span>#</span></a></h3><p>连接释放即 4 次挥手 or 3 次挥手：</p><ol>
<li>客户端向服务器发送的 FIN 包，其中 Seq = x ；</li>
<li>服务器向客户端发送 ACK 包，其中 Seq = y ，ACK = x + 1 ；</li>
<li>服务器向客户端发送 FIN 包，其中 Seq = y ，ACK = x + 1 ；</li>
<li>客户端向服务端发送 ACK 包，其中 Seq = x + 1 ，ACK = y + 1 ；</li>
</ol><p>第 2 次和第 3 次事实上可以合并为 1 次握手，具体取决于服务器上层是否还有数据需要发送。</p></section><section><h3>2.4 TCP 协议的功能<a href="#24-tcp-协议的功能"><span>#</span></a></h3><ul>
<li>重传；</li>
<li>滑动窗口；</li>
<li>流量控制；</li>
<li>拥塞控制；</li>
</ul><p>详见<a href="/posts/journey-through-computer-networking-technologies/">《漫游计算机网络之技术篇》</a>。</p></section></section><section><h2>3. UDP 协议<a href="#3-udp-协议"><span>#</span></a></h2><p>UDP 无 ACK 、无差错控制（这个是可选的）、无流量控制、无拥塞控制，无连接、不可靠。为 IP 协议提供了一个接口，并加上端口号，实现复用。</p><section><h3>3.1 和 TCP 有什么区别？<a href="#31-和-tcp-有什么区别"><span>#</span></a></h3>

<table><thead><tr><th><strong>比较项</strong></th><th><strong>TCP</strong></th><th><strong>UDP</strong></th></tr></thead><tbody><tr><td><strong>是否面向连接</strong></td><td>✅ 是（需三次握手建立连接）</td><td>❌ 否（无连接，直接发送）</td></tr><tr><td><strong>可靠性</strong></td><td>✅ 可靠（有确认 ACK、重传、顺序控制）</td><td>❌ 不可靠（不确认、不重传、不保证顺序）</td></tr><tr><td><strong>是否保留消息边界</strong></td><td>不保留</td><td>保留</td></tr><tr><td><strong>是否支持一对多</strong></td><td>不支持，一对一，面向连接</td><td>支持广播和组播，可以实现一对多</td></tr><tr><td><strong>传输顺序</strong></td><td>✅ 保证顺序</td><td>❌ 不保证顺序</td></tr><tr><td><strong>拥塞控制</strong></td><td>✅ 有（慢启动、拥塞避免等）</td><td>❌ 没有</td></tr><tr><td><strong>流量控制</strong></td><td>✅ 有（滑动窗口）</td><td>❌ 没有</td></tr><tr><td><strong>首部开销</strong></td><td>📦 大（20 字节起）</td><td>📦 小（8 字节）</td></tr><tr><td><strong>速度</strong></td><td>🐢 较慢（因为各种控制）</td><td>⚡ 较快（无握手/控制）</td></tr><tr><td><strong>适用场景</strong></td><td>文件传输、网页浏览、邮件（需要可靠性）</td><td>音视频、语音通话、DNS 查询（需要实时性）</td></tr></tbody></table></section><section><h3>3.2 报文格式<a href="#32-报文格式"><span>#</span></a></h3><ul>
<li><strong>UDP Header：8 Bytes ！！！</strong>
<ul>
<li>Src Port Number ：16 bits ；</li>
<li>Dest Port Number ： 16 bits ；</li>
<li>Total Length ： 16 bits ；</li>
<li>Checksum ： 16 bits ；</li>
</ul>
</li>
<li><strong>UDP Data</strong>：应用层有多少传多少，不会对数据进行分片，如果超出最大可发送的长度就不会发送；</li>
<li><strong>伪报头</strong>：和 TCP 类似，UDP 同样会在差错检测计算校验和时添加一个伪报头，包括源 IP 地址、目的 IP 地址等，共 12 Bytes ；</li>
</ul></section></section></section>
<section><h1>网络层<a href="#网络层"><span>#</span></a></h1><p>网络层是处理端到端通信的最底层。</p><section><h2>1. 有什么协议？<a href="#1-有什么协议-2"><span>#</span></a></h2><ol>
<li>IP 协议（真王朝了），以及 IPv4 和 IPv6 ，可以说统治了整个网络层。</li>
<li>在 IP 协议之下，有 ARP 和 RARP 协议；</li>
<li>在 IP 协议之上，有 OSPF 、ICMP 和 IS-IS 协议；</li>
</ol><p>关于 RIP 和 BGP 协议的问题已经在前文中阐述，并且按照前文的理解，IP 协议既然也为 OSPF 协议提供服务，那么 OSPF 协议应该属于传输层协议。不过说白了，由于 TCP/IP 的统治力，界限未必一定要搞得很分明。</p><p>简单来说：</p><ul>
<li>RIP 、BGP 、OSPF 协议是 <strong>Routing Protocol</strong> ，即路由协议，用来计算最优路径并更新路由表；</li>
<li>而 IP 协议 严格来说是 <strong>Routed Protocol</strong> ，即可被路由协议，或者称其为路由的载体，将上层上层打包为 IP 包，根据路由表进行转发；</li>
</ul></section><section><h2>2. 有什么功能？<a href="#2-有什么功能"><span>#</span></a></h2><ul>
<li>Internetworking 网络互联；</li>
<li>Addressing 编址；</li>
<li>Routing 路由；</li>
<li>Packeting 组包；</li>
<li>Fragmenting 分片；</li>
</ul></section><section><h2>3. 提供的服务？<a href="#3-提供的服务"><span>#</span></a></h2><section><h3>3.1 无连接的服务 —— 数据报 Datagram<a href="#31-无连接的服务--数据报-datagram"><span>#</span></a></h3><ul>
<li>无连接；</li>
<li>每个包需要携带完整的目的地 IP 地址；</li>
<li>包可能乱序到达；</li>
</ul></section><section><h3>3.2 面向连接的服务 —— 虚电路 Virtual Circuit<a href="#32-面向连接的服务--虚电路-virtual-circuit"><span>#</span></a></h3><ul>
<li>面向连接的；</li>
<li>在正式传输数据前，先建立一条从源路由器到目的路由器之间的路径；</li>
</ul></section></section><section><h2>3. IPv4 协议<a href="#3-ipv4-协议"><span>#</span></a></h2><section><h3>3.1 IPv4 报文格式<a href="#31-ipv4-报文格式"><span>#</span></a></h3><p>考试会提供报文格式，这里再复习一遍几个比较重要的字段：</p><ul>
<li><strong>IHL(IP Header Length)</strong>：报文头长度，实际的头长度是 IHL <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> 4 ，即这 4 bits 表示的范围是 5 ～ 15 ，实际头长度范围是 20 Bytes ～ 60 Bytes ；</li>
<li><strong>Type of Service</strong>：知道这里的后 2 bits 是显式拥塞控制位即可；</li>
<li><strong>Total Length</strong>：注意包括<strong>头长度 + 数据长度</strong>；</li>
<li><strong>DF</strong>：不分片标志，DF = 1 则表示不分片，一般为 0 ；</li>
<li><strong>MF</strong>：更多段标志，同一个 IP 包，除最后一个分片外，MF = 1 ；</li>
<li><strong>Fragment Offset</strong>：分片后的数据在原始数据中的位置，即偏移量，注意实际偏移量必须是 8 的整数倍，这 13 bits 的内容 <span><span>×\times</span><span><span><span></span><span>×</span></span></span></span> 8 = 实际偏移量；</li>
<li><strong>Checksum</strong>：只对 IP Header 进行校验；</li>
</ul></section><section><h3>3.2 IPv4 地址<a href="#32-ipv4-地址"><span>#</span></a></h3><p>IPv4 地址共 32 bits ；</p><section><h4>3.2.1 分类 IP 地址<a href="#321-分类-ip-地址"><span>#</span></a></h4><p>网络号 + 主机号。</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154944191.png" alt="" /></p></section><section><h4>3.2.2 特殊的 IP 地址<a href="#322-特殊的-ip-地址"><span>#</span></a></h4><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154949833.png" alt="" /></p><p>主要在计算 IP 地址块数时，减去网络号和网络等广播地址；</p></section><section><h4>3.2.3 私有 IP 地址<a href="#323-私有-ip-地址"><span>#</span></a></h4><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154955005.png" alt="" /></p><p>用于 NAT 中。</p></section><section><h4>3.2.4 子网与子网掩码<a href="#324-子网与子网掩码"><span>#</span></a></h4><p>简单来说，就是向主机号借若干位，作为子网号，将 1 个较大的网分为若干个子网。对外界是不可见的。</p><p>子网掩码：一般为 <code>255.255.224.0</code> 这种形式，不过写成前缀长度的形式 <code>/19</code> 似乎也没问题。</p><p><strong>路由时，如何得到子网地址？</strong></p><p>将目的 IP 与子网掩码进行按位与的操作即可。</p></section><section><h4>3.2.5 无类别地址 CIDR<a href="#325-无类别地址-cidr"><span>#</span></a></h4><p>形如 <code>a.b.c.d/x</code> ；</p></section><section><h4>3.2.6 路由聚合<a href="#326-路由聚合"><span>#</span></a></h4><p>使用 CIDR 除了可以用来表示一个子网，也可以将多个子网聚合为一个超网；不过，将多个地址数较少的网合并为一个大网，会使地址空间被放大。</p><p>对于路由时，一个目的 IP 地址与多个不同前缀长度网络匹配时，选择前缀最长的那个网络进行转发，这就是<strong>最长匹配原则</strong>。</p></section></section></section><section><h2>4. IPv6 协议<a href="#4-ipv6-协议"><span>#</span></a></h2><p>大概了解即可：</p><ul>
<li>IPv6 的地址长度为 128 bits ；</li>
<li>IPv6 Header 头长度固定为 40 Bytes ；</li>
<li>IPv6 的扩展头位于 Payload ，即数据部分；</li>
</ul></section><section><h2>5. ARP 地址解析协议<a href="#5-arp-地址解析协议"><span>#</span></a></h2><p>用于解决从网络层的 IP 地址到数据链路层的 MAC 地址的映射关系问题。即已知 IP 地址，需要获得 MAC 地址。</p></section><section><h2>6. RARP 协议<a href="#6-rarp-协议"><span>#</span></a></h2><p>上述的逆向，即已知 MAC 地址，需要获得 IP 地址，已经被淘汰了。</p></section><section><h2>7. ICMP 因特网控制报文协议<a href="#7-icmp-因特网控制报文协议"><span>#</span></a></h2><p>包括差错报告报文、控制报文和查询报文。</p></section><section><h2>8. IS-IS 协议<a href="#8-is-is-协议"><span>#</span></a></h2><p>IS-IS 协议，全称 Intermediate System-Intermediate System ，为 IGP（内部网关协议）之一，属于链路状态协议，使用 LSP 链路状态选路工作。</p><section><h3>8.1 IS-IS 协议 vs. OSPF 协议<a href="#81-is-is-协议-vs-ospf-协议"><span>#</span></a></h3>

<table><thead><tr><th><strong>比较项</strong></th><th><strong>IS-IS</strong></th><th><strong>OSPF</strong></th></tr></thead><tbody><tr><td>起源</td><td>OSI 协议（CLNP），更早</td><td>IP 协议（IETF）</td></tr><tr><td>网络层协议</td><td>原生支持 CLNP，后支持 IPv4/IPv6 等多种协议</td><td>只支持 IP 协议</td></tr><tr><td>封装方式</td><td>使用 OSI 网络层直接封装，不依赖 IP</td><td>运行在 IP 层，使用协议号 89</td></tr><tr><td>灵活性</td><td>更易扩展，适合大规模网络</td><td>设计固定，结构清晰</td></tr><tr><td>应用领域</td><td>常用于运营商、ISP、MPLS 网络</td><td>企业网、数据中心常见</td></tr></tbody></table></section></section></section>
<section><h1>介质访问控制子层<a href="#介质访问控制子层"><span>#</span></a></h1><section><h2>1. 有什么协议？<a href="#1-有什么协议-3"><span>#</span></a></h2><ul>
<li>IEEE 802.3（经典以太网，CSMA/CD）；</li>
<li>IEEE 802.11（Wifi，CSMA/CA）；</li>
<li>IEEE 802.1g（VLAN）；</li>
</ul></section><section><h2>2. 有什么功能？<a href="#2-有什么功能-1"><span>#</span></a></h2><p>解决广播式链路（即共享通信介质）的通信环境下介质访问控制问题，共享介 质的通信环境包括有线通信环境和无线通信环境。</p></section><section><h2>3. Ethernet 以太网<a href="#3-ethernet-以太网"><span>#</span></a></h2><section><h3>3.1 10M 经典以太网<a href="#31-10m-经典以太网"><span>#</span></a></h3><ul>
<li><strong>传输介质</strong>：Coaxial Cable 或 Twisted Pair（一般使用的是 UTP5），常见的有 <code>10Base5-Thick</code> 和 <code>10Base-T</code> ；</li>
<li><strong>拓扑结构与设备</strong>：
<ul>
<li>同轴电缆采用 Bus 总线型；</li>
<li>到双绞线后引入<strong>集线器 Hub</strong> ；</li>
<li>由于电缆长度有限，所以引入<strong>中继器 Repeater</strong>；</li>
</ul>
</li>
<li><strong>编码方式</strong>：Manchester 编码；</li>
<li><strong>共享信道协议</strong>：使用 1 - 坚持的 CSMA/CD 技术；
<ul>
<li>注意，经典以太网中，认定信道空闲的方式有说区别：需要监听到<strong>信道在 IFG(Inter Frame Gap, 帧间隙)时间内均空闲，才认为空闲</strong>；</li>
</ul>
</li>
<li><strong>最大最小帧长</strong>：
<ul>
<li>数据部分长度范围为 46 Bytes ～ 1500 Bytes ；</li>
<li>加上 18 Bytes 的控制字段，所以总长度为 64 Bytes ~ 1518 Bytes ；</li>
<li>如果不满足最小长度，则需要用 Pad 字段补齐；</li>
</ul>
</li>
</ul></section><section><h3>3.2 百兆以太网/快速以太网<a href="#32-百兆以太网快速以太网"><span>#</span></a></h3><ul>
<li><strong>传输介质</strong>：双绞线或光纤，常见的有 <code>100Base-T4</code> 和 <code>100Base-TX</code> ；</li>
<li><strong>工作方式</strong>：
<ul>
<li><code>100Base-T4</code> ：使用 <strong>4 对 UTP3 双绞线</strong>，<strong>半双工</strong>；</li>
<li><code>100Base-TX</code> ：使用 <strong>2 对 UTP 5 双绞线</strong>，<strong>全双工</strong>；</li>
</ul>
</li>
<li><strong>编码方式</strong>：不再使用 Manchester 编码，而是使用 8B/6T（三电平）或 4B/5B + MLT-3 ；</li>
<li><strong>共享信道协议</strong>：如果采用 Hub 连接，则为半双工模式，且仍需要 CSMA/CD 技术；</li>
</ul></section><section><h3>3.3 G 比特以太网<a href="#33-g-比特以太网"><span>#</span></a></h3><ul>
<li><strong>传输介质</strong>：双绞线或光纤；</li>
<li><strong>工作方式和设备</strong>：Hub 或 Switch ，对应半双工或全双工的工作方式；</li>
<li><strong>共享信道协议</strong>：如果采用 Hub 连接，仍需 CSMA/CD 技术，但存在问题，由于速度更快，电缆的最大长度相应减少；</li>
<li><strong>流量控制</strong>：发送 <strong>Pause</strong> 帧；</li>
</ul><section><h4>3.3.1 解决电缆长度限制的方法<a href="#331-解决电缆长度限制的方法"><span>#</span></a></h4><ul>
<li><strong>Carrier Extension 载波扩展</strong>：在原来帧的尾部再添加一个 Extension 字段，将帧长度扩展至 512 Bytes ；</li>
<li><strong>Frame Burst 帧突发</strong>：如果对于有大量需要连续发送的帧的情况，帧的发送往往用队列存储，发送方可以将需要连续发送的帧拼成一个帧进行发送；</li>
</ul></section></section><section><h3>3.4 10G 比特以太网<a href="#34-10g-比特以太网"><span>#</span></a></h3><ul>
<li><strong>传输介质</strong>：电缆较短的一般用的是<strong>双绞线（6a UDP）</strong>，长距离一般使用<strong>光纤</strong>；</li>
<li><strong>应用环境</strong>：高速的本地<strong>骨干网</strong>、<strong>高性能企业网络</strong>、<strong>数据中心</strong>等；</li>
<li><strong>工作方式和设备</strong>：彻底抛弃了 Hub ，只有全双工工作模式，不再需要 CSMA/CD 技术；</li>
</ul></section><section><h3>3.5 以太网一图流<a href="#35-以太网一图流"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101154958228.png" alt="" /></p></section></section><section><h2>4. Wireless 无线通信 IEEE 802.11<a href="#4-wireless-无线通信-ieee-80211"><span>#</span></a></h2><section><h3>4.1 共享信道协议<a href="#41-共享信道协议"><span>#</span></a></h3><p>CD ，即冲突检测已经不再适用！</p><ul>
<li>Point coordination function（PCF, 点协调）：
<ul>
<li>基站控制，进行轮询 Polling ；</li>
</ul>
</li>
<li>Distributed coordination function（DCF, 分布式协调）：
<ul>
<li>没有中心控制，实行 CSMA/CA ；</li>
</ul>
</li>
</ul><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155004180.png" alt="" /></p></section><section><h3>4.2 CSMA/CA<a href="#42-csmaca"><span>#</span></a></h3><p>详见<a href="/posts/journey-through-computer-networking-technologies/">《漫游计算机网络之技术篇》</a>。</p></section><section><h3>4.3 监听方式<a href="#43-监听方式"><span>#</span></a></h3><ul>
<li><strong>物理监听</strong>；</li>
<li><strong>虚拟监听</strong>：
<ul>
<li>每个站点都记录一个 NAV(Network Allocation Vector, 网络分配向量)；</li>
<li>每个帧都携带一个 NAV；</li>
<li>NAV 表示这个数据帧将预计占用信道多长时间，通过 NAV 来确定退避时间，减少冲突；</li>
</ul>
</li>
</ul></section><section><h3>4.4 可靠性 —— 段突发<a href="#44-可靠性--段突发"><span>#</span></a></h3><p>短帧出错概率小，所以采用 <strong>Fragment burst 段突发</strong> 的方式，将较长的帧分为小的 Fragment进行发送，来提高可靠性；</p></section><section><h3>4.5 节省电量<a href="#45-节省电量"><span>#</span></a></h3><p>一直监听信道会造成电量浪费。</p><section><h4>4.5.1 省电模式<a href="#451-省电模式"><span>#</span></a></h4><ol>
<li><strong>客户端进入省电模式</strong>：
<ul>
<li>终端设备（如笔记本、手机）告诉 AP（无线接入点）自己要进入省电状态。</li>
<li>然后设备会<strong>关闭无线网卡部分模块</strong>，不再一直监听信道。</li>
</ul>
</li>
<li><strong>AP 缓存数据（buffer traffic）</strong>：
<ul>
<li>当有数据要发给该客户端时，AP 会<strong>暂时缓存这些数据</strong>。</li>
</ul>
</li>
<li><strong>Beacon 帧广播</strong>：
<ul>
<li>AP 会周期性发送 <strong>Beacon 帧</strong>（例如每 100ms），</li>
<li>其中包含一个 <strong>Traffic Indication Map（TIM）</strong>，指示哪些客户端有待接收的数据。</li>
</ul>
</li>
<li><strong>客户端唤醒并检查 Beacon</strong>：
<ul>
<li>客户端周期性<strong>唤醒接收 Beacon 帧</strong>，看看 TIM 中是否有自己的标识。</li>
</ul>
</li>
<li><strong>发送 PS-Poll 帧</strong>：
<ul>
<li>如果 TIM 表示有数据，客户端就发送一个 <strong>PS-Poll（Power Save Poll）帧</strong> 请求接收数据。</li>
</ul>
</li>
<li><strong>AP 发送缓存数据</strong>：
<ul>
<li>AP 收到 PS-Poll 后立即发送缓存数据给客户端。</li>
</ul>
</li>
</ol></section><section><h4>4.5.2 APSD 增强型省电机制<a href="#452-apsd-增强型省电机制"><span>#</span></a></h4><ol>
<li><strong>客户端进入 APSD 模式</strong>（也处于省电状态）；</li>
<li><strong>客户端主动发送数据帧</strong>（比如语音包）；</li>
<li><strong>AP 在接收到数据帧后，立即发送</strong>之前缓存的数据（无需 PS-Poll）；</li>
<li>客户端周期性发送语音包（如每 20ms），正好触发 AP 同步传送下行数据；</li>
</ol></section></section><section><h3>4.6 QoS —— 帧间隔 and TXOP<a href="#46-qos--帧间隔-and-txop"><span>#</span></a></h3><p>详见<a href="/posts/journey-through-computer-networking-technologies/">《漫游计算机网络之技术篇》</a>。</p></section></section><section><h2>5. VLAN 虚拟局域网<a href="#5-vlan-虚拟局域网"><span>#</span></a></h2><section><h3>5.1 为什么需要 VLAN ？<a href="#51-为什么需要-vlan-"><span>#</span></a></h3>

<table><thead><tr><th><strong>目的</strong></th><th><strong>好处</strong></th></tr></thead><tbody><tr><td><strong>隔离广播</strong></td><td>将单一的大广播域拆分成多个小广播域，减少网络风暴与 CPU 占用</td></tr><tr><td><strong>提高安全性</strong></td><td>不同部门/业务放在不同 VLAN，相互默认不可直接访问</td></tr><tr><td><strong>灵活部署</strong></td><td>逻辑分段代替物理布线；用户搬工位只要改端口 VLAN 设置即可</td></tr><tr><td><strong>简化管理</strong></td><td>基于功能或安全策略（而非地理位置）来组织网络</td></tr></tbody></table></section><section><h3>5.2 IEEE 802.1Q 协议<a href="#52-ieee-8021q-协议"><span>#</span></a></h3><p>在以太网帧头插入 4 字节字段，携带 12 位 VLAN ID ；</p><p>第一个遇到的支持 VLAN 的网桥/交换机给帧加上 VLAN 字段，最后一个去掉 VLAN 字段。</p><p><strong>注</strong>：</p><ul>
<li>终端设备对 VLAN Tag 无感知，即只需要网桥/交换机支持 VLAN ；</li>
<li>集线器 Hub 由于工作在物理层，因此无法识别 VLAN 的标记。</li>
</ul></section></section></section>
<section><h1>数据链路层<a href="#数据链路层"><span>#</span></a></h1><section><h2>1. 有什么协议？<a href="#1-有什么协议-4"><span>#</span></a></h2><ul>
<li>面向局域网 LAN 的：
<ul>
<li>IEEE 802.3(Ethernet)；</li>
<li>IEEE 802.11(Wifi)；</li>
</ul>
</li>
<li>面向广域网 WAN 的：
<ul>
<li>PPP 协议；</li>
<li>HDLC 协议；</li>
</ul>
</li>
</ul></section><section><h2>2. 有什么功能？<a href="#2-有什么功能-2"><span>#</span></a></h2><ul>
<li>差错控制；</li>
<li>流量控制；</li>
<li>编址（MAC 地址）和介质访问控制（应用于共享信道）；</li>
<li>成帧 Framing ；</li>
</ul><section><h3>2.1 Framing 成帧<a href="#21-framing-成帧"><span>#</span></a></h3><ul>
<li>字符计数法；</li>
<li>字节填充法；</li>
<li>比特填充法；</li>
<li>物理层编码违例；</li>
</ul><p>字节填充法如下图所示：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155009894.png" alt="" /></p><p>比特填充法如下图所示：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155017071.png" alt="" /></p><p>只需要知道：</p><ul>
<li>PPP 协议使用字节填充法；</li>
<li>HDLC 协议使用比特填充法，HDLC等协议差错校验和成帧的顺序是：
<ul>
<li>发送方处理步骤：
<ul>
<li>计算并添加校验码 -&gt; 0比特填充 -&gt; 添加收尾标记01111110</li>
</ul>
</li>
<li>接受方处理步骤：
<ul>
<li>收到物理层的01比特串 -&gt; 首先根据首尾标记位提出帧 -&gt; 删除发送方添加的零比特，就是进行比特删除 -&gt; 最后，进行差错校验</li>
</ul>
</li>
</ul>
</li>
<li>如果物理层采用 Manchester 编码，则可以使用物理层编码违例方式；</li>
</ul></section><section><h3>2.2 差错控制<a href="#22-差错控制"><span>#</span></a></h3><p>详见<a href="/posts/journey-through-computer-networking-technologies/">《漫游计算机网络之技术篇》</a>。</p></section><section><h3>2.3 流量控制<a href="#23-流量控制"><span>#</span></a></h3><p>详见<a href="/posts/journey-through-computer-networking-technologies/">《漫游计算机网络之技术篇》</a>。</p></section></section><section><h2>3. 提供的服务？<a href="#3-提供的服务-1"><span>#</span></a></h2><ul>
<li>Connectionless services 无连接的服务；
<ul>
<li>Unacknowledged connectionless services：大多数局域网LAN使用无确认；</li>
<li>Acknowledged connectionless services：无线通信；</li>
</ul>
</li>
<li>Acknowledged connection-oriented services 面向连接的服务；</li>
</ul></section><section><h2>4. HDLC 协议<a href="#4-hdlc-协议"><span>#</span></a></h2><p>HDLC 协议，即 High-level Datalink Control ，高级数据链路控制协议；</p><section><h3>4.1 特点<a href="#41-特点"><span>#</span></a></h3><ul>
<li>可靠，面向连接；</li>
<li>包含流量控制（GBN ARQ or SR ARQ）和差错控制功能（CRC）；</li>
<li>同步串行链路传输；</li>
<li>不支持链路和网络参数协商；</li>
<li>不支持认证；</li>
</ul></section><section><h3>4.2 帧格式<a href="#42-帧格式"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155025193.png" alt="" /></p><p>从上图也可见，HDLC 采用<strong>零比特填充</strong>；</p><p><strong>注</strong>：监督帧就是单独的 ACK 或 NAK ；</p></section></section><section><h2>5. PPP 协议<a href="#5-ppp-协议"><span>#</span></a></h2><p>PPP 协议，即 Point-to-Point Protocol ，点对点协议；</p><section><h3>5.1 特点<a href="#51-特点"><span>#</span></a></h3><ul>
<li>只支持点到点；</li>
<li>无连接，无确认；</li>
<li>采用字节填充法；</li>
<li>适配多种网络层协议；</li>
<li>支持身份认证；</li>
<li>物理层可以采用异步和同步传输；</li>
</ul></section><section><h3>5.2 帧格式<a href="#52-帧格式"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155027864.png" alt="" /></p><p><strong>注</strong>：</p><ul>
<li>其中用红线❌掉的都是可以省略的部分；</li>
<li>Checksum 使用 CRC 校验来检错；</li>
</ul></section></section><section><h2>6. HDLC vs. PPP 协议<a href="#6-hdlc-vs-ppp-协议"><span>#</span></a></h2><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155031080.png" alt="" /></p></section></section>
<section><h1>物理层<a href="#物理层"><span>#</span></a></h1><p>物理层涉及的主要是一些标准，包括对速率、传输介质、编码方式和调制方式的规定。</p><section><h2>1. 基本概念<a href="#1-基本概念"><span>#</span></a></h2><section><h3>1.1 带宽 Bandwidth<a href="#11-带宽-bandwidth"><span>#</span></a></h3><p>带宽最早用来指模拟信号具有的频段宽度，即<strong>信道能够有效传输信号的频率范围</strong>，对应的单位就是 <span><span>HzHz</span><span><span><span></span><span>H</span><span>z</span></span></span></span> ，e.g. 语音电话线 300 ～ 3400 Hz ，对应带宽 3.1 KHz ；</p><p>现在这个概念被扩展到数字信号上，带宽指的事实上就是<strong>最大数据率</strong>。</p></section><section><h3>1.2 数据率/比特率/传输速率<a href="#12-数据率比特率传输速率"><span>#</span></a></h3><p>指的就是数据的传送速率，单位 <span><span>bpsbps</span><span><span><span></span><span>b</span><span>p</span><span>s</span></span></span></span> ，e.g. 百兆以太网数据率为 100 Mbps ；</p></section><section><h3>1.3 吞吐量 Throughput<a href="#13-吞吐量-throughput"><span>#</span></a></h3><p>在单位时间内通过某个网络的数据量，或者说网络可以传送的数据位数。单位：<span><span>b/sb/s</span><span><span><span></span><span>b</span><span>/</span><span>s</span></span></span></span>（其实就是 <span><span>bpsbps</span><span><span><span></span><span>b</span><span>p</span><span>s</span></span></span></span>）；</p><p>其与数据率是不同的，数据率是可用带宽，而吞吐量是一个实际链路工作过程中的测试指标，往往数据率。</p></section><section><h3>1.4 负载 Load<a href="#14-负载-load"><span>#</span></a></h3><p>单位时间内注入网络的数据位数，单位：<span><span>bpsbps</span><span><span><span></span><span>b</span><span>p</span><span>s</span></span></span></span> 。</p></section><section><h3>1.5 码元速率/调制速率/波特率 and 采样速率<a href="#15-码元速率调制速率波特率-and-采样速率"><span>#</span></a></h3><p>每秒钟传输的码元数，单位 <span><span>BaudBaud</span><span><span><span></span><span>B</span><span>a</span><span>u</span><span>d</span></span></span></span> ，和比特率有以下转换关系：</p><span><span><span>Bit Rate=Baud Rate×log2V,V为信号级数Bit\space Rate = Baud\space Rate\times log_2{V},V \text{为信号级数}</span><span><span><span></span><span>B</span><span>i</span><span>t</span><span> </span><span>R</span><span>a</span><span>t</span><span>e</span><span></span><span>=</span><span></span></span><span><span></span><span>B</span><span>a</span><span>u</span><span>d</span><span> </span><span>R</span><span>a</span><span>t</span><span>e</span><span></span><span>×</span><span></span></span><span><span></span><span>l</span><span>o</span><span><span>g</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>V</span></span><span>,</span><span></span><span>V</span><span><span>为信号级数</span></span></span></span></span></span><p>一个例外是 Manchester 编码，其用 2 个码元代表 1 个 bit ，所以 Bit Rate = <span><span>12\frac{1}{2}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> Baud Rate 。</p><p>关于采样速率是否与前者相同的问题，理论上来说，采样速率应该指的是对模拟信号的采样过程，而波特率已经是数字信号了。不过似乎题目中有时也会把两者混为一谈，比如：</p><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155036552.png" alt="" /></p></section><section><h3>1.6 带宽时延积 bandwidth-delay product<a href="#16-带宽时延积-bandwidth-delay-product"><span>#</span></a></h3><p>表示充满整个网络的比特数。带宽时延积 = <span><span>tprop×Bandwidtht_{prop} \times Bandwidth</span><span><span><span></span><span><span>t</span><span><span><span><span><span><span></span><span><span><span>p</span><span>r</span><span>o</span><span>p</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>×</span><span></span></span><span><span></span><span>B</span><span>an</span><span>d</span><span>w</span><span>i</span><span>d</span><span>t</span><span>h</span></span></span></span> ；</p></section><section><h3>1.7 单工/半双工/全双工<a href="#17-单工半双工全双工"><span>#</span></a></h3><ul>
<li><strong>单工 Simplex</strong>：信息只进行单向的传输，e.g. 收音机；</li>
<li><strong>半双工 Half-Duplex</strong>：同一节点，在同一时刻只能作为发送方或接收方，e.g. 对讲机；</li>
<li><strong>全双工 Full-duplex</strong>：同一节点在同一时刻可以既发又收，e.g. 打电话；</li>
</ul></section><section><h3>1.8 同步/异步传输<a href="#18-同步异步传输"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2026/01/20260101155038506.png" alt="" /></p></section></section><section><h2>2. 信道最大传输速率计算<a href="#2-信道最大传输速率计算"><span>#</span></a></h2><p><span><span>CNyquist=2Blog2V,CShannon=Blog2(1+SN),SNdb=10log10SNC_{Nyquist} = 2Blog_2{V},C_{Shannon}=Blog_2(1 + \frac{S}{N}),\frac{S}{N}_{db} = 10log_{10}{\frac{S}{N}}</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span><span>N</span><span>y</span><span>q</span><span>u</span><span>i</span><span>s</span><span>t</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>2</span><span>B</span><span>l</span><span>o</span><span><span>g</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>V</span></span><span>,</span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span><span>S</span><span>hann</span><span>o</span><span>n</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>B</span><span>l</span><span>o</span><span><span>g</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>1</span><span></span><span>+</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>N</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>S</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>)</span><span>,</span><span></span><span><span><span></span><span><span><span><span><span><span></span><span><span><span>N</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>S</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span><span><span><span><span><span></span><span><span><span>d</span><span>b</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>10</span><span>l</span><span>o</span><span><span>g</span><span><span><span><span><span><span></span><span><span><span>10</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span><span></span><span><span><span><span><span><span></span><span><span><span>N</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>S</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span> ；</p></section><section><h2>3. 计算采样率<a href="#3-计算采样率"><span>#</span></a></h2><p>Nyquist 指出，对于一个带宽为 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> 的基带信号，需要 <span><span>2B2B</span><span><span><span></span><span>2</span><span>B</span></span></span></span> sample/s 的采样率才能够完全重构。</p></section><section><h2>4. 传输介质<a href="#4-传输介质"><span>#</span></a></h2><ul>
<li>有线传输介质：
<ul>
<li><strong>双绞线 Twisted Pair</strong>：既能传模拟信号（电话线），又能传数字信号（以太网）；</li>
<li><strong>同轴电缆 Coaxial Cable</strong>：和双绞线相比，屏蔽性能和带宽更好；</li>
<li><strong>光纤 Fiber Optics</strong>：带宽大，抗干扰能力好；</li>
</ul>
</li>
<li>无线传输介质：
<ul>
<li><strong>无线电</strong>：一句话，频率越高，波长越短，传播距离越短，超高频需要在视线范围内传播；</li>
<li><strong>微波</strong>：直线传输，适合长距离，需要地面中继站 Repeater 进行整形和放大；</li>
<li>红外线和可见光通信：略；</li>
<li><strong>卫星通信</strong>：通信卫星相当于微波通信中中继站 Repeater 的作用，只不过在大气层以外；</li>
</ul>
</li>
</ul></section><section><h2>5. 数字调制技术<a href="#5-数字调制技术"><span>#</span></a></h2><p>ASK ，FSK ，PSK ，QAM ，QPSK 等。</p></section><section><h2>6. 物理层协议<a href="#6-物理层协议"><span>#</span></a></h2><p>参数包括：</p><ul>
<li>Mechanical Features 机械特性：接口形状和尺寸、引线数目和排列、固定和锁定装置等；</li>
<li>Electrical Features 电气特性：电缆线上的电压范围；</li>
<li>Functional Features 功能特性：某条线上出现的某一电平的电压保持何种意义；</li>
<li>Procedure Features 过程特性：对于不同功能的各种可能事件的出现顺序；</li>
</ul><p>e.g. RS-232 协议。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/how-to-insert-pseudocode-in-hexo-blog/</id>
      <title type="text">如何在 Hexo 博客中插入伪代码</title>
      <published>2025-05-19T04:11:00.000Z</published>
      <updated>2025-05-19T04:11:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/how-to-insert-pseudocode-in-hexo-blog/"/>
      <summary type="text">在技术博客中，优雅地展示算法伪代码是常见需求。但是 Hexo 本身并不支持 的，Hexo 默认只支持常规代码高亮，无法直接渲染算法伪代码。当然，Hexo 提供丰富的插件，但是我搜索后并没有找到相关的（如果是我遗漏了，可以在评论区指出）。 所以，自行摸索后，找到以下的替代方法：</summary>
      <content type="html"><![CDATA[<p>在技术博客中，优雅地展示算法伪代码是常见需求。但是 Hexo 本身并不支持 <span><span>LaTeX\LaTeX</span><span><span><span></span><span><span>L</span><span></span><span><span><span><span><span></span><span><span>A</span></span></span></span></span></span><span></span><span><span>T</span><span></span><span><span><span><span><span></span><span><span>E</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span><span></span><span>X</span></span></span></span></span></span> 的，Hexo 默认只支持常规代码高亮，无法直接渲染算法伪代码。当然，Hexo 提供丰富的插件，但是我搜索后并没有找到相关的（如果是我遗漏了，可以在评论区指出）。</p>
<p>所以，自行摸索后，找到以下的替代方法：</p>
<p><strong><a href="https://github.com/SaswatPadhi/pseudocode.js" target="_blank">pseudocode.js</a></strong> 是一个 JavaScript 库，可将伪代码精美地排版为 HTML，轻量且兼容多种浏览器。以下是具体的配置步骤，由于我使用的是 Fluid 主题模板，支持直接在主题配置文件中引入自定义的 css 和 js 文件，不同主题可能存在区别，但原理类似。</p>
<section><h1>1. 具体步骤<a href="#1-具体步骤"><span>#</span></a></h1><section><h2>步骤一：引入依赖<a href="#步骤一引入依赖"><span>#</span></a></h2><section><h4>1. 引入 CSS<a href="#1-引入-css"><span>#</span></a></h4><p>在 <code>source/css/custom.css</code> 末尾添加：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>@import</span><span> </span><span>url</span><span>(</span><span>'https://cdn.jsdelivr.net/npm/pseudocode@2.4.1/build/pseudocode.min.css'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>2. 动态引入 JS<a href="#2-动态引入-js"><span>#</span></a></h4><p>编辑 <code>source/js/custom.js</code>，添加如下内容：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 动态加载 JS 文件</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>loadScript</span><span><span>(</span><span>url</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resolve</span><span>, </span><span>reject</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>const</span><span> </span><span>script</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>'script'</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>script</span><span>.</span><span>src</span><span> </span><span>=</span><span><span> </span><span>url</span><span>;</span></span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>script</span><span>.</span><span>onload</span><span> </span><span>=</span><span><span> </span><span>resolve</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>script</span><span>.</span><span>onerror</span><span> </span><span>=</span><span><span> </span><span>reject</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>document</span><span>.</span><span>head</span><span>.</span><span>appendChild</span><span><span>(</span><span>script</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// 加载 KaTeX 和 Pseudocode.js</span></div></div><div><div><div>13</div></div><div><span>Promise</span><span>.</span><span>all</span><span>([</span></div></div><div><div><div>14</div></div><div><span>    </span><span>loadScript</span><span>(</span><span>'https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.js'</span><span>),</span></div></div><div><div><div>15</div></div><div><span>    </span><span>loadScript</span><span>(</span><span>'https://cdn.jsdelivr.net/npm/pseudocode@2.4.1/build/pseudocode.min.js'</span><span>)</span></div></div><div><div><div>16</div></div><div><span>]).</span><span>then</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>pseudocode</span><span> </span></span><span>===</span><span> </span><span>'undefined'</span><span>) {</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'Pseudocode.js not loaded'</span><span>);</span></div></div><div><div><div>19</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>    </span><span>// 匹配 Hexo 渲染后的伪代码块</span></div></div><div><div><div>22</div></div><div><span>    </span><span>const</span><span> </span><span>pseudocodes</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelectorAll</span><span>(</span><span>'figure.highlight table tr td.code pre code.hljs.pseudocode'</span><span>);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>// 渲染每个伪代码块</span></div></div><div><div><div>24</div></div><div><span>    </span><span>const</span><span> </span><span>options</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>lineNumber</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>26</div></div><div><span>        </span><span>lineNumberPunc</span><span>:</span><span> </span><span>':'</span><span>,</span></div></div><div><div><div>27</div></div><div><span>        </span><span>noEnd</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>28</div></div><div><span>        </span><span>titlePrefix</span><span>:</span><span> </span><span>'Algorithm'</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>pseudocodes</span><span>.</span><span>forEach</span><span><span>((</span><span>codeBlock</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span>        </span><span>try</span><span> {</span></div></div><div><div><div>32</div></div><div><span>            </span><span>const</span><span> </span><span>code</span><span> </span><span>=</span><span><span> </span><span>codeBlock</span><span>.</span></span><span>textContent</span><span>;</span></div></div><div><div><div>33</div></div><div><span>            </span><span>const</span><span> </span><span>container</span><span> </span><span>=</span><span><span> </span><span>codeBlock</span><span>.</span></span><span>closest</span><span>(</span><span>'figure.highlight'</span><span>);</span></div></div><div><div><div>34</div></div><div><span>            </span><span>const</span><span> </span><span>newContainer</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>'div'</span><span>);</span></div></div><div><div><div>35</div></div><div><span><span>            </span></span><span>newContainer</span><span>.</span><span>className</span><span> </span><span>=</span><span> </span><span>'pseudocode-container'</span><span>;</span></div></div><div><div><div>36</div></div><div><span><span>            </span></span><span>pseudocode</span><span>.</span><span>render</span><span><span>(</span><span>code</span><span>, </span><span>newContainer</span><span>, </span><span>options</span><span>);</span></span></div></div><div><div><div>37</div></div><div><span><span>            </span></span><span>container</span><span>.</span><span>parentNode</span><span>.</span><span>replaceChild</span><span><span>(</span><span>newContainer</span><span>, </span><span>container</span><span>);</span></span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>39</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'Error rendering pseudocode:'</span><span><span>, </span><span>error</span><span>);</span></span></div></div><div><div><div>40</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>42</div></div><div><span>}).</span><span>catch</span><span><span>(</span><span>error</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'Error loading required scripts:'</span><span><span>, </span><span>error</span><span>);</span></span></div></div><div><div><div>44</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这里主要是查找原页面 HTML 中的伪代码并渲染。具体可以参考 <a href="https://github.com/SaswatPadhi/pseudocode.js/blob/master/README.md" target="_blank">pseudocode官方文档</a>；</p></section></section><section><h2>步骤二：美化伪代码样式<a href="#步骤二美化伪代码样式"><span>#</span></a></h2><p>我在使用默认配置渲染时，会出现行号和标点换行的问题，如果出现这一问题，则在 <code>source/css/custom.css</code> 末尾添加如下样式，保证行号悬挂、缩进美观：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.pseudocode-container</span><span> </span><span>.ps-line</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>padding-left: </span><span><span>2.5</span><span>em</span></span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>text-indent: </span><span>0</span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>.pseudocode-container</span><span> </span><span>.ps-linenum</span><span> {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>position: </span><span>absolute</span><span> </span><span>!important</span><span>;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>/* 不强制 left: 0，保留 JS 计算的 left，兼容多层嵌套 */</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>min-width: </span><span><span>2</span><span>em</span></span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>text-align: </span><span>right</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>white-space: </span><span>nowrap</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>color: </span><span>#</span><span>607080</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>font-variant-numeric: </span><span>tabular-nums</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>line-height: </span><span>inherit</span><span>;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>步骤三：在 Markdown 中书写伪代码<a href="#步骤三在-markdown-中书写伪代码"><span>#</span></a></h2><p>直接在 Markdown 文件中使用如下格式：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>\begin{algorithm}</span></div></div><div><div><div>2</div></div><div><span>\caption{Quicksort}</span></div></div><div><div><div>3</div></div><div><span>\begin{algorithmic}</span></div></div><div><div><div>4</div></div><div><span>\PROCEDURE{Quicksort}{$A, p, r$}</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>\IF{$p &lt; r$}</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>\STATE $q = $ \CALL{Partition}{$A, p, r$}</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>\STATE \CALL{Quicksort}{$A, p, q - 1$}</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>\STATE \CALL{Quicksort}{$A, q + 1, r$}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>\ENDIF</span></div></div><div><div><div>10</div></div><div><span>\ENDPROCEDURE</span></div></div><div><div><div>11</div></div><div><span>\PROCEDURE{Partition}{$A, p, r$}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>\STATE $x = A[r]$</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>\STATE $i = p - 1$</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>\FOR{$j = p$ \TO $r - 1$}</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>\IF{$A[j] &lt; x$}</span></div></div><div><div><div>16</div></div><div><span><span>            </span></span><span>\STATE $i = i + 1$</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>\STATE exchange $A[i]$ with $A[j]$</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>\ENDIF</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>\ENDFOR</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>\STATE exchange $A[i+1]$ with $A[r]$</span></div></div><div><div><div>21</div></div><div><span>\ENDPROCEDURE</span></div></div><div><div><div>22</div></div><div><span>\end{algorithmic}</span></div></div><div><div><div>23</div></div><div><span>\end{algorithm}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>2. 常见问题与解决<a href="#2-常见问题与解决"><span>#</span></a></h1><ul>
<li><strong>行号换行/错位</strong>：请务必添加上文的 CSS，保证 <code>.ps-linenum</code> 不换行且悬挂。</li>
<li><strong>嵌套算法块行号对齐</strong>：推荐保留 Pseudocode.js 的 <code>left</code> 机制，CSS 不要强制 <code>left: 0</code>，以兼容多层嵌套。</li>
</ul><hr /><p>如有疑问，欢迎留言交流！</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/jsdelivr-github-image-hosting-acceleration/</id>
      <title type="text">使用jsDelivr加速Github图床</title>
      <published>2025-05-18T02:18:00.000Z</published>
      <updated>2025-05-18T02:18:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/jsdelivr-github-image-hosting-acceleration/"/>
      <summary type="text">对于个人博客，图片如果保存在本地，迁移时总是不太方便。一种比较好的方式就是使用图床，无需再担心迁移时的麻烦。像腾讯云、阿里云和七牛云等厂商都提供稳定但收费的 OSS 对象存储服务，国内免费的图床也有，不过多数有上传限制，或存在不稳定的因素（毕竟公益运营，存在跑路可能性），并且有些会对图片内容的合规性进行检测（比如二维…</summary>
      <content type="html"><![CDATA[<section><h1>前言<a href="#前言"><span>#</span></a></h1><p>对于个人博客，图片如果保存在本地，迁移时总是不太方便。一种比较好的方式就是使用图床，无需再担心迁移时的麻烦。像腾讯云、阿里云和七牛云等厂商都提供稳定但收费的 OSS 对象存储服务，国内免费的图床也有，不过多数有上传限制，或存在不稳定的因素（毕竟公益运营，存在跑路可能性），并且有些会对图片内容的合规性进行检测（比如二维码等内容是不被允许的），国外的图床多数国内访问容易被墙。</p><p>Github，作为知名代码托管平台，早就被发掘出图床的功能，但是一样存在国内联通性不佳的问题。因此，需要使用 CDN 来加速和优化图片的加载速度。</p><p>CDN，即内容分发网络（Content Delivery Network）是一种通过分布在全球各地的服务器节点，将内容快速、可靠地传递给用户的网络架构。它的核心目标是在尽可能靠近用户的位置提供服务，从而提高访问速度、稳定性并降低传输成本。</p><p>简单来说，CDN 会在多个地理位置部署缓存服务器，这些节点将原始服务器上的内容缓存下来。当用户访问某个内容时，CDN 会自动将请求引导至距离用户最近、最优的节点，从而实现快速响应和数据传输。</p><p>jsDelivr，作为一款开源且免费的公共 CDN，并且与 GitHub 和 NPM 紧密集成，不存在带宽限制和付费功能。Github 存储和 jsDelivr 加速，稳定快速免费高效的选择，你值得拥有。</p><p>有关 jsDelivr 的加速效果，可以看下图，至于有没有别的平替，反正我觉得是没什么同类竞品的，当然有钱且有备案 （其实我觉得整一个备案比下面的方法还折腾） ，也可以考虑国内 CDN。</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729203202624.png" alt="" /></p></section>
<section><h1>一、方法<a href="#一方法"><span>#</span></a></h1><section><h2>1.1 Github 存储部分<a href="#11-github-存储部分"><span>#</span></a></h2><section><h3>1.1.1 新建仓库<a href="#111-新建仓库"><span>#</span></a></h3><p>首先，需要新建一个仓库，<strong>仓库要求公开</strong>，别的无所谓。</p></section><section><h3>1.1.2 创建 Token<a href="#112-创建-token"><span>#</span></a></h3><p>进入 Settings <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> Developer Settings <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> Personal access tokens，然后，新建一个 classic token，然后勾选相应的权限，点击 Generate token，<strong>注意这里的 Token 只会生成一次，请妥善保存！</strong></p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729202937013.png" alt="" /></p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729203016809.png" alt="" /></p></section></section><section><h2>1.2 PicGo配置<a href="#12-picgo配置"><span>#</span></a></h2><section><h3>1.2.1 PicGo 下载<a href="#121-picgo-下载"><span>#</span></a></h3><p>PicGo 是一个用于快速上传图片并获取图片 URL 链接的工具，支持多种图床，并且支持自行开发适配其他图床。下载见 <a href="https://github.com/Molunerfinn/PicGo" target="_blank">PicGo</a>。</p></section><section><h3>1.2.2 图床配置<a href="#122-图床配置"><span>#</span></a></h3><p>下载完毕后，打开主窗口（默认可能在任务栏中，不显示），找到图床设置，选择 Github，填入相应配置，参考下图：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729203041333.png" alt="" /></p><p>有几个注意点：</p><ol>
<li>仓库名：即 <code>Github用户名/仓库名</code>；</li>
<li>分支名：一般默认为 <code>master</code>，但是如果你选择了其他分支，你需要在自定义域名部分作相应修改；</li>
<li>存储路径：这个无所谓；</li>
<li>自定义域名：
<ol>
<li>如果是默认分支，那么自定义域名为 <code>https://cdn.jsdelivr.net/gh/Github用户名/仓库名</code>；</li>
<li>如果是其他分支，那么自定义域名为 <code>https://cdn.jsdelivr.net/gh/Github用户名/仓库名@分支名</code>；</li>
</ol>
</li>
</ol><p>有关 jsDelivr 的具体细节，可以参考 <a href="https://www.jsdelivr.com/" target="_blank">jsDelivr 官网</a>；</p></section><section><h3>1.2.3 PicGo 设置<a href="#123-picgo-设置"><span>#</span></a></h3><p>你可以参考我对 PicGo 进行如下设置，这样，PicGo 就可以自动读取你剪切板中的图片，上传并生成 MarkDown 链接了。</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729203113493.png" alt="" /></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/git-micro-operations-self-rescue-guide/</id>
      <title type="text">Git 微操自救指南</title>
      <published>2025-05-16T16:17:00.000Z</published>
      <updated>2025-05-16T16:17:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/git-micro-operations-self-rescue-guide/"/>
      <summary type="text">使用 Git 进行版本管理时，在通过 commit 提交并 push 了大量垃圾（bushi）后，然后看到提交历史，往往对自己的 push 垃圾的行为后悔不已。那么有对 git 不太熟悉的就要问了，主包主包，有什么悔棋的方法吗？ 有的兄弟，有的。像这样的方法，这篇博客一共整理了 9 个。</summary>
      <content type="html"><![CDATA[<p>使用 Git 进行版本管理时，在通过 commit 提交并 push 了大量垃圾（bushi）后，然后看到提交历史，往往对自己的 push 垃圾的行为后悔不已。那么有对 git 不太熟悉的就要问了，主包主包，有什么悔棋的方法吗？</p>
<p>有的兄弟，有的。像这样的方法，这篇博客一共整理了 9 个。</p>
<blockquote><p>目前仅整理了部分内容，学习 Git 不亚于新学一门编程语言，学不完，根本学不完。</p></blockquote>
<div><div><div></div><div>Warning</div></div><div><p><strong>！！！禁止修改多人共用的远端分支！！！</strong> 你在使用任何 Git 操作时都应该明确这一点，如果一条远端分支有多人共用，那么不要在上面执行 reset 、rebase 等会修改这条分支已经存在的 commit object 的命令。具体原因可以参考 <a href="https://stackoverflow.com/questions/46656240/understanding-git-rebase-and-the-golden-rule" target="_blank">understanding-git-rebase-and-the-golden-rule</a> ；</p></div></div>
<section><h1>一、修改最近一个提交<a href="#一修改最近一个提交"><span>#</span></a></h1><p>开发过程中，提交完发现有一些临时的 log 等文件忘记去掉，或者有一些文件忘了提交，甚至 commit 信息打了错别字或者单纯写的不满意，这些情形是常有的。回滚回前一次提交再重新提交当然没啥问题，以下提供一种更清晰快捷的方法。</p><section><h2>1.1 操作步骤<a href="#11-操作步骤"><span>#</span></a></h2><p>假如此时你已经提交了部分内容（无论有没有推送到远程仓库）。比如：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 修改了几个文件</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>add</span><span> </span><span>a.py</span><span> </span><span>b.py</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>"feat: 实现初始功能"</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 又改了 a.py，或者想把别的文件一起补进去上一次提交</span></div></div><div><div><div>6</div></div><div><span>git</span><span> </span><span>add</span><span> </span><span>a.py</span></div></div><div><div><div>7</div></div><div><span>git</span><span> </span><span>commit</span><span> </span><span>--amend</span><span> </span><span># 然后输入新的提交信息</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>不过需要注意的是 <code>--amend</code> 会<strong>覆盖</strong>上一次提交，你可以观察到提交的哈希值变了，所以如果你已经推送到远程仓库，需要使用 <code>git push --force</code> 强推新的提交。</p></section></section>
<section><h1>二、提交一个文件中的部分修改<a href="#二提交一个文件中的部分修改"><span>#</span></a></h1><p>你可以使用 <code>git add -p</code>（patch 模式）或 <code>git add -i</code>（interactive 模式）来<strong>选择性地将文件的部分修改加入暂存区</strong>，而不是整个文件。这对于精细控制提交、按功能分组提交代码非常有用。</p><ol>
<li>执行 <code>git add -p &lt;文件名&gt;</code> ；</li>
</ol><p>你可以先执行 <code>git add -i</code> ，进入交互菜单，按数字键选择 patch 模式。（interactive 模式还提供多种操作，这里暂不展开）</p><ol>
<li>Git 会逐个展示该文件的修改块（hunk），你可以选择是否添加这个修改；</li>
</ol><p>比如，某一次的修改提示为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>diff --git a/index.js b/index.js</span></div></div><div><div><div>2</div></div><div><span>@@ -1,6 +1,7 @@</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>function add(a, b) {</span></div></div><div><div><div>4</div></div><div><span>+  console.log('计算中...');</span></div></div><div><div><div>5</div></div><div><span><span>   </span></span><span>return a + b;</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>Stage this hunk [y,n,q,a,d,s,e,?]?</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>它会提供给你如下选项：</p>

<table><thead><tr><th><strong>输入</strong></th><th><strong>含义</strong></th></tr></thead><tbody><tr><td>y</td><td>暂存这个 hunk（即这部分更改）</td></tr><tr><td>n</td><td>不暂存这个 hunk</td></tr><tr><td>q</td><td>退出</td></tr><tr><td>a</td><td>暂存所有剩余的 hunk</td></tr><tr><td>d</td><td>不暂存任何剩余的 hunk</td></tr><tr><td>s</td><td>拆分这个 hunk 为更小的块（如果可能）</td></tr><tr><td>e</td><td>手动编辑这个 hunk（精确选择添加哪些行）</td></tr><tr><td>?</td><td>显示帮助信息</td></tr></tbody></table><p>一般，如果你想要提交部分内容，推荐使用 <code>s</code> 进行拆分。</p></section>
<section><h1>三、普通版：合并多个提交为一个提交<a href="#三普通版合并多个提交为一个提交"><span>#</span></a></h1><p>大部分人合并可能都会用 <code>git reset</code> 命令实现：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>reset</span><span> </span><span>--soft</span><span> </span><span>HEAD~X</span><span> &amp;&amp; </span><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>'xxx'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是 <code>git reset</code> 只能合并最近的 X 个提交。如果你要整理整个提交历史呢？</p><p><code>git rebase</code> 是 Git 中非常强大的命令，用于<strong>整理提交历史、合并分支时避免产生多余的 merge commit，甚至于还能对其中的一些提交进行排序</strong>，提高提交历史的清晰度。</p><section><h2>3.1 操作步骤<a href="#31-操作步骤"><span>#</span></a></h2><p>现在，假如你的提交历史如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>A - B - C - D - E - F - G - H - I - J (HEAD)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你现在想要合并 C、D、E 这三个提交成一个提交。</p><ol>
<li>首先，找到你所希望合并的提交中的最早一个提交的上一个提交（本情形中是 B）；</li>
</ol><p>可以使用该命令查看节点与其对应的哈希值：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>log</span><span> </span><span>--oneline</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后可以看到类似以下内容：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>j9k8l7  J</span></div></div><div><div><div>2</div></div><div><span>i8h7g6  I</span></div></div><div><div><div>3</div></div><div><span>...</span></div></div><div><div><div>4</div></div><div><span>e5d4c3  E</span></div></div><div><div><div>5</div></div><div><span>d4c3b2  D</span></div></div><div><div><div>6</div></div><div><span>c3b2a1  C</span></div></div><div><div><div>7</div></div><div><span>b2a1z0  B   ← ← 这个是你需要的基准点</span></div></div><div><div><div>8</div></div><div><span>a1z0y9  A</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>执行 rebase 命令：<code>git rebase -i b2a1z0</code> ；</li>
</ol><p>在执行完以上命令后，终端会自动跳转到 Git 的 Vim 编辑器；</p><ol>
<li>编辑提交列表；</li>
</ol><p>Vim 编辑器会打开如下列表（从旧到新）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>pick c3b2a1 C</span></div></div><div><div><div>2</div></div><div><span>pick d4c3b2 D</span></div></div><div><div><div>3</div></div><div><span>pick e5d4c3 E</span></div></div><div><div><div>4</div></div><div><span>pick ...    （后面的如果不希望合并就不用管）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>改为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>pick c3b2a1 C</span></div></div><div><div><div>2</div></div><div><span>squash d4c3b2 D</span></div></div><div><div><div>3</div></div><div><span>squash e5d4c3 E</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后保存；</p><ol>
<li>编辑提交信息，输入你希望保留的新提交说明，保存退出即可完成；</li>
<li>推送到远程仓库：<code>git push --force-with-lease</code> ；</li>
</ol></section><section><h2>3.2 注意点<a href="#32-注意点"><span>#</span></a></h2><ol>
<li>如果你在上述操作过程中有任何失误，可以运行 <code>git rebase --abort</code> 及时恢复到变基以前的状态；</li>
<li>⚠️ <code>rebase</code> 会导致新的 commit 节点产生，所以切记不要对多人共用的远端分支进行 rebase 。</li>
</ol></section></section>
<section><h1>四、进阶版：合并多个提交为大于一个提交<a href="#四进阶版合并多个提交为大于一个提交"><span>#</span></a></h1><p>其实与上面的操作是类似的，只不过你不需要重复上述步骤整理单独一个提交，这就是 <code>git rebase -i</code> 的强大之处 —— 你可以精确控制哪些 commit 合并，哪些保留。</p><p>现在假设你的提交历史为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>A - B - C - D - E - F - G (HEAD)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你想把：</p><ul>
<li>B+C+D 合并为一个提交</li>
<li>E+F 合并为另一个提交</li>
<li>保留 G 不动</li>
</ul><ol>
<li>仍然找所有你希望变更的提交里最早的前一个提交并变基：</li>
</ol><p>本情形中就是 <code>git rebase -i &lt;A的commit哈希&gt;</code> ；</p><p>然后会进入编辑提交列表，形如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>pick bbbbbbb B</span></div></div><div><div><div>2</div></div><div><span>pick ccccccc C</span></div></div><div><div><div>3</div></div><div><span>pick ddddddd D</span></div></div><div><div><div>4</div></div><div><span>pick eeeeeee E</span></div></div><div><div><div>5</div></div><div><span>pick fffffff F</span></div></div><div><div><div>6</div></div><div><span>pick ggggggg G</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>编辑提交历史；</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>pick bbbbbbb B</span></div></div><div><div><div>2</div></div><div><span>squash ccccccc C</span></div></div><div><div><div>3</div></div><div><span>squash ddddddd D</span></div></div><div><div><div>4</div></div><div><span>pick eeeeeee E</span></div></div><div><div><div>5</div></div><div><span>squash fffffff F</span></div></div><div><div><div>6</div></div><div><span>pick ggggggg G</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>说明：</p><ul>
<li>第一组：B（pick）+ C（squash）+ D（squash） → 合并成 1 个提交</li>
<li>第二组：E（pick）+ F（squash） → 合并成另一个提交</li>
<li>第三组：G 保持不动</li>
</ul><ol>
<li>编辑 commit message</li>
</ol><p>Git 会依次提示你编辑每一组的合并 commit message ；</p></section>
<section><h1>五、找回丢失的 commit 节点或分支<a href="#五找回丢失的-commit-节点或分支"><span>#</span></a></h1><section><h2>5.1 原理<a href="#51-原理"><span>#</span></a></h2><p>首先，你需要知道，在你通过 Git 合并或删除一些提交或分支后，远程仓库和本地仓库的提交历史看似没有内容，确实删除了，但并非不可恢复。</p><p>分支和提交均有唯一的哈希值，虽然远程仓库不保存删除或覆盖的内容，但是 Git 的强大之处就在于，它提供了 Reflog 命令，它可以查看你本地 Git 仓库中所有分支引用的变动历史，<strong>即使是你已经被“删除”的 commit 或 reset 过的状态</strong>，它也能找到。（只要还没被垃圾回收，Git 默认在本地保留 reflog 数据 90 天）</p></section><section><h2>5.2 具体步骤<a href="#52-具体步骤"><span>#</span></a></h2><ol>
<li>执行命令：<code>git reflog</code> ；</li>
</ol><p>会输出以下信息：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>8a2c28d (HEAD -&gt; main) HEAD@{0}: reset: moving to origin/main</span></div></div><div><div><div>2</div></div><div><span>fbf9a61 HEAD@{1}: commit: 修复BUG</span></div></div><div><div><div>3</div></div><div><span>77d0d44 HEAD@{2}: commit: 添加功能A</span></div></div><div><div><div>4</div></div><div><span>e4d0c1b HEAD@{3}: rebase -i (finish): returning to refs/heads/main</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中，<code>HEAD@{0}</code> 是当前 HEAD 的变更位置（0 表示最新）。</p><ol>
<li>找到你需要恢复的节点的哈希值，恢复该版本；</li>
</ol><p>比如：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>reset</span><span> </span><span>--hard</span><span> </span><span>&lt;commit哈希&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或者：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>checkout</span><span> </span><span>&lt;commit哈希&gt;</span></div></div><div><div><div>2</div></div><div><span># 或者创建个分支保住它</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>checkout</span><span> </span><span>-b</span><span> </span><span>rescued-branch</span><span> </span><span>&lt;commit哈希&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>六、获得干净的工作空间<a href="#六获得干净的工作空间"><span>#</span></a></h1><p>假如你在本地修改了一些文件，但又没必要上传，或者只是临时用的，在实际工作中仍需与远程仓库一致，这时候，你当然可以通过 <code>git reset --hard HEAD</code> 等直接丢弃本地修改，但万一等你重置完又想用了呢，这些文件并没有提交到 Git 历史，自然无法通过第三部分中的操作恢复。</p><section><h2>6.1 原理<a href="#61-原理"><span>#</span></a></h2><p><code>git stash</code> 是 Git 中的一个命令，用来<strong>临时保存当前工作目录的修改</strong>，让你可以切换分支或执行其他操作，而不会丢失当前更改。它非常适用于“你正在开发功能 A，突然需要切换到主分支修个 Bug，但你又不想提交当前没完成的代码。”的这种情形。</p><p>一句话概括，<code>git stash</code> 让你可以随时“藏”起当前代码进度，切分支不怕打断，再“拿”回来继续写。</p></section><section><h2>6.2 具体步骤<a href="#62-具体步骤"><span>#</span></a></h2><ol>
<li>执行 <code>git stash</code> ，暂存当前修改（<strong>包括已暂存和未暂存的</strong>）；</li>
</ol><p>或者你也可以像 commit 一样，增加信息，方便区分：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>stash</span><span> </span><span>push</span><span> </span><span>-m</span><span> </span><span>"临时代码：XX模块"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>还有一些参数可以选择：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>stash</span><span> </span><span>-k</span><span>       </span><span># 只保存未暂存的文件</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>stash</span><span> </span><span>-u</span><span>       </span><span># 包含 untracked</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>stash</span><span> </span><span>-a</span><span>       </span><span># 包含 untracked + ignored</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>恢复最近一次 stash （同时会将其从 stash list 中删除）；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>stash</span><span> </span><span>pop</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你也可以通过 <code>git stash apply stash@{1}</code> 恢复特定一次的 stash ；</p><ol>
<li>可以通过 <code>git stash list</code> 查看 stash 列表；</li>
<li>如果希望删除某次 stash ，可以通过 <code>git stash drop stash@{0}</code> ；</li>
<li>如果希望清空所有 stash ，可以通过 <code>git stash clear</code> ；</li>
</ol></section></section>
<section><h1>七、占位<a href="#七占位"><span>#</span></a></h1></section>
<section><h1>八、占位<a href="#八占位"><span>#</span></a></h1></section>
<section><h1>九、占位<a href="#九占位"><span>#</span></a></h1></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/prompt-engineering/</id>
      <title type="text">写好提示词</title>
      <published>2025-04-15T12:32:00.000Z</published>
      <updated>2025-04-15T12:32:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/prompt-engineering/"/>
      <summary type="text">Prompt Engineering = improving prompts to get more reliable, higher-quality outputs from language models. Key components = Action verb at start + direct task s…</summary>
      <content type="html"><![CDATA[<p>Prompt Engineering = improving prompts to get more reliable, higher-quality outputs from language models.</p>
<section><h2>1. Being Clear and Direct<a href="#1-being-clear-and-direct"><span>#</span></a></h2><p>Key components = Action verb at start + direct task statement + expected output details.</p><ul>
<li>避免提问，直接告诉模型要做什么。</li>
<li>例如：Instead of “Can you summarize this article?”, say “Summarize the following article in 3 sentences: [article text]”.</li>
</ul><p>Examples:</p><ul>
<li>“Write three paragraphs about how solar panels work”</li>
<li>“Identify three countries that use geothermal energy and for each include generation stats”</li>
<li>“Generate a one day meal plan for an athlete that meets their dietary restrictions”</li>
</ul></section>
<section><h2>2. Being Specific<a href="#2-being-specific"><span>#</span></a></h2><ol>
<li>Type A (Attributes) = list qualities/attributes desired in output (length, structure, format)
<ul>
<li>适用于一般情况下的大多数任务，帮助模型理解输出的具体要求。</li>
</ul>
</li>
<li>Type B (Steps) = provide specific steps for model to follow in reasoning process
<ul>
<li>通常用于复杂任务，帮助模型分解问题并逐步解决。</li>
<li>需要模型有更广阔的视角，能够考虑更多因素并进行更深入的分析。</li>
</ul>
</li>
</ol></section>
<section><h2>3. Structure with XML Tags<a href="#3-structure-with-xml-tags"><span>#</span></a></h2><p>一种已经在现代模型中能够以几乎确定性的方式被正确解析的结构化提示方法，通过结构化的标签区分内容边界，而非依靠模型对自然语言提示的理解推理边界。</p><p>对于传统的自然语言提示，边界是隐式的，以理解”First analyze the data then provide recommendations.”这句话的语义来推断出”analyze”和”recommendations”为例，逐词解析时对每一步的隶属关系都保持着“不确定性”，假设模型在解析时对每个词的概率分布如下：</p>

<table><thead><tr><th>词</th><th>P(analysis)</th><th>P(recommendations)</th></tr></thead><tbody><tr><td>First</td><td>0.95</td><td>0.05</td></tr><tr><td>analyze</td><td>0.9</td><td>0.1</td></tr><tr><td>the</td><td>0.85</td><td>0.15</td></tr><tr><td>data</td><td>0.8</td><td>0.2</td></tr><tr><td>then</td><td>0.4</td><td>0.6</td></tr><tr><td>provide</td><td>0.2</td><td>0.8</td></tr><tr><td>recommendations</td><td>0.1</td><td>0.9</td></tr></tbody></table><p>其中，“then”是一个边界词（boundary token），这就是边界不确定性出现的时刻，这种不确定性会向前传播，导致模型可能错误地将”provide”归到”analysis”的部分，从而导致后续对”recommendations”的理解也受到影响。</p><p>而 XML 标签直接在算法层面，让模型能直接通过语法解析来区分不同部分的内容，消除了边界不确定性，例如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>analysis</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>analyze the data</span></div></div><div><div><div>3</div></div><div><span>&lt;/</span><span>analysis</span><span>&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>&lt;</span><span>recommendations</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>provide recommendations</span></div></div><div><div><div>7</div></div><div><span>&lt;/</span><span>recommendations</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>它的可能解析结果如下：</p>

<table><thead><tr><th>token</th><th>P(analysis)</th><th>P(recommendations)</th></tr></thead><tbody><tr><td><code>&lt;analysis&gt;</code></td><td>1</td><td>0</td></tr><tr><td>analyze</td><td>1</td><td>0</td></tr><tr><td>the</td><td>1</td><td>0</td></tr><tr><td>data</td><td>1</td><td>0</td></tr><tr><td><code>&lt;/analysis&gt;</code></td><td>1</td><td>0</td></tr><tr><td><code>&lt;recommendations&gt;</code></td><td>0</td><td>1</td></tr><tr><td>provide</td><td>0</td><td>1</td></tr><tr><td>recommendations</td><td>0</td><td>1</td></tr><tr><td><code>&lt;/recommendations&gt;</code></td><td>0</td><td>1</td></tr></tbody></table><p>概率经过标签直接跳变，直接阻断了边界不确定性的传播。</p><p>几点建议：</p><ul>
<li>Tag naming = Use descriptive, specific tag names (e.g., “sales_records” better than “data”) to provide context about content nature.</li>
<li>Example use case = Debugging prompt with mixed code and documentation becomes clearer when separated into &lt;my_code&gt; and  tags.</li>
</ul></section>
<section><h2>4. Providing Examples<a href="#4-providing-examples"><span>#</span></a></h2><p>即 One-shot/Multi-shot，通过提供示例来引导模型理解任务要求和预期输出格式。</p><ul>
<li>为边界情况提供示例，帮助模型理解如何处理特殊或复杂的输入。</li>
<li>提供的示例包括对为什么该输出理想的解释。</li>
<li>将示例放在主要提示和指导原则之后。</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/game-theory-study-notes/</id>
      <title type="text">一些有关Game Theory(博弈论)的学习记录</title>
      <published>2025-04-05T08:22:00.000Z</published>
      <updated>2025-04-05T08:22:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/game-theory-study-notes/"/>
      <summary type="text">以下是笔者学习【耶鲁大学】博弈论 课程中整理的内容，因为笔者实在是懒的自己找书啃。有些内容可能结合自己的研究方向（Multiple-Agent-System）简单拓展。 关于耶鲁大学《博弈论》课程上的积分游戏的具体推导见下，懒地打了。。。 定义符号后，我们给出更严格的严格优势策略的定义：</summary>
      <content type="html"><![CDATA[<p><strong>前言</strong>：</p>
<p>以下是笔者学习<a href="https://www.bilibili.com/video/BV1xY411Y7Wj/" target="_blank">【耶鲁大学】博弈论</a> 课程中整理的内容，因为笔者实在是懒的自己找书啃。有些内容可能结合自己的研究方向（Multiple-Agent-System）简单拓展。</p>
<section><h1>一、Prisoner’s Dilemma 囚徒困境<a href="#一prisoners-dilemma-囚徒困境"><span>#</span></a></h1><section><h2>1.1 四个重要结论<a href="#11-四个重要结论"><span>#</span></a></h2><div><div><div></div><div>Important</div></div><div><p><strong>定义 1.1</strong>：<strong>Strictly Dominant Strategy 严格优势策略</strong></p><p>假如策略<span><span>α\alpha</span><span><span><span></span><span>α</span></span></span></span>在无论对手选择何种应对的情况下的收益都高于策略<span><span>β\beta</span><span><span><span></span><span>β</span></span></span></span>，则称策略<span><span>α\alpha</span><span><span><span></span><span>α</span></span></span></span>为相对<span><span>β\beta</span><span><span><span></span><span>β</span></span></span></span>的严格优势策略；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>结论 1.1</strong>：<strong>不要使用严格劣势策略</strong></p><p>我们假设参与博弈的行为人都是理性的（和经济学中定义的理性经济人类似），都希望追求更高的收益（当前，每个人衡量收益大小的标准有不同，对收益的看法显然也会影响决策）；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>结论 1.2</strong>：<strong>理性的选择可能导致更糟的结果</strong></p><p>显然，如果双方均追求对于个人收益的最大化，可能导致双输；</p></div></div><div><div><div></div><div>Important</div></div><div><p><strong>如何破解囚徒困境？</strong></p><p>沟通并不能解决这个困境，在缺乏强制力介入的情况下，沟通缺乏意义，可能有效的方式是有强制力的合同（比如书面合同）、重复博弈、教育。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>结论 1.3</strong>：<strong>如欲得之，必先知之 If you want to get it, you must know it first</strong></p><p>如果我们不知道一个人或一场博弈中的收益情况，那么我们就不可能获得收益；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>结论 1.4</strong>：<strong>换位思考 Put yourself  in others’ shoes and try to figure out what they’ll do</strong></p><p>假如我们知道另一方的收益情况，那么如果我们确认对方会根据 <strong>结论 1.1</strong> 选择某种策略，那么我们可以根据对方的选择选择对于自己收益更高的那种，即使站在自己的角度，并没有严格优势策略；</p></div></div></section><section><h2>1.2 Grade Game 积分游戏<a href="#12-grade-game-积分游戏"><span>#</span></a></h2><p>关于耶鲁大学《博弈论》课程上的积分游戏的具体推导见下，懒地打了。。。</p><section><h3>积分游戏推导<a href="#积分游戏推导"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729203901926.png" alt="" />
<img src="https://webp-pic.yokumi.cn/2025/07/20250729212436159.png" alt="" /></p></section></section><section><h2>1.3 Ingredients of a game 博弈的要素<a href="#13-ingredients-of-a-game-博弈的要素"><span>#</span></a></h2><ul>
<li><strong>Players 参与者</strong>；</li>
<li><strong>Strategies 策略</strong>；
<ul>
<li><span><span>sis_i</span><span><span><span></span><span><span>s</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>:  <span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>的某个特定策略；</li>
<li><span><span>SiS_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>: <span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>的策略集合；</li>
<li><span><span>SS</span><span><span><span></span><span>S</span></span></span></span>: 一次博弈，即所有参与者的策略组合；</li>
</ul>
</li>
<li><strong>Payoff 收益</strong>；</li>
<li><strong>Assumption 假设</strong>：每个参与者都知道其他人的可能策略和收益，即博弈者之间信息透明；</li>
<li><span><span>S−iS_{-i}</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>: 一次博弈中除了<span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>之外的其他所有参与者的策略；</li>
</ul><p>定义符号后，我们给出更严格的严格优势策略的定义：</p><div><div><div></div><div>Important</div></div><div><p><strong>定义 1.1 Plus</strong>：<strong>Strictly Dominant Strategy 严格优势策略</strong></p><p><span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>‘s strategy <span><span>Si′S^\prime_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is strictly dominated by <span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>‘s strategy <span><span>SiS_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> if <span><span>Ui(Si,S−i)&gt;Ui(Si′,S−i)U_i(S_i, S_{-i}) &gt; U_i(S^\prime_i, S_{-i})</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> for all <span><span>S−iS_{-i}</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>；</p></div></div></section><section><h2>1.4 Hannibal 汉尼拔<a href="#14-hannibal-汉尼拔"><span>#</span></a></h2><p>Ben教授通过汉尼拔进军罗马的例子，引入了弱优势策略，弱优势策略允许了一部分策略组下收益相等而不是严格大于；</p><div><div><div></div><div>Important</div></div><div><p><strong>定义 1.1 Extension</strong>：<strong>Weakly Dominant Strategy 弱优势策略</strong></p><p><span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>‘s strategy <span><span>Si′S^\prime_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is weakly dominated by <span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>‘s strategy <span><span>SiS_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> if <span><span>Ui(Si,S−i)≥Ui(Si′,S−i)U_i(S_i, S_{-i}) \ge U_i(S^\prime_i, S_{-i})</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>≥</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> for all <span><span>S−iS_{-i}</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>, and <span><span>Ui(Si,S−i)&gt;Ui(Si′,S−i)U_i(S_i, S_{-i}) &gt; U_i(S^\prime_i, S_{-i})</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>′</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> for some <span><span>S−iS_{-i}</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>;</p></div></div><p>关于汉尼拔的具体推导见下：</p><section><h3>汉尼拔<a href="#汉尼拔"><span>#</span></a></h3><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729212343136.png" alt="" /></p></section></section><section><h2>1.5 Iterative Deletion 迭代剔除劣势策略<a href="#15-iterative-deletion-迭代剔除劣势策略"><span>#</span></a></h2><p>该策略要求参与者首先找到所有劣势策略，剔除它们，然后再重新审视整个博弈，如此往复。一个具体的例子如下：</p><p>每人选择一个1到100之间的数字，谁选的数字最接近平均数的三分之二，则获得胜利。</p><ul>
<li><strong>第一层</strong>：假设大家选取的数字是在 0 ～ 100 间随机分布的，那么 average 约等于50，50的2/3应该是33左右；但是问题也很明显，大家不会都进行随机选择；</li>
<li><strong>第二层</strong>：我认为别人都按照第一层的思路进行思考，即大部分人都会选择33，那么我应该选择 33 * 2/3 = 22；</li>
<li><strong>第三层</strong>：从这一层开始，使用了博弈论的框架，即假设参与者都是理性的。那么，选择大于67的数字属于弱劣势策略（除非大家均选择100）；那么大于67的数字就被剔除了；按照这个思路，我选择45；</li>
<li><strong>第四层</strong>：如果大家都考虑到了上面一层，那么基于第三层，现在选择大于45的也变成了弱劣势策略；按照这个思路，我应该选择30；</li>
</ul><p>按照这个逻辑一直持续下去，30 ～ 20、20 ～ 13，不断持续剔除下去，那么最终所有人都会选择1；</p><p>但1就是正确答案吗？</p><p>得到1需要反复迭代、剔除，即需要我不断知道别人都想到了上一层，即我知道你知道我知道你知道（无限套娃）我是理性的，即 <strong>Common Knowledge 共同知识</strong> 。</p><div><div><div></div><div>Important</div></div><div><p><strong>定义 1.5</strong>：<strong>共同知识</strong></p><p>共同知识是指某个信息或事件不仅被所有参与者知晓，而且所有参与者都知道其他参与者也知道该信息，并且知道其他参与者也知道其他人知道该信息，如此无限递归。</p><p>即：所有人都是理性的；所有人都知道所有人是理性的；所有人都知道所有人都知道所有人是理性的……</p></div></div></section><section><h2>1.6 The Median-Voter Theorem 中位选民定理<a href="#16-the-median-voter-theorem-中位选民定理"><span>#</span></a></h2><p>该定理事实上只是迭代剔除劣势策略在政治学上的一个应用。并且该模型还是简化了现实问题，存在不少问题；</p><p>首先，对于2个候选人A和B，他们的立场用 1 ～ 10 数字表示，假定每个数字对应10%的选民，选民仅按立场的接近程度进行投票；如果立场的接近程度相同，则一半一半；候选人的目标就是最大化选票，即收益；</p><p>容易发现，1和10为劣势策略，具体推导如下：如果A选择2，那么</p><ol>
<li>当B选1时，<span><span>UA(2,1)=90%&gt;UA(1,1)=50%U_A(2,1) = 90\% &gt; U_A(1,1) = 50\%</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>2</span><span>,</span><span></span><span>1</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>90%</span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>1</span><span>,</span><span></span><span>1</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>50%</span></span></span></span>；</li>
<li>当B选2时，<span><span>UA(2,2)=50%&gt;UA(1,2)=10%U_A(2,2) = 50\% &gt; U_A(1,2) = 10\%</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>2</span><span>,</span><span></span><span>2</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>50%</span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>1</span><span>,</span><span></span><span>2</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>10%</span></span></span></span>；</li>
<li>当B选3时，<span><span>UA(2,3)=20%&gt;UA(1,3)=15%U_A(2,3) = 20\% &gt; U_A(1,3) = 15\%</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>2</span><span>,</span><span></span><span>3</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>20%</span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>1</span><span>,</span><span></span><span>3</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>15%</span></span></span></span>；</li>
<li>当B选4时，<span><span>UA(2,4)=25%&gt;UA(1,4)=20%U_A(2,4) = 25\% &gt; U_A(1,4) = 20\%</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>2</span><span>,</span><span></span><span>4</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>25%</span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>1</span><span>,</span><span></span><span>4</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>20%</span></span></span></span>；</li>
</ol><p>以此类推，后面都是2优于1并且均相差<span><span>5%5\%</span><span><span><span></span><span>5%</span></span></span></span>；</p><p>选2严格优于选1，根据对称性，选9也严格优于选10。</p><p>越接近中间就优于两侧吗？并不，可以算一个例子：如果A选择3，那么</p><p>当B选择1时，<span><span>UA(3,1)=85%&lt;UA(2,1)=90%U_A(3,1) = 85\% &lt; U_A(2, 1) = 90\%</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>3</span><span>,</span><span></span><span>1</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>85%</span><span></span><span>&lt;</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>2</span><span>,</span><span></span><span>1</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>90%</span></span></span></span>，即A选3并不严格优于选2；</p><p>但是如果按照迭代剔除的思想，剔除劣势策略1和10之后，那么接下来策略2和9就变成了劣势策略，以此类推，最优的策略是选5和选6；这就是中位选民定理；</p><p>这个模型存在以下问题：</p><ol>
<li>选民并非平均分布；</li>
<li>选民并非完全根据立场来投票，即考量因素往往是多维度的；</li>
<li>选民往往会根据候选人过去的行为判断立场而并非按候选人所声称的；</li>
<li>选民存在弃票；</li>
<li>候选人往往大于2个；</li>
<li>大选之前还有初选；</li>
<li>…</li>
</ol></section><section><h2>1.7 Best Response 最优对策<a href="#17-best-response-最优对策"><span>#</span></a></h2><p>如果站在双方的角度，都不存在严格最优对策，那么站在我的角度，假设另一方采取某策略的概率是<span><span>pp</span><span><span><span></span><span>p</span></span></span></span>，据此计算我选择每个策略的期望收益；</p></section></section>
<section><h1>二、Nash Equilibrium 纳什均衡<a href="#二nash-equilibrium-纳什均衡"><span>#</span></a></h1><section><h2>2.1 Penalty Kick Game 足球比赛<a href="#21-penalty-kick-game-足球比赛"><span>#</span></a></h2><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729212325685.png" alt="" /></p><p>考虑以上情形。数字表示进球的可能性，比如 4 代表点球者有 40% 点进，对于守门员就是 40% 失球，用 -4 表示。按照 1.7 节中的最优对策，可以画出点球者使用 3 种不同策略时，期望收益和守门员扑向右侧的概率的关系。</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729212306513.png" alt="" /></p><ul>
<li>从图中可见，点球者向中间射门，在守门员采取任何扑救的情况下，都不是最优策略；</li>
</ul><div><div><div></div><div>Note</div></div><div><p><strong>结论 2.1</strong>：</p><p>不要选择在任何情况下都不是最优对策的策略。</p></div></div><p>当然，这个模型还是相当简化的。</p><div><div><div></div><div>Important</div></div><div><p>Def <span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>‘s strategy <span><span>Si′′S^{\prime\prime}_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is a BR (Best Response) to the strategy <span><span>S−iS_{-i}</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> of other players if <span><span>Ui(Si′′,S−i)≥Ui(Si′,S−i)U_i(S^{\prime\prime}_i, S_{-i}) \ge U_i(S^{\prime}_i, S_{-i})</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>≥</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> for all <span><span>Si′∈SiS^{\prime}_i \in S_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∈</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>.</p></div></div><p>定义可能并不是很好理解，简单来说，就是对于所有玩家 <span><span>ii</span><span><span><span></span><span>i</span></span></span></span> 的可能策略 <span><span>Si′S^{\prime}_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，如果策略 <span><span>Si′′S^{\prime\prime}_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的效用不低于其他策略的效用，那它就是一个最优应对；换句话说，<span><span>S′′iS^{\prime\prime}i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>′′</span></span></span></span></span></span></span></span></span><span>i</span></span></span></span> 是使得 <span><span>Ui(Si,S−i)U_i(S_i, S{-i})</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>S</span><span><span>−</span><span>i</span></span><span>)</span></span></span></span> 最大的策略。</p><p>还有一个更广义的定义：</p><div><div><div></div><div>Important</div></div><div><p>Def <span><span>PlayeriPlayer_i</span><span><span><span></span><span>P</span><span>l</span><span>a</span><span>y</span><span>e</span><span><span>r</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>‘s strategy <span><span>Si′′S^{\prime\prime}_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is a BR to the belief <span><span>PP</span><span><span><span></span><span>P</span></span></span></span> about the others’ choices if <span><span>EUi(Si′′,P)≥EUi(Si′,P)EU_i(S^{\prime\prime}_i, P) \ge EU_i(S^{\prime}_i, P)</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>P</span><span>)</span><span></span><span>≥</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>P</span><span>)</span></span></span></span> for all <span><span>Si′∈SiS^{\prime}_i \in S_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∈</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>.</p></div></div><p>这和上述情形类似，事实上就是引入了<strong>不确定性</strong>；当其他玩家的选择不再是确定的，而是存在一种概率信念 <span><span>PP</span><span><span><span></span><span>P</span></span></span></span>（即“我认为对手可能这么选的概率是多少”），我们就不能只考虑一个确定的对手策略组合，而是要对可能结果取期望。(马上就要引入纳什均衡的主题了)</p></section><section><h2>2.2 Partnership Game 商业合作<a href="#22-partnership-game-商业合作"><span>#</span></a></h2><p>这种博弈常用于商业合作，甚至于任何合作项目中，用来分析个人的努力如何影响总收益、合作是否可持续、是否存在“搭便车”现象等。</p><blockquote><p>妈的，我寻思这不是小组作业吗？</p></blockquote><p>一个典型的模型结构如下：</p><ul>
<li>设有两个玩家 <span><span>i=1,2i = 1,2</span><span><span><span></span><span>i</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span><span>,</span><span></span><span>2</span></span></span></span>，每人选择一个努力水平 <span><span>ei∈[0,4]e_i \in [0,4]</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∈</span><span></span></span><span><span></span><span>[</span><span>0</span><span>,</span><span></span><span>4</span><span>]</span></span></span></span>，努力有成本，但共同创造总收益；</li>
</ul><ul>
<li><strong>总产出函数</strong>（合作创造的收益）：<span><span>Π(e1,e2)=α(e1+e2+γe1e2)\Pi(e_1, e_2) = \alpha (e_1 + e_2 + \gamma e_1 e_2)</span><span><span><span></span><span>Π</span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>α</span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>γ</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span>，其中 <span><span>α&gt;0,γ≥0\alpha &gt; 0,\gamma \geq 0</span><span><span><span></span><span>α</span><span></span><span>&gt;</span><span></span></span><span><span></span><span>0</span><span>,</span><span></span><span>γ</span><span></span><span>≥</span><span></span></span><span><span></span><span>0</span></span></span></span>，体现了协同效应；</li>
<li><strong>努力成本函数</strong>：<span><span>C(ei)=12cei2,c&gt;0C(e_i) = \frac{1}{2} c e_i^2, \quad c &gt; 0</span><span><span><span></span><span>C</span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>c</span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span></span><span>c</span><span></span><span>&gt;</span><span></span></span><span><span></span><span>0</span></span></span></span>；</li>
<li><strong>收益分配</strong>：两个玩家<strong>平分</strong>产出，也就是每人拿到 <span><span>12Π(e1,e2)\frac{1}{2} \Pi(e_1, e_2)</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>Π</span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> ；</li>
</ul><blockquote><p>关于协同和边际效应，将在之后叙述。</p></blockquote><p>自然，可以计算出对于每个玩家的收益，或者说效用函数为：</p><span><span><span>Ui(e1,e2)=12Π(e1,e2)−C(ei)=12α(e1+e2+γe1e2)−12cei2U_i(e_1, e_2) = \frac{1}{2} \Pi(e_1, e_2) - C(e_i) = \frac{1}{2} \alpha (e_1 + e_2 + \gamma e_1 e_2) - \frac{1}{2} c e_i^2</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>Π</span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>−</span><span></span></span><span><span></span><span>C</span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>α</span><span>(</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>γ</span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>−</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>c</span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span><p>此时，另一个玩家的策略集合并不是一个离散的（努力或者完全不努力），而是一个连续的取值，自然难以画出图像进行求解。那么如果要计算玩家效用的最大化，就需要对 <span><span>e1e_1</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 求导了。</p><p>先求一阶导数：</p><span><span><span>∂Ui∂ei=12α(1+γej)−cei\frac{\partial U_i}{\partial e_i} = \frac{1}{2}\alpha \left(1 + \gamma e_j\right) - c e_i</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>∂</span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>∂</span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>α</span><span></span><span><span>(</span><span>1</span><span></span><span>+</span><span></span><span>γ</span><span><span>e</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span><span></span><span>−</span><span></span></span><span><span></span><span>c</span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span><p>求一阶导数是不够的，因为无法判断是极大值还是极小值，所以继续求二阶导数：</p><span><span><span>∂2Ui∂ei2=−c&lt;0\frac{\partial^2 U_i}{\partial e_i^2} = -c &lt; 0</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>∂</span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>∂</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>−</span><span>c</span><span></span><span>&lt;</span><span></span></span><span><span></span><span>0</span></span></span></span></span><p>恒小于 <span><span>00</span><span><span><span></span><span>0</span></span></span></span> 表示效用函数关于 <span><span>eie_i</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 是<strong>严格凹的</strong>，这意味着效用在 <span><span>eie_i</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 上存在唯一最大值。即在给定对方努力 <span><span>eje_j</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 的情况下，玩家 <span><span>ii</span><span><span><span></span><span>i</span></span></span></span> 的最优努力是：</p><span><span><span>ei∗=α(1+γej)2ce_i^* = \frac{\alpha(1 + \gamma e_j)}{2c}</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>c</span></span></span><span><span></span><span></span></span><span><span></span><span><span>α</span><span>(</span><span>1</span><span></span><span>+</span><span></span><span>γ</span><span><span>e</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>这是玩家 i 的<strong>最优反应函数</strong>。同样，对于另一名玩家，显然这是对称的。</p><p>按照课上的情形，取 <span><span>α=4,c=2,γ=14\alpha=4,c=2,\gamma=\frac{1}{4}</span><span><span><span></span><span>α</span><span></span><span>=</span><span></span></span><span><span></span><span>4</span><span>,</span><span></span><span>c</span><span></span><span>=</span><span></span></span><span><span></span><span>2</span><span>,</span><span></span><span>γ</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>4</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ，可以画出两者的最优反应函数如下图：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/202506201310576.png" alt="" /></p><p>无限套娃，最终逼近交点，即 <span><span>e1∗=e2∗=43e_1^* = e_2^* = \frac{4}{3}</span><span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>1</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>e</span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>4</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ，该交点意味着博弈双方均达到了平衡，即这是双方在<strong>没有外部协调机制</strong>、各自最大化自己效用时，<strong>最终均衡下的努力水平</strong>。</p><p>但这对企业整体的收益并非最优解，原因也很简单，和努力程度和协同效应越大都没啥关系，即使协同效应，即 <span><span>γ\gamma</span><span><span><span></span><span>γ</span></span></span></span> 进一步增加，合伙人还是只获得产出的 50%，边际成本却全自己承担，所以 Nash 均衡下的努力水平仍是<strong>次优的</strong>（即低于社会最优）；</p><p>在经济学上，这种就称之为<strong>外部性</strong>；</p><div><div><div></div><div>Important</div></div><div><p><strong>正外部性</strong>：一个人的行为对他人带来额外好处，但他<strong>自己无法完全内化这个收益</strong>；</p></div></div><p>简单来说，你的努力对别人有“<strong>正外部性</strong>”，但在博弈中，你只能拿到产出的一部分（比如一半），所以你<strong>没有激励为别人多努力</strong>；结果就是：<strong>所有人都努力得比社会最优要少</strong>，也就是激励不足。</p><p>也可以通过图像进一步验证，如果协同效应 <span><span>γ\gamma</span><span><span><span></span><span>γ</span></span></span></span> 减少，两条直线的交点会向原点方向持续下移，即双方投入的努力均变少。</p><p>这种博弈也被称为<strong>策略互补博弈（Strategic Complements Game）</strong>。</p><p>在这里给出 NE 的标准定义：</p><div><div><div></div><div>Note</div></div><div><p>Def a strategy profile <span><span>S∗=(S1,S2,…,Sn∗)∈S1×⋯×Sn\mathbf{S}^* = (S_1^, S_2^, \dots, S_n^*) \in S_1 \times \dots \times S_n</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>1</span></span></span><span><span></span><span><span>,</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span><span>,</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>…</span><span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>n</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>∈</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>×</span><span></span></span><span><span></span><span>⋯</span><span></span><span>×</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is a NE(Nash Equilibrium) if and only if <span><span>∀i∈N,∀Si′∈Si,Ui(S∗,S−i)≥Ui(Si′,S−i∗)\forall i \in N,\quad \forall S^{\prime}_i \in S_i,\quad U_i(\mathbf{S}^*, S_{-i}) \ge U_i(S^{\prime}_i, S_{-i}^*)</span><span><span><span></span><span>∀</span><span>i</span><span></span><span>∈</span><span></span></span><span><span></span><span>N</span><span>,</span><span></span><span></span><span>∀</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∈</span><span></span></span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>≥</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span><span>′</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>−</span><span>i</span></span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span>.</p></div></div><p>简单来说，就是“你做了最优选择，假设别人不变；别人也做了最优选择，假设你不变。没有人想自己单独改变。”纳什均衡是一个自我实现的信念，既博弈双方都认为事情会如此发展，最后事情也确实如此发展了，双方都不后悔。</p><blockquote><p>课上还举了几个非连续决策空间的例子，找到双方对策 BR 的重合点就是 NE ，比较基础，这里就偷懒跳过了。</p></blockquote><p>纳什均衡可以和第一章中的优劣势策略建立起一定的联系：</p><ul>
<li>所有严格优势策略均衡都是纳什均衡；</li>
<li>纳什均衡不一定由严格优势策略构成；</li>
<li>严格劣势策略不可能出现在纳什均衡中；（被迭代剔除了）</li>
<li>弱劣势策略可能出现在纳什均衡中；</li>
</ul></section><section><h2>2.3 Investment Game 投资游戏<a href="#23-investment-game-投资游戏"><span>#</span></a></h2><p>博弈对象包括全体同学，策略是投资 10 元，收益是净赚 5 元，但是要满足 90% 的人都选择了投资，或者不投资并无收益。很显然，这里的 NE 有 2 种情况，要么都投资，要么都不投资。</p><p>初始情况非常重要，如果第一次博弈中，更多的人站在了不投资的这一方，那么在下一轮投资中，更多的人也会不投资，最终在不断重复博弈过程中达到 (0,0) 的 NE ；但如果一开始就有超过 90% 的人选择投资，那么结果会达到 (1,1) 的 NE 。</p><p>现实中的案例包括：如腾讯的 QQ 和微信，在面世后就会面对协调博弈，安装的这些沟通软件的人必须要大于一个阈值，彼此才会有收益；股票交易所，如果上市的公司数量、交易量等大于阈值，则会形成良性循环；银行挤兑的问题，储户要么都不跑，要么一起跑，很多历史案例中银行本身并没有问题，但是由于预期变化，储户们选择了一起跑，那么就寄了。</p><p>注意它和囚徒困境的区别，囚徒困境中，沟通并不能有效解决问题，但是本情形属于<strong>协调博弈（Coordination Game）</strong>，通过沟通，可以引导参与者从一个 NE 向另一个 NE 移动，这种移动是符合自身利益的，并非舍弃优势策略，所以并不需要强制力的介入，领导力在其中发挥了重要作用。</p></section><section><h2>2.4 Battle Of Sex 性别大战<a href="#24-battle-of-sex-性别大战"><span>#</span></a></h2><p>考虑情侣约会决定看什么电影的情形：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729212103645.png" alt="" /></p><p>很显然，选择看白雪公主对双方都是严格劣势策略，所以先剔除。然后会发现存在两个 NE ，但与之前情形的区别是，这次双方对于选择哪种 NE 有利益上的冲突。</p></section><section><h2>2.5 Cournot Duopoly 古诺双寡头模型<a href="#25-cournot-duopoly-古诺双寡头模型"><span>#</span></a></h2><p><strong>古诺双寡头模型（Cournot Duopoly Model）</strong> 是博弈论和产业组织理论中的一个经典模型，用来描述两个厂商（寡头）<strong>在同时决定产量的条件下如何竞争</strong>，并分析均衡市场价格与产量。</p><ul>
<li>博弈者：生产同质化商品的两家厂商（Firm 1 和 Firm 2）；</li>
<li>策略：选择生产某种同质化产品的产量 <span><span>q1q_1</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 及 <span><span>q2q_2</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
<li>然后市场上形成一个统一价格：<span><span>P(Q)=a−bQP(Q) = a - bQ</span><span><span><span></span><span>P</span><span>(</span><span>Q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>a</span><span></span><span>−</span><span></span></span><span><span></span><span>b</span><span>Q</span></span></span></span>，其中 <span><span>Q=q1+q2,a,b&gt;0Q = q_1 + q_2, a, b &gt; 0</span><span><span><span></span><span>Q</span><span></span><span>=</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>a</span><span>,</span><span></span><span>b</span><span></span><span>&gt;</span><span></span></span><span><span></span><span>0</span></span></span></span> ；</li>
<li>厂商成本函数：<span><span>Ci(qi)=cqiC_i(q_i) = c q_i</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>（单位成本，或者说边际成本为常数 <span><span>cc</span><span><span><span></span><span>c</span></span></span></span>）；</li>
<li>每个厂商的利润函数：<span><span>πi(qi,qj)=[a−b(qi+qj)]qi−cqi\pi_i(q_i, q_j) = \left[ a - b(q_i + q_j) \right] q_i - c q_i</span><span><span><span></span><span><span>π</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span>[</span><span>a</span><span></span><span>−</span><span></span><span>b</span><span>(</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span>]</span></span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span>c</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ，其中 <span><span>j≠ij \ne i</span><span><span><span></span><span>j</span><span></span><span><span><span><span><span><span></span><span><span><span></span></span></span><span></span></span></span></span></span><span></span><span>=</span></span><span></span></span><span><span></span><span>i</span></span></span></span>，即厂商 <span><span>ii</span><span><span><span></span><span>i</span></span></span></span> 把对方产量当作已知，最大化自己的利润，与之前的商业合作类似；</li>
</ul><p>同样，对 <span><span>qiq_i</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 求一阶导数，设导数为 <span><span>00</span><span><span><span></span><span>0</span></span></span></span> ，得到<strong>最优反应函数（Best Response Function）</strong>：</p><span><span><span>dπidqi=a−b(qi+qj)−bqi−c=0⇒qi=a−c−bqj2b\frac{d\pi_i}{dq_i} = a - b(q_i + q_j) - bq_i - c = 0 \Rightarrow q_i = \frac{a - c - b q_j}{2b}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>d</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>d</span><span><span>π</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>a</span><span></span><span>−</span><span></span></span><span><span></span><span>b</span><span>(</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>−</span><span></span></span><span><span></span><span>b</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span>c</span><span></span><span>=</span><span></span></span><span><span></span><span>0</span><span></span><span>⇒</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>b</span></span></span><span><span></span><span></span></span><span><span></span><span><span>a</span><span></span><span>−</span><span></span><span>c</span><span></span><span>−</span><span></span><span>b</span><span><span>q</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>进一步求二阶导数：<span><span>d2πidqi2=−2b&lt;0\frac{d^2 \pi_i}{d q_i^2} = -2b &lt; 0</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>d</span><span><span>q</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span><span>d</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span><span><span>π</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>−</span><span>2</span><span>b</span><span></span><span>&lt;</span><span></span></span><span><span></span><span>0</span></span></span></span> ，所以该点为极大值点。</p><span><span><span>BR(q2)=q1^=a−c−bq22bBR(q_2) = \hat{q_1} = \frac{a - c - b q_2}{2b}</span><span><span><span></span><span>B</span><span>R</span><span>(</span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span></span><span><span>^</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>b</span></span></span><span><span></span><span></span></span><span><span></span><span><span>a</span><span></span><span>−</span><span></span><span>c</span><span></span><span>−</span><span></span><span>b</span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><span><span><span>BR(q1)=q2^=a−c−bq12bBR(q_1)=\hat{q_2} = \frac{a - c - b q_1}{2b}</span><span><span><span></span><span>B</span><span>R</span><span>(</span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span></span><span><span>^</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>b</span></span></span><span><span></span><span></span></span><span><span></span><span><span>a</span><span></span><span>−</span><span></span><span>c</span><span></span><span>−</span><span></span><span>b</span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729212045960.png" alt="" /></p><p>显然，NE 为两条曲线的交点，即 <span><span>q1∗=q2∗=a−c3bq_1^* = q_2^* = \frac{a - c}{3b}</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span><span>b</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>a</span><span>−</span><span>c</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 。不同于投资或者合作，付出的努力越多收益也越多，这里，企业继续增加产量对自己并没有好处，属于<strong>策略替代博弈（Strategic Substitute）</strong>；这里的替代并不是指生产产品上的替代，而是策略替代，即我的策略实施的越多，你的策略实施的就越少，反之亦然。</p><p>现在，我们考虑以下情形，如果两家企业协议，各自生产垄断产量的一半，即 <span><span>q1=q2=QM2=a−c4bq_1 = q_2 = \frac{Q^M}{2} = \frac{a - c}{4b}</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span><span>Q</span><span><span><span><span><span><span></span><span><span>M</span></span></span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>4</span><span>b</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>a</span><span>−</span><span>c</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ，如果两家均遵守协议，这种产量安排就等价于一个双头垄断，实现利润最大化。</p><p>但现实情况并非如此：</p><ul>
<li>如果另一方按协议不动，你偷偷多产一点，你能多赚；</li>
<li>另一方料到了你会偷偷多产，那么他就会根据你的产量调整到 BR ，即也进行多产；</li>
<li>如此博弈，那么最终又回到了 NE ，即古诺产量；</li>
</ul><p>就算两方能严格遵守协议，在寡头联合操控市场、价格抬高之后：高价格和高利润就会吸引新企业进入市场，使得产量扩大，原来的两人“垄断协议”会被打破。</p></section><section><h2>2.6 Bertrand competition 伯特兰模型<a href="#26-bertrand-competition-伯特兰模型"><span>#</span></a></h2><p>它与古诺模型是有部分相似之处的，首先，他们研究的都是不完全竞争市场（Imperfect Competition），即是<strong>市场上企业数量有限</strong>，或存在某种市场势力（market power），企业可以<strong>影响价格或产量</strong>，不再是完全竞争中那种“价格接受者”。模型的基本设定如下：</p><ul>
<li>博弈者：仍是两家生产同质产品的企业；</li>
<li>策略：选择某种同质化产品的价格，分别记为 <span><span>p1p_1</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 和 <span><span>p2p_2</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ，为方便分析，假定 <span><span>0≤pi≤10\le p_i\le 1</span><span><span><span></span><span>0</span><span></span><span>≤</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>≤</span><span></span></span><span><span></span><span>1</span></span></span></span> ；</li>
<li>生产成本：假定边际成本仍为常数 <span><span>cc</span><span><span><span></span><span>c</span></span></span></span> ；</li>
<li>市场总需求量：<span><span>Q=1−plowerQ = 1 - p_{lower}</span><span><span><span></span><span>Q</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span><span>l</span><span>o</span><span>w</span><span>er</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ，<span><span>plowerp_{lower}</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span><span>l</span><span>o</span><span>w</span><span>er</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 为 <span><span>p1,p2p_1,p_2</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 中的较小者；</li>
<li>两家的销量：<span><span>q1={1−q1if q1&lt;q20if q1&gt;q21−q12if q1=q2q_1 =\begin{cases}1 - q_1 &amp; \text{if } q_1 &lt; q_2 \\0 &amp; \text{if } q_1 &gt; q_2 \\\dfrac{1 - q_1}{2} &amp; \text{if } q_1 = q_2\end{cases}</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span><span><span></span><span><span>⎩</span></span></span><span><span></span><span></span></span><span><span></span><span><span>⎨</span></span></span><span><span></span><span></span></span><span><span></span><span><span>⎧</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span><span><span><span><span><span><span></span><span><span>1</span><span></span><span>−</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span><span>0</span></span></span><span><span></span><span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span><span></span><span>−</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span><span><span><span><span></span><span><span><span>if </span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&lt;</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span><span><span>if </span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&gt;</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span><span><span>if </span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span></span></span></span></span></span> ；（<span><span>q2q_2</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 同理）</li>
<li>企业的收益：<span><span>payoff1=⌈q1⌉(p1−c)payoff_1 = \left \lceil q_1 \right \rceil (p_1 - c)</span><span><span><span></span><span>p</span><span>a</span><span>y</span><span>o</span><span>f</span><span><span>f</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>⌈</span><span><span>q</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>⌉</span></span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span>c</span><span>)</span></span></span></span> ；（<span><span>payoff2payoff_2</span><span><span><span></span><span>p</span><span>a</span><span>y</span><span>o</span><span>f</span><span><span>f</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 同理）</li>
</ul><p>不难发现，与古诺模型的区别就是，古诺模型中，企业决定产量，而伯特兰模型中，企业决定价格。那么，伯特兰模型中，BR 是什么？</p><span><span><span>BR1(p2)={p1&gt;p2if p2&lt;cp1=p2−εif p2&gt;c1−p12,if p1=p2p1≥cif p2=cBR_1(p_2) = \begin{cases}
p_1 &gt; p_2 &amp; \text{if } p_2 &lt; c \\
p_1 = p_2 - \varepsilon &amp; \text{if } p_2 &gt; c \\
\dfrac{1 - p_1}{2}, &amp; \text{if } p_1 = p_2 \\
p_1 \ge c &amp; \text{if } p_2 = c
\end{cases}</span><span><span><span></span><span>B</span><span><span>R</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span><span><span></span><span><span>⎩</span></span></span><span><span></span><span></span></span><span><span></span><span><span>⎨</span></span></span><span><span></span><span></span></span><span><span></span><span><span>⎧</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span><span><span><span><span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&gt;</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span><span>ε</span></span></span><span><span></span><span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span><span></span><span>−</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>≥</span><span></span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span><span><span><span><span></span><span><span><span>if </span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&lt;</span><span></span><span>c</span></span></span><span><span></span><span><span><span>if </span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&gt;</span><span></span><span>c</span></span></span><span><span></span><span><span><span>if </span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span><span><span>if </span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span></span></span></span></span></span></span><p>简单解释一下：</p><ol>
<li>当 <span><span>p2p_2</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 定价小于成本时，我从事生产将会亏本，所以应该选择退出市场，<span><span>p1&gt;p2p_1 &gt; p_2</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 就相当于把市场份额全部让给另一家企业了；</li>
<li>当 <span><span>p2&gt;cp_2 &gt; c</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&gt;</span><span></span></span><span><span></span><span>c</span></span></span></span> 时，我需要做的就是比另一家企业定价稍微低一点点，这样我就能最大化利润，并且占有全部市场份额；</li>
<li>当 <span><span>p1=p2p_1 = p_2</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 时，那么我们五五开；</li>
<li>当 <span><span>p2=cp_2 = c</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span></span></span></span> 时，我需要做的就是大于等于成本，即一起生产但不都赚，或者直接退出市场；</li>
</ol><p>容易发现 <span><span>NE=(p1=c,p2=c)NE = (p_1=c,p_2=c)</span><span><span><span></span><span>N</span><span>E</span><span></span><span>=</span><span></span></span><span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>)</span></span></span></span> 是一个纳什均衡点，还有别的吗？比如<span><span>(p1=c,p2=c+3ε)(p_1 = c,p_2 = c + 3\varepsilon)</span><span><span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span></span><span>+</span><span></span></span><span><span></span><span>3</span><span>ε</span><span>)</span></span></span></span> ？</p><p>当 <span><span>p1=c+3εp_1 = c + 3\varepsilon</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span></span><span>+</span><span></span></span><span><span></span><span>3</span><span>ε</span></span></span></span> 时，企业 1 的 BR 应该是 <span><span>c+3ε−ε=c+2εc + 3\varepsilon - \varepsilon = c + 2\varepsilon</span><span><span><span></span><span>c</span><span></span><span>+</span><span></span></span><span><span></span><span>3</span><span>ε</span><span></span><span>−</span><span></span></span><span><span></span><span>ε</span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span></span><span>+</span><span></span></span><span><span></span><span>2</span><span>ε</span></span></span></span> ；然后站在另一家企业的角度考虑，当 <span><span>p1=c+2εp_1 = c + 2\varepsilon</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span></span><span>+</span><span></span></span><span><span></span><span>2</span><span>ε</span></span></span></span> 时，BR 应该是 <span><span>c+εc + \varepsilon</span><span><span><span></span><span>c</span><span></span><span>+</span><span></span></span><span><span></span><span>ε</span></span></span></span> ，你会发现也是不断向 <span><span>NE=(p1=c,p2=c)NE = (p_1=c,p_2=c)</span><span><span><span></span><span>N</span><span>E</span><span></span><span>=</span><span></span></span><span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>c</span><span>)</span></span></span></span> 移动。</p><p>结果就产生了一个悖论：两个厂商居然会像“完全竞争市场”一样，把价格压到边际成本。这与我们观察到的现实不符（现实中寡头通常有市场势力）！<strong>伯特兰悖论（Bertrand Paradox）</strong> 说明，该模型本身预测的太极端了，或者说，需要对它的假设进行修正。</p><section><h3>2.6* 应对伯特兰悖论的扩展模型 —— 线性城市模型 Linear City Model<a href="#26-应对伯特兰悖论的扩展模型--线性城市模型-linear-city-model"><span>#</span></a></h3><p>该模型假设：</p><ul>
<li>一条<strong>长度为 1 的线段</strong>，代表“线性城市”；</li>
<li>消费者<strong>均匀分布</strong>在这条线上；</li>
<li>两家企业分别设在线上的不同位置（通常是 0 和 1）；</li>
<li>消费者购买时，不仅考虑价格，还考虑<strong>与厂商的距离成本</strong>。</li>
</ul><p>虽然两个企业仍生产同质化产品，但由于地理位置，变相引入了差异化。这里并不一定需要一定是地理位置，可能是消费者对同一品类的不同品牌的偏好性（比如可口可乐 and 百事可乐）。</p><ul>
<li>Firm 1 在 <span><span>x=0x = 0</span><span><span><span></span><span>x</span><span></span><span>=</span><span></span></span><span><span></span><span>0</span></span></span></span>，Firm 2 在 <span><span>x=1x = 1</span><span><span><span></span><span>x</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span> ；</li>
<li>消费者在 <span><span>x∈[0,1]x \in [0,1]</span><span><span><span></span><span>x</span><span></span><span>∈</span><span></span></span><span><span></span><span>[</span><span>0</span><span>,</span><span></span><span>1</span><span>]</span></span></span></span> ；</li>
<li>厂商价格为 <span><span>p1p_1</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>、<span><span>p2p_2</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
<li>消费者有交通成本（或者就是产品“差异化”不适配的心理成本）：<span><span>t⋅dt\cdot d</span><span><span><span></span><span>t</span><span></span><span>⋅</span><span></span></span><span><span></span><span>d</span></span></span></span> ；（注意这里与课程的差异，课程使用了 <span><span>d2d^2</span><span><span><span></span><span><span>d</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span></span></span></span></span></span></span></span> ，而标准的线性城市模型似乎使用的是 <span><span>dd</span><span><span><span></span><span>d</span></span></span></span> ，我在这里先使用后者进行求解）；</li>
</ul><p>一个消费者在位置 <span><span>x∈[0,1]x \in [0,1]</span><span><span><span></span><span>x</span><span></span><span>∈</span><span></span></span><span><span></span><span>[</span><span>0</span><span>,</span><span></span><span>1</span><span>]</span></span></span></span>，面临两种选择：</p><ul>
<li>从 Firm 1 购买，支付 <span><span>p1+t⋅xp_1 + t \cdot x</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>t</span><span></span><span>⋅</span><span></span></span><span><span></span><span>x</span></span></span></span> ；</li>
<li>从 Firm 2 购买，支付 <span><span>p2+t⋅(1−x)p_2 + t \cdot (1 - x)</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>t</span><span></span><span>⋅</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>x</span><span>)</span></span></span></span> ；</li>
</ul><p>显然，消费者会选择成本更小的，我们首先找出这条直线上的分界点：</p><span><span><span>p1+tx=p2+t(1−x)⇒tx+tx=p2−p1+t⇒x∗=p2−p1+t2t\begin{matrix}
&amp; p_1 + t x = p_2 + t (1 - x) \\
\Rightarrow &amp; t x + t x = p_2 - p_1 + t \\ 
\Rightarrow &amp; x^* = \frac{p_2 - p_1 + t}{2t}
\end{matrix}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span></span></span><span><span></span><span><span>⇒</span></span></span><span><span></span><span><span>⇒</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span>x</span><span></span><span>=</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>x</span><span>)</span></span></span><span><span></span><span><span>t</span><span>x</span><span></span><span>+</span><span></span><span>t</span><span>x</span><span></span><span>=</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span></span></span><span><span></span><span><span><span>x</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span><span>=</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span><span>t</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>−</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>+</span><span>t</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><p><span><span>x∗x^*</span><span><span><span></span><span><span>x</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span></span></span></span> 即为无差异点：</p><ul>
<li>位于左侧（<span><span>x&lt;x∗x &lt; x^*</span><span><span><span></span><span>x</span><span></span><span>&lt;</span><span></span></span><span><span></span><span><span>x</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span></span></span></span>）的消费者选择 Firm 1；</li>
<li>位于右侧（<span><span>x&gt;x∗x &gt; x^*</span><span><span><span></span><span>x</span><span></span><span>&gt;</span><span></span></span><span><span></span><span><span>x</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span></span></span></span>）的消费者选择 Firm 2。</li>
</ul><p>由于消费者在直线上均匀分布，自然可以计算出 Firm 1 和 Firm 2 的市场份额以及对应利润函数：</p><span><span><span>π1=(p1−c)⋅p2−p1+t2t\pi_1 = (p_1 - c) \cdot \frac{p_2 - p_1 + t}{2t}</span><span><span><span></span><span><span>π</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span>c</span><span>)</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>t</span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><span><span><span>π2=(p2−c)⋅p1−p2+t2t\pi_2 = (p_2 - c) \cdot \frac{p_1 - p_2 + t}{2t}</span><span><span><span></span><span><span>π</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span>c</span><span>)</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>t</span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>然后是经典微积分环节：</p><span><span><span>dπ1dp1=(p2−2p1+t+c)2t=0⇒p1∗=p2+t+c2\frac{d\pi_1}{dp_1} = \frac{(p_2 - 2p_1 + t + c)}{2t} = 0 \Rightarrow p_1^* = \frac{p_2 + t + c}{2}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>d</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>d</span><span><span>π</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>t</span></span></span><span><span></span><span></span></span><span><span></span><span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span><span>2</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span></span><span>+</span><span></span><span>c</span><span>)</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>0</span><span></span><span>⇒</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span></span><span>+</span><span></span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><span><span><span>dπ2dp2=(p1−2p2+t+c)2t=0⇒p2∗=p1+t+c2\frac{d\pi_2}{dp_2} = \frac{(p_1 - 2p_2 + t + c)}{2t} = 0 \Rightarrow p_2^* = \frac{p_1 + t + c}{2}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>d</span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span><span><span></span><span></span></span><span><span></span><span><span>d</span><span><span>π</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span><span>t</span></span></span><span><span></span><span></span></span><span><span></span><span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span><span>2</span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span></span><span>+</span><span></span><span>c</span><span>)</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>0</span><span></span><span>⇒</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span></span><span>+</span><span></span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>则有：</p><span><span><span>BR1(p2)=p1^=p2+t+c2BR_1(p_2) = \hat{p_1} = \frac{p_2 + t + c}{2}</span><span><span><span></span><span>B</span><span><span>R</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span></span><span><span>^</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span></span><span>+</span><span></span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><span><span><span>BR2(p1)=p2^=p1+t+c2BR_2(p_1) = \hat{p_2} = \frac{p_1 + t + c}{2}</span><span><span><span></span><span>B</span><span><span>R</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span></span><span><span>^</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span></span><span>+</span><span></span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729212029396.png" alt="" /></p><p>不难求得纳什均衡 <span><span>NE=(p1=t+c,p2=t+c)NE = (p_1 = t+c,p_2 = t+c)</span><span><span><span></span><span>N</span><span>E</span><span></span><span>=</span><span></span></span><span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>t</span><span></span><span>+</span><span></span></span><span><span></span><span>c</span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>t</span><span></span><span>+</span><span></span></span><span><span></span><span>c</span><span>)</span></span></span></span> ，双方定价相等，且各占有一半市场，并且定价高于成本，均获利，解决了伯特兰悖论。</p><p>如果像课上一样改成<strong>凸运输成本</strong>？</p><p>同样，先求解无差异点：</p><span><span><span>p1+tx2=p2+t(1−2x+x2)⇒2tx=p2−p1+t⇒x∗=p2−p1+t2t\begin{matrix}
&amp; p_1 + t x^{2} = p_2 + t (1 - 2x + x^{2}) \\ 
\Rightarrow &amp; 2t x = p_2 - p_1 + t \\ 
\Rightarrow &amp; x^* = \frac{p_2 - p_1 + t}{2t}
\end{matrix}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span></span></span><span><span></span><span><span>⇒</span></span></span><span><span></span><span><span>⇒</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span><span>x</span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span></span></span></span></span></span><span></span><span>=</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>2</span><span>x</span><span></span><span>+</span><span></span><span><span>x</span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span></span></span></span></span></span><span>)</span></span></span><span><span></span><span><span>2</span><span>t</span><span>x</span><span></span><span>=</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span><span>t</span></span></span><span><span></span><span><span><span>x</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span><span>=</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span><span>t</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>−</span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>+</span><span>t</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><p>可以发现，和线性成本下完全一致，那么之后的运算也是一样的。</p></section></section><section><h2>2.7 Candidate-Voter Model 选民投票<a href="#27-candidate-voter-model-选民投票"><span>#</span></a></h2><p>该模型与 1.6 的中位选民模型基本类似，选民均匀分布，但有以下区别：</p><ol>
<li>候选人数量不固定；</li>
<li>候选人不能自行选择立场，简单来说，选民是知道这个候选人是什么成分的；</li>
</ol><ul>
<li>博弈者：选民/候选人（选民也可以参选，参选了就变成候选人了）；</li>
<li>策略：参选或不参选；</li>
<li>收益：设胜选收益为 <span><span>BB</span><span><span><span></span><span>B</span></span></span></span> ，参选成本为 <span><span>CC</span><span><span><span></span><span>C</span></span></span></span> ，并且对于选民 <span><span>XX</span><span><span><span></span><span>X</span></span></span></span> ，如果选民 <span><span>YY</span><span><span><span></span><span>Y</span></span></span></span> 成功，则还需要额外付出 <span><span>∣X−Y∣|X-Y|</span><span><span><span></span><span>∣</span><span>X</span><span></span><span>−</span><span></span></span><span><span></span><span>Y</span><span>∣</span></span></span></span> 的成本（可以理解为对政治立场与自己不同的人获胜的不爽）；</li>
</ul><p>可能你会觉得与中位选民类似，但事实上情况要复杂很多：</p><ul>
<li>如果候选人人数 = 0 ，并不存在 NE ，因为此时任何一个人参选，都会获得更高的收益；</li>
<li>如果候选人人数 = 1 ， 如果总人数为奇数，那么最中间的人参选达到 NE ；</li>
<li>如果候选人人数 = 2 ，如果总人数为偶数，，并且候选人为中间两位时达到 NE ；（考虑如果 1 ～ 10 个选民，选民 4 如果不参选，选民 5 和 6 平局，收益为 <span><span>−5+62−4=−1.5-\frac{5 + 6}{2}-4=-1.5</span><span><span><span></span><span>−</span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>5</span><span>+</span><span>6</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>−</span><span></span></span><span><span></span><span>4</span><span></span><span>=</span><span></span></span><span><span></span><span>−</span><span>1.5</span></span></span></span> ，如果参选，那么将会导致选民 6 获胜，收益为 <span><span>−c−(6−4)=−c−2&lt;1.5-c - (6 - 4) = - c - 2 &lt; 1.5</span><span><span><span></span><span>−</span><span>c</span><span></span><span>−</span><span></span></span><span><span></span><span>(</span><span>6</span><span></span><span>−</span><span></span></span><span><span></span><span>4</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>−</span><span>c</span><span></span><span>−</span><span></span></span><span><span></span><span>2</span><span></span><span>&lt;</span><span></span></span><span><span></span><span>1.5</span></span></span></span> ，所以选民 4 不会参选，该情形属于纳什均衡）</li>
</ul><p>该情形比较复杂，这一就不一一罗列。我们考虑参选人的立场最远能极端到什么程度，现在假设有选民 1 ～ 9 ，假如选民 2 、5 、8 参选，那么它们得票的概率是一致的。可以说，在均匀的直线上，<span><span>16,12,56\frac{1}{6},\frac{1}{2},\frac{5}{6}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>6</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>6</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>5</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span>这三个位置正好是三方打平的临界状态。当两个候选人从临界位置向中间靠拢的时候，即便是正中间的选民也不可能通过参选获胜了，反之，当两个候选人从临界位置向两端偏离的时候，可以通过参选获胜的中间区域扩大。</p><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 1</strong>：该选民投票模型有很多 NE ，并且并非如中位选民模型一般位于中间；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 2</strong>：任意选民参选可能导致一个立场偏离自己更远的候选人获胜；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 3</strong>：如果在两人参选的均衡中两人位置偏离中间太远，即太极端，那么中间位置的选民可以参选并获胜；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 4</strong>：先猜后证。</p></div></div></section><section><h2>2.8 Location Model 选址模型<a href="#28-location-model-选址模型"><span>#</span></a></h2><ul>
<li>博弈者：10w 个高个子 and 10w 个矮个子；</li>
<li>策略：选择东城区或西城区居住，每个城区可以住 10w 人；</li>
<li>收益：<span><span>y={x50000,if 0&lt;x≤5000032−x100000,if 50000&lt;x&lt;100000y =\begin{cases}\dfrac{x}{50000}, &amp; \text{if } 0 &lt; x \le 50000 \\\dfrac{3}{2} - \dfrac{x}{100000}, &amp; \text{if } 50000 &lt; x &lt; 100000\end{cases}</span><span><span><span></span><span>y</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span><span><span></span><span><span>⎩</span></span></span><span><span></span><span></span></span><span><span></span><span><span>⎨</span></span></span><span><span></span><span></span></span><span><span></span><span><span>⎧</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span><span><span><span><span><span><span></span><span><span><span></span><span><span><span><span><span><span></span><span><span>50000</span></span></span><span><span></span><span></span></span><span><span></span><span><span>x</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span></span></span><span><span></span><span><span><span></span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span></span></span><span><span></span><span><span>3</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>−</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>100000</span></span></span><span><span></span><span></span></span><span><span></span><span><span>x</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span><span><span><span><span></span><span><span><span>if </span></span><span>0</span><span></span><span>&lt;</span><span></span><span>x</span><span></span><span>≤</span><span></span><span>50000</span></span></span><span><span></span><span><span><span>if </span></span><span>50000</span><span></span><span>&lt;</span><span></span><span>x</span><span></span><span>&lt;</span><span></span><span>100000</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span></span></span></span></span></span> ，其中 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span> 表示与自己同类的人的数量；</li>
</ul><p>画图可以看出这个收益函数是倒 U 型的，在同类人数量 <span><span>x=50000x = 50000</span><span><span><span></span><span>x</span><span></span><span>=</span><span></span></span><span><span></span><span>50000</span></span></span></span> 时收益达到峰值（<span><span>y=1y = 1</span><span><span><span></span><span>y</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span>）；如果同类太少，则不合群，收益低；反之，如果同类太多，则拥挤、缺多样性，收益也低；</p><p>采用先猜后证的方法，不难看出 2 种纳什均衡，<strong>10 万个同类人全住在一个城区（比如东区），异类人住在另一城区（西区）</strong>，这样，大家的收益都是 <span><span>12\frac{1}{2}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>2</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 。证明也并不难，考虑：东城区住着 10 万个高个子，西城区住着 10 万个矮个子；（反过来一样）</p><p>对于单个高个子，是否想搬到西城区？搬过去之后，收益变为 <span><span>00</span><span><span><span></span><span>0</span></span></span></span> ！不搬；反过来，单个矮子也不搬，所以没人想改变选择，确实是 NE ！这就是一种<strong>种族隔离</strong>的均衡。</p><p>还有一种纳什均衡，就是<strong>每个城区都高个子和矮个子都五五开混居</strong>。因为任意个人搬家，都会导致他自己的收益略微降低。</p><p>但是，可不要小看个体的影响，一旦如果一个个体搬家了，原来城区中所有同类的收益均会受损，并且此时搬家成了他们的 BR 。事实上，<strong>混居的均衡是不稳定的，或者说是弱均衡</strong>。而<strong>种族隔离的均衡是严格均衡且稳定</strong>。</p><p>在本次博弈中，50% 就是<strong>临界点 Tipping Point</strong>，一旦越过了 50% 的临界点，那么原有的均衡将“失衡”，向着另一个均衡的方向快速发展，尽管人们更偏好原有的均衡。</p><p>不过，如果博弈中，所有人都选择了一个地方呢？那么根据规则，将由政府进行随机分配（中央调控）。</p><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 1</strong>：建模时的琐碎细节可以导致巨大的区别；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 2</strong>：社会随机分配的结果好于自主选择（根据大数定律）；</p><p>简单解释一下，如果让个体自主选择，会因策略性行为、聚集心理、博弈效应而偏离整体最优；而随机分配则避免这些效应，在<strong>大样本下平均结果更好</strong>（靠近期望）。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 3</strong>：在一个区域看到了种族隔离现象，不能等价于人们偏好隔离状态。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 4</strong>：随机分配政策的合理性；</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 5</strong>：可以由个人的随机化选择替代系统随机（中央调控）；</p></div></div><p>最后一点就引入了接下来一节的混合策略。注意，我在前两章讲的都是纯策略，即博弈者确定性地选择一个动作，而对于混合策略，玩家选择的是一个<strong>概率分布</strong>，即以一定概率在多个动作中<strong>随机选择</strong>一个行动。</p></section></section>
<section><h1>三、Mixed Strategies 混合策略<a href="#三mixed-strategies-混合策略"><span>#</span></a></h1><section><h2>3.1 Rock, Paper, Scissors 剪刀石头布<a href="#31-rock-paper-scissors-剪刀石头布"><span>#</span></a></h2><p>显然，剪刀石头布游戏中，我单纯的采取某种策略，比如每次博弈都出剪刀，那么对方的 BR 就是出石头，那么你的 BR 就是出布，如此循环，并非 NE ，这时候就需要混合策略了。</p><p>设 Player 1 使用混合策略 <span><span>(pR,pS,pP)(p_R, p_S, p_P)</span><span><span><span></span><span>(</span><span><span>p</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span>，分别是出石头、剪刀、布的概率。Player 2 使用混合策略 <span><span>(qR,qS,qP)(q_R, q_S, q_P)</span><span><span><span></span><span>(</span><span><span>q</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> 。则有：</p><ul>
<li>Player 1 出石头时的预期收益为：<span><span>UR=0⋅qR+1⋅qS+(−1)⋅qP=qS−qPU_R = 0 \cdot q_R + 1 \cdot q_S + (-1) \cdot q_P = q_S - q_P</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>0</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>(</span><span>−</span><span>1</span><span>)</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
<li>Player 1 出剪刀时的预期收益为：<span><span>US=(−1)⋅qR+0⋅qS+1⋅qP=−qR+qPU_S = (-1) \cdot q_R + 0 \cdot q_S + 1 \cdot q_P = -q_R + q_P</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>(</span><span>−</span><span>1</span><span>)</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>0</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>−</span><span><span>q</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
<li>Player 1 出布时的预期收益为：<span><span>UP=1⋅qR+(−1)⋅qS+0⋅qP=qR−qSU_P = 1 \cdot q_R + (-1) \cdot q_S + 0 \cdot q_P = q_R - q_S</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span>1</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>(</span><span>−</span><span>1</span><span>)</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>0</span><span></span><span>⋅</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>−</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ；</li>
</ul><p>显然，如果出某个动作的预期收益比另外两个高，那就会只出期望高的那个动作，退化为纯策略，所以，需要使得 <span><span>UR=US=UPU_R = U_S = U_P</span><span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>U</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ，可解得 <span><span>qR=qS=qP=13q_R = q_S = q_P = \frac{1}{3}</span><span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>q</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 。同理也解得 <span><span>pR=pS=pP=13p_R = p_S = p_P = \frac{1}{3}</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>R</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>S</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>P</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 。</p><p>此时双方均达到了 BR ，所以 <span><span>S(13,13,13),(13,13,13)S_{(\frac{1}{3},\frac{1}{3},\frac{1}{3}),(\frac{1}{3},\frac{1}{3},\frac{1}{3})}</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span><span>(</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>)</span><span>,</span><span>(</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>)</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> 是 NE 。</p><div><div><div></div><div>Important</div></div><div><p>Def a mixed strategy <span><span>PiP_i</span><span><span><span></span><span><span>P</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is a randomization over i’s pure strategies.</p><p><span><span>Pi(Si)P_i(S_i)</span><span><span><span></span><span><span>P</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span></span></span></span> is the probability that <span><span>PiP_i</span><span><span><span></span><span><span>P</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> assigns to pure strategy <span><span>SiS_i</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>.</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 1</strong>：如果一个混合策略是 BR ，那么这个混合策略中的每一个纯策略必然也都是 BR ，并且每一个策略的预期收益相同。</p></div></div><p>这点由加权平均数的角度理解就行，混合策略的收益是期望，所以结果必定界于纯策略中最大的收益和最小的收益之间，如果该混合策略是最优反应，那么其中的所有纯策略必然收益最大且相同。</p><div><div><div></div><div>Important</div></div><div><p>Def a mixed strategy profile <span><span>p1∗,p2∗,⋯ ,pn∗p^*_1,p^*_2,\cdots,p^*_n</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>1</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>2</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>⋯</span><span></span><span></span><span>,</span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>n</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is a mixed NE if for each Player <span><span>ii</span><span><span><span></span><span>i</span></span></span></span>, <span><span>pi∗p^*_i</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> is a BR to <span><span>p−1∗p^*_{-1}</span><span><span><span></span><span><span>p</span><span><span><span><span><span><span></span><span><span><span>−</span><span>1</span></span></span></span><span><span></span><span><span>∗</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>.</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 2</strong>：如果混合最优策略中某一纯策略的采用概率大于 0 ，那么这个纯策略必然是最优反应。</p></div></div></section><section><h2>3.2 Tennis Match 网球比赛<a href="#32-tennis-match-网球比赛"><span>#</span></a></h2><p>假设有 2 个选手正在进行网球比赛，选手 1 要选择打左边还是打右边，选手 2 需要预判向左还是向右防守。博弈矩阵如下，数字表示得分概率：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211659133.png" alt="" /></p><p>显然，不存在纯策略的 NE 。那么混合策略呢？</p><p>不妨设选手 1 在达到 NE 时的混合策略为 <span><span>(p,1−p)(p,1-p)</span><span><span><span></span><span>(</span><span>p</span><span>,</span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span></span></span></span> ，选手 2 在达到 NE 时的混合策略为 <span><span>(q,1−q)(q,1-q)</span><span><span><span></span><span>(</span><span>q</span><span>,</span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span></span></span></span> 。</p><span><span><span>EU1(L,q&amp;1−q)=50q+80(1−q)=80−30qEU_1(L,q \&amp; 1-q) = 50q + 80(1-q) = 80-30q</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>L</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>50</span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>80</span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>80</span><span></span><span>−</span><span></span></span><span><span></span><span>30</span><span>q</span></span></span></span></span><span><span><span>EU1(R,q&amp;1−q)=90q+20(1−q)=20+70qEU_1(R,q \&amp; 1-q) = 90q + 20(1-q) = 20+70q</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>R</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>90</span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>20</span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>20</span><span></span><span>+</span><span></span></span><span><span></span><span>70</span><span>q</span></span></span></span></span><p>那么，<strong>选手 2 的混合策略如果希望达到 NE ，应该使得选手 1 选左或选右都没有区别，即每一个纯策略预期收益都相同</strong>，则 <span><span>EU1(L,q&amp;1−q)=EU1(R,q&amp;1−q)EU_1(L,q \&amp; 1-q)=EU_1(R,q \&amp; 1-q)</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>L</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>R</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span></span></span></span> ，由此可解得 <span><span>q=0.6q = 0.6</span><span><span><span></span><span>q</span><span></span><span>=</span><span></span></span><span><span></span><span>0.6</span></span></span></span> 。</p><p>那么对于选手 1 的混合策略是同理的：</p><span><span><span>EU2(p&amp;1−p,L)=50p+10(1−p)=EU2(p&amp;1−p,R)=20p+80(1−p)⇒p=0.7EU_2(p \&amp; 1-p,L) = 50p+10(1-p)=EU_2(p \&amp; 1-p,R) = 20p+80(1-p)\Rightarrow p = 0.7</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>p</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>,</span><span></span><span>L</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>50</span><span>p</span><span></span><span>+</span><span></span></span><span><span></span><span>10</span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>p</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>,</span><span></span><span>R</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>20</span><span>p</span><span></span><span>+</span><span></span></span><span><span></span><span>80</span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span><span></span><span>⇒</span><span></span></span><span><span></span><span>p</span><span></span><span>=</span><span></span></span><span><span></span><span>0.7</span></span></span></span></span><p>所以，双方分别采用混合策略 <span><span>[(0.7,0.3),(0.6,0.4)][(0.7,0.3),(0.6,0.4)]</span><span><span><span></span><span>[(</span><span>0.7</span><span>,</span><span></span><span>0.3</span><span>)</span><span>,</span><span></span><span>(</span><span>0.6</span><span>,</span><span></span><span>0.4</span><span>)]</span></span></span></span> 是 NE 。简单解释一下，如果选手 1 选择左边的概率大于 0.7 ，那么选手 2 就应该选择始终防守左边（退化为纯策略）；如果选手 2 选择左边的概率大于 0.6 ，那么选手 1 就应该始终进攻右边。</p><p>现在，假如选手 2 的反手击球（往左）能力提升了呢？比如，博弈矩阵的 <span><span>(L,L)(L,L)</span><span><span><span></span><span>(</span><span>L</span><span>,</span><span></span><span>L</span><span>)</span></span></span></span> 处变为 <span><span>(30,70)(30,70)</span><span><span><span></span><span>(</span><span>30</span><span>,</span><span></span><span>70</span><span>)</span></span></span></span> ，并且该信息对双方都是透明的，即对手也知道选手 2 的能力进步了。</p><p>我们先从直觉的角度看这个问题，如果选手 2 的反手能力提升：</p><ul>
<li><strong>直接影响</strong>：选手 2 更倾向于打反手球，<span><span>qq</span><span><span><span></span><span>q</span></span></span></span> 增大；</li>
<li><strong>间接/战略影响</strong>：由于信息透明，选手 1 会减少打反手球，那么选手 2 也会相应减少打反手球，<span><span>qq</span><span><span><span></span><span>q</span></span></span></span> 减小；</li>
</ul><p>该问题的关键是，谁起了主导地位？即最后的 <span><span>qq</span><span><span><span></span><span>q</span></span></span></span> 是增大还是减小？</p><p>求解 NE 如下：</p><span><span><span>EU1(L,q&amp;1−q)=30q+80(1−q)=EU1(R,q&amp;1−q)=90q+20(1−q)⇒q=0.5EU_1(L,q \&amp; 1-q) = 30q + 80(1-q)=EU_1(R,q \&amp; 1-q) = 90q + 20(1-q)\Rightarrow q = 0.5</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>L</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>30</span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>80</span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>R</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>90</span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>20</span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>⇒</span><span></span></span><span><span></span><span>q</span><span></span><span>=</span><span></span></span><span><span></span><span>0.5</span></span></span></span></span><p>即，<span><span>qq</span><span><span><span></span><span>q</span></span></span></span> 减小，<strong>战略/间接影响发挥了主导作用</strong>。同样，对于选手 1 呢？既然选手 2 减小了反手球的概率，那么从直觉来看，选手 1 进攻左边的概率也会减小，即 <span><span>pp</span><span><span><span></span><span>p</span></span></span></span> 减小，你也可以通过计算验证，此处省略。</p><p>以下是对于上述得出的混合策略是最优策略的证明：</p><p>对于选手 1 ，其选择左边的收益为 <span><span>50×0.6+80×0.4=6250\times 0.6 + 80\times 0.4=62</span><span><span><span></span><span>50</span><span></span><span>×</span><span></span></span><span><span></span><span>0.6</span><span></span><span>+</span><span></span></span><span><span></span><span>80</span><span></span><span>×</span><span></span></span><span><span></span><span>0.4</span><span></span><span>=</span><span></span></span><span><span></span><span>62</span></span></span></span> ，选择右边的收益为 <span><span>90×0.6+20×0.4=6290\times 0.6 + 20\times 0.4 = 62</span><span><span><span></span><span>90</span><span></span><span>×</span><span></span></span><span><span></span><span>0.6</span><span></span><span>+</span><span></span></span><span><span></span><span>20</span><span></span><span>×</span><span></span></span><span><span></span><span>0.4</span><span></span><span>=</span><span></span></span><span><span></span><span>62</span></span></span></span> ，采用混合策略的收益为 <span><span>0.7×62+0.3×62=620.7\times 62 + 0.3\times 62 = 62</span><span><span><span></span><span>0.7</span><span></span><span>×</span><span></span></span><span><span></span><span>62</span><span></span><span>+</span><span></span></span><span><span></span><span>0.3</span><span></span><span>×</span><span></span></span><span><span></span><span>62</span><span></span><span>=</span><span></span></span><span><span></span><span>62</span></span></span></span> 。显然，采用纯策略也并非严格有利，对于其他的混合策略，其收益也肯定界于较高的纯策略收益和较低的纯策略收益之间。</p><p>由此也可以得出一个新结论：</p><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 3</strong>：混合策略是否是最优策略，只需证明改选纯策略是否严格有利即可。</p></div></div></section><section><h2>3.3 Battle Of Sex Ⅱ 性别大战 Ⅱ<a href="#33-battle-of-sex-ⅱ-性别大战-ⅱ"><span>#</span></a></h2><p>与之前提到的类似，博弈矩阵如下：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211644203.png" alt="" /></p><p>显然，（选择1，选择1）和（选择2，选择2）都是 NE 。只要通过沟通便能达到均衡。但如果现实中，并没有事先沟通，协调失败的情况也是常有的。那么就需要考虑混合策略。计算混合策略下的 NE 的方法已经在上面一节给出了，这里直接计算。</p><p>设男方采用混合策略 <span><span>(p,1−p)(p,1-p)</span><span><span><span></span><span>(</span><span>p</span><span>,</span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span></span></span></span> ，女方采用混合策略 <span><span>(q,1−q)(q,1-q)</span><span><span><span></span><span>(</span><span>q</span><span>,</span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span></span></span></span> 。</p><span><span><span>EUM(1,q&amp;1−q)=2×q+(1−q)×0=EUM(2,q&amp;1−q)=0×q+1×(1−q)⇒q=13EU_{M}(1,q\&amp;1-q)=2\times q + (1-q)\times 0=EU_M(2,q\&amp;1-q)=0\times q + 1\times (1-q)\Rightarrow q = \frac{1}{3}</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span><span>M</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>1</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>2</span><span></span><span>×</span><span></span></span><span><span></span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>×</span><span></span></span><span><span></span><span>0</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>M</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>2</span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>0</span><span></span><span>×</span><span></span></span><span><span></span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span></span><span>×</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>⇒</span><span></span></span><span><span></span><span>q</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>3</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><span><span><span>EUW(p&amp;1−p,1)=1×p+(1−p)×0=EUW(p&amp;1−p,2)=0×p+2×(1−p)⇒p=23EU_{W}(p\&amp; 1-p,1)=1\times p + (1-p)\times 0=EU_W(p\&amp; 1-p,2)=0\times p + 2\times (1-p)\Rightarrow p = \frac{2}{3}</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span><span>W</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>p</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>,</span><span></span><span>1</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span><span></span><span>×</span><span></span></span><span><span></span><span>p</span><span></span><span>+</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span><span></span><span>×</span><span></span></span><span><span></span><span>0</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>W</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>p</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>,</span><span></span><span>2</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>0</span><span></span><span>×</span><span></span></span><span><span></span><span>p</span><span></span><span>+</span><span></span></span><span><span></span><span>2</span><span></span><span>×</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span><span></span><span>⇒</span><span></span></span><span><span></span><span>p</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>3</span></span></span><span><span></span><span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>来简单验证一下 <span><span>[(23,13),(13,23)][(\frac{2}{3},\frac{1}{3}),(\frac{1}{3},\frac{2}{3})]</span><span><span><span></span><span>[(</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>)</span><span>,</span><span></span><span>(</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>)]</span></span></span></span> 是 NE ，对于男方，选择 1 的收益为 <span><span>2×13+0×23=232\times\frac{1}{3}+0\times\frac{2}{3}=\frac{2}{3}</span><span><span><span></span><span>2</span><span></span><span>×</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>0</span><span></span><span>×</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ，选择 2 的收益为 <span><span>0×13+1×23=230\times\frac{1}{3}+1\times\frac{2}{3}=\frac{2}{3}</span><span><span><span></span><span>0</span><span></span><span>×</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>+</span><span></span></span><span><span></span><span>1</span><span></span><span>×</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ，混合策略的收益也为 <span><span>23\frac{2}{3}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ，改选纯策略并非严格有利的策略。对于女方也类似。</p><p>这里混合策略中的概率，并不一定是字面意义上的随机化。可以理解为实际约会中，对手对你会怎么做的一种猜测。</p><p>至于为什么混合策略的期望收益只有 <span><span>23\frac{2}{3}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> ，这是因为纯策略下，双方见面成功，但是混合策略下，双方只有 <span><span>23×13+13×23=49\frac{2}{3}\times\frac{1}{3}+\frac{1}{3}\times\frac{2}{3}=\frac{4}{9}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>×</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>+</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>×</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>9</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>4</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 的概率见面成功。</p></section><section><h2>3.4 Tax Payer 纳税人<a href="#34-tax-payer-纳税人"><span>#</span></a></h2><p>考虑以下情形：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211612096.png" alt="" /></p><p>如图，纯策略下并不存在 NE 。同上，计算混合策略：</p><p>设税务人员采用的混合策略为 <span><span>(p,1−p)(p,1-p)</span><span><span><span></span><span>(</span><span>p</span><span>,</span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span></span></span></span> ，纳税者采用的策略为 <span><span>(q,1−q)(q,1-q)</span><span><span><span></span><span>(</span><span>q</span><span>,</span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span></span></span></span> 。</p><span><span><span>EUA(审查,q&amp;1−q)=2×q+(1−q)×4=EUA(不审查,q&amp;1−q)=4×q+0×(1−q)⇒q=23EU_A(\text{审查},q\&amp;1-q)=2\times q + (1-q)\times 4=EU_A(\text{不审查},q\&amp;1-q)=4\times q + 0\times (1-q)\Rightarrow q = \frac{2}{3}</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>审查</span></span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>2</span><span></span><span>×</span><span></span></span><span><span></span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>×</span><span></span></span><span><span></span><span>4</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>不审查</span></span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>4</span><span></span><span>×</span><span></span></span><span><span></span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>0</span><span></span><span>×</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>⇒</span><span></span></span><span><span></span><span>q</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>3</span></span></span><span><span></span><span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><span><span><span>EUT(p&amp;1−p,如实申报)=0×p+(1−p)×0=EUT(p&amp;1−p,瞒报)=−10×p+4×(1−p)⇒p=27EU_T(p\&amp; 1-p,\text{如实申报})=0\times p + (1-p)\times 0=EU_T(p\&amp; 1-p,\text{瞒报})=-10\times p + 4\times (1-p)\Rightarrow p = \frac{2}{7}</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>p</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>,</span><span></span><span><span>如实申报</span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>0</span><span></span><span>×</span><span></span></span><span><span></span><span>p</span><span></span><span>+</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span><span></span><span>×</span><span></span></span><span><span></span><span>0</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>T</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>p</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>,</span><span></span><span><span>瞒报</span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>−</span><span>10</span><span></span><span>×</span><span></span></span><span><span></span><span>p</span><span></span><span>+</span><span></span></span><span><span></span><span>4</span><span></span><span>×</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>p</span><span>)</span><span></span><span>⇒</span><span></span></span><span><span></span><span>p</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>7</span></span></span><span><span></span><span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>混合策略下，<span><span>NE=[(27,57),(23,13)]NE=[(\frac{2}{7},\frac{5}{7}),(\frac{2}{3},\frac{1}{3})]</span><span><span><span></span><span>N</span><span>E</span><span></span><span>=</span><span></span></span><span><span></span><span>[(</span><span><span></span><span><span><span><span><span><span></span><span><span><span>7</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>7</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>5</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>)</span><span>,</span><span></span><span>(</span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>,</span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>1</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>)]</span></span></span></span> 。容易验证。即对于税务人员以 <span><span>27\frac{2}{7}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>7</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 的概率进行审查（或者说审查 <span><span>27\frac{2}{7}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>7</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 的纳税人），而对于纳税者，以 <span><span>23\frac{2}{3}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 的概率选择如实纳税。当然，这都是<strong>从个体采取的策略来理解的</strong>。</p><p>还有一种理解是，这里的概率不一定要理解为策略的随机化，也可以视为采取不同策略的人的比例，比如该案例的<strong>总体</strong>中，有 <span><span>23\frac{2}{3}</span><span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>3</span></span></span></span><span><span></span><span></span></span><span><span></span><span><span><span>2</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span> 选择如实纳税，即可以用来<strong>预测比例</strong>。</p><p>接下来，从政策制定者的角度考虑，如果你发现逃税者的比例很高，并希望通过提升偷税漏税的惩罚力度来减少逃税者，会发生什么呢？假如博弈矩阵变化如下：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211555955.png" alt="" /></p><p>我们希望计算 <span><span>qq</span><span><span><span></span><span>q</span></span></span></span> ，即逃税者的比例，需要计算税务人员的期望收益，即：</p><span><span><span>EUA(审查,q&amp;1−q)=2×q+(1−q)×4=EUA(不审查,q&amp;1−q)=4×q+0×(1−q)⇒q=23EU_A(\text{审查},q\&amp;1-q)=2\times q + (1-q)\times 4=EU_A(\text{不审查},q\&amp;1-q)=4\times q + 0\times (1-q)\Rightarrow q = \frac{2}{3}</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>审查</span></span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>2</span><span></span><span>×</span><span></span></span><span><span></span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>×</span><span></span></span><span><span></span><span>4</span><span></span><span>=</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>A</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span><span>不审查</span></span><span>,</span><span></span><span>q</span><span>&amp;1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>4</span><span></span><span>×</span><span></span></span><span><span></span><span>q</span><span></span><span>+</span><span></span></span><span><span></span><span>0</span><span></span><span>×</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>q</span><span>)</span><span></span><span>⇒</span><span></span></span><span><span></span><span>q</span><span></span><span>=</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>3</span></span></span><span><span></span><span></span></span><span><span></span><span><span>2</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>和之前一样，纳税意愿并没有改变。不改变税务人员的收益，自然不会改变纳税人的纳税意愿。新均衡只改变了 <span><span>pp</span><span><span><span></span><span>p</span></span></span></span> ，容易计算出 <span><span>pp</span><span><span><span></span><span>p</span></span></span></span> 会减小，即税务人员审查的力度下降了。</p></section></section>
<section><h1>四、Evolution 进化论<a href="#四evolution-进化论"><span>#</span></a></h1><section><h2>4.1 Simplified Model 简化模型<a href="#41-simplified-model-简化模型"><span>#</span></a></h2><p>简化的模型有以下假设：</p><ol>
<li>种群内部竞争；</li>
<li>博弈者之间的各类情况互为镜像；</li>
<li>博弈者们身处一个大数量的种群中，随机配对博弈；</li>
<li>相对成功的策略会增长；</li>
<li>不存在基因重组，即只考虑无性繁殖；</li>
</ol><p>现在，假如一个大种群中的所有个体都采用了相同策略 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> ，但是某一时刻有一部分个体发生了“变异”，改为采用策略 <span><span>S′S^\prime</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>′</span></span></span></span></span></span></span></span></span></span></span> ，如果发生变异的个体采用的策略只可能导致灭绝，那么则称原策略 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> 是<strong>进化稳定（Evolutionarily Stable）</strong> 的。</p><p>继续以囚徒困境为例：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211239564.png" alt="" /></p><p>C 和 D 分别代表合作/不合作。现在，假设所有鼠鼠都是天生理性的，都会选择合作。显然，如果都是合作型鼠鼠，无论如何随机配对，互相都有收益，能无限繁衍下去。</p><p>现在，假如突然产生了变异，出现了一只不合作鼠鼠，它随机配对到一个合作型鼠鼠进行博弈，于是这个合作型鼠鼠会死，不合作鼠鼠活下去并无性生殖，不合作鼠鼠越来越多，不会灭绝。</p><p>显然，合作策略下，发生突变的个体并不会灭绝，所以合作策略并不是一个进化稳定的策略。或者你也可以通过计算期望收益，假设合作的个体占种群的 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span> ：</p><span><span><span>E(C)=2ε+0(1−ε)=2εE(D)=3ε+1(1−ε)=2ε+1\begin{aligned}
&amp; E(C)=2\varepsilon + 0(1-\varepsilon)=2\varepsilon\\
&amp; E(D)=3\varepsilon + 1(1-\varepsilon)=2\varepsilon + 1
\end{aligned}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span><span><span><span><span><span></span><span><span></span><span>E</span><span>(</span><span>C</span><span>)</span><span></span><span>=</span><span></span><span>2</span><span>ε</span><span></span><span>+</span><span></span><span>0</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>ε</span><span>)</span><span></span><span>=</span><span></span><span>2</span><span>ε</span></span></span><span><span></span><span><span></span><span>E</span><span>(</span><span>D</span><span>)</span><span></span><span>=</span><span></span><span>3</span><span>ε</span><span></span><span>+</span><span></span><span>1</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>ε</span><span>)</span><span></span><span>=</span><span></span><span>2</span><span>ε</span><span></span><span>+</span><span></span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><p>显然，不合作个体的收益更大，它们不会灭绝，并且不合作才是进化稳定（ES）的策略。由此案例我们可以得出 2 个结论：</p><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 1</strong>：自然选择的结果可能是糟糕的。</p></div></div><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 2</strong>：严格劣势策略一定不是 ES ，会在进化中被淘汰。</p></div></div><p>为什么该模型会推出与现实似乎有些相悖的结论呢？简单来说，就是因为该模型假设的是无性繁殖，没有考虑基因的融合。</p><p>再考虑以下博弈：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211224182.png" alt="" /></p><p>假如种群中的所有个体天生都会选择 c ，c 是 ES 吗？考虑 b 入侵 c 的情况，设 b 占比为 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span> ，则：</p><span><span><span>EUc=0(1−ε)+1(ε)=εEUb=1(1−ε)+0(ε)=1−ε\begin{aligned}
&amp;EU_c=0(1-\varepsilon)+1(\varepsilon)=\varepsilon\\
&amp;EU_b=1(1-\varepsilon)+0(\varepsilon)=1-\varepsilon
\end{aligned}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span><span><span><span><span><span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span>0</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>ε</span><span>)</span><span></span><span>+</span><span></span><span>1</span><span>(</span><span>ε</span><span>)</span><span></span><span>=</span><span></span><span>ε</span></span></span><span><span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>b</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span>1</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>ε</span><span>)</span><span></span><span>+</span><span></span><span>0</span><span>(</span><span>ε</span><span>)</span><span></span><span>=</span><span></span><span>1</span><span></span><span>−</span><span></span><span>ε</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><p>由于 <span><span>ε\varepsilon</span><span><span><span></span><span>ε</span></span></span></span> 是一个无穷小量，所以 <span><span>EUb&gt;EUcEU_b &gt; EU_c</span><span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>b</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>&gt;</span><span></span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>c</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span> ，所以 c 不是 ES 。但 b 和 c 是对称的，同样可以推出 b 也不是 ES 。</p><p>该博弈中的 NE 不难推出是 (a, a)、(b, c) 和 (c, b) 。如果一个策略不是 NE ，那么肯定是不能形成进化稳定的，因为必然会出现有利变动入侵该策略。所以进化稳定策略一定是 NE 。</p><p>反过来成立吗，即 NE 一定是进化稳定策略吗？并非如此，可以看以下例子：</p><p><img src="https://webp-pic.yokumi.cn/2025/07/20250729211209379.png" alt="" /></p><p>NE = (a, a) and (b, b) ，而 (b, b) 并不是进化稳定的，因为：</p><span><span><span>EUb=0(1−ε)+0ε=0EUa=0(1−ε)+1ε=ε\begin{aligned}
&amp; EU_b = 0(1-\varepsilon)+0\varepsilon=0\\
&amp; EU_a = 0(1-\varepsilon)+1\varepsilon=\varepsilon\\
\end{aligned}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span><span><span><span><span><span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>b</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span>0</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>ε</span><span>)</span><span></span><span>+</span><span></span><span>0</span><span>ε</span><span></span><span>=</span><span></span><span>0</span></span></span><span><span></span><span><span></span><span>E</span><span><span>U</span><span><span><span><span><span><span></span><span><span>a</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span><span>0</span><span>(</span><span>1</span><span></span><span>−</span><span></span><span>ε</span><span>)</span><span></span><span>+</span><span></span><span>1</span><span>ε</span><span></span><span>=</span><span></span><span>ε</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></span></span><p>事实上，NE 与最优策略的交集才是进化稳定策略，即：</p><div><div><div></div><div>Note</div></div><div><p><strong>Lesson 3</strong>：严格纳什均衡和进化稳定策略互为充要条件。</p></div></div><p>最后，我们再从生物学和经济学的视角分别给出进化稳定的标准定义：</p><div><div><div></div><div>Important</div></div><div><p><strong>定义 4.1</strong>：在 2 个玩家的对称博弈（<strong>symmetric game</strong>）中，一个纯策略满足进化稳定策略（<strong>Evolutionarily Stable</strong>）当满足以下条件：</p><p><span><span>∃ε∗&gt;0\exists \varepsilon^* &gt; 0</span><span><span><span></span><span>∃</span><span><span>ε</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span><span>&gt;</span><span></span></span><span><span></span><span>0</span></span></span></span> 满足 <span><span>(1−ε)U(S,S)+εU(S,S∗)&gt;(1−ε)U(S∗,S)+εU(S∗,S∗)(1-\varepsilon)U(S,S)+\varepsilon U(S,S^*)&gt;(1-\varepsilon)U(S^*,S)+\varepsilon U(S^*,S^*)</span><span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>ε</span><span>)</span><span>U</span><span>(</span><span>S</span><span>,</span><span></span><span>S</span><span>)</span><span></span><span>+</span><span></span></span><span><span></span><span>ε</span><span>U</span><span>(</span><span>S</span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>)</span><span></span><span>&gt;</span><span></span></span><span><span></span><span>(</span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span>ε</span><span>)</span><span>U</span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>,</span><span></span><span>S</span><span>)</span><span></span><span>+</span><span></span></span><span><span></span><span>ε</span><span>U</span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>)</span></span></span></span> 对于其余所有策略 <span><span>S∗S^*</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span></span></span></span> 和对足够小的正数 <span><span>ε∗&lt;ε\varepsilon^*&lt;\varepsilon</span><span><span><span></span><span><span>ε</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span></span><span>&lt;</span><span></span></span><span><span></span><span>ε</span></span></span></span> 。</p></div></div><p>简单来说，就是少量使用其他策略 <span><span>S∗S^*</span><span><span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span></span></span></span> 的突变者不能获得更高收益，即突变体不能成功“入侵”，则称策略 <span><span>SS</span><span><span><span></span><span>S</span></span></span></span> 是进化稳定策略。</p><div><div><div></div><div>Important</div></div><div><p><strong>定义 4.2</strong>：在 2 个玩家的对称博弈（<strong>symmetric game</strong>）中，一个纯策略满足进化稳定策略（<strong>Evolutionarily Stable</strong>）当满足以下条件：</p><p>如果 <span><span>(S,S)(S,S)</span><span><span><span></span><span>(</span><span>S</span><span>,</span><span></span><span>S</span><span>)</span></span></span></span> 是一个对称纳什均衡，那么 <span><span>U(S,S)≥U(S,S∗),∀S∗U(S,S)\ge U(S,S^*),\forall S^*</span><span><span><span></span><span>U</span><span>(</span><span>S</span><span>,</span><span></span><span>S</span><span>)</span><span></span><span>≥</span><span></span></span><span><span></span><span>U</span><span>(</span><span>S</span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>)</span><span>,</span><span></span><span>∀</span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span></span></span></span> ；如果 <span><span>U(S,S)=U(S,S∗)U(S,S)=U(S,S^*)</span><span><span><span></span><span>U</span><span>(</span><span>S</span><span>,</span><span></span><span>S</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>U</span><span>(</span><span>S</span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>)</span></span></span></span> ，则 <span><span>U(S,S∗)&gt;U(S∗,S∗)U(S,S^*)&gt;U(S^*,S^*)</span><span><span><span></span><span>U</span><span>(</span><span>S</span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>)</span><span></span><span>&gt;</span><span></span></span><span><span></span><span>U</span><span>(</span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>,</span><span></span><span><span>S</span><span><span><span><span><span><span></span><span><span>∗</span></span></span></span></span></span></span></span><span>)</span></span></span></span> 。</p></div></div><p>经济学上的定义涵盖了 2 种情况，第一种情况属于严格 NE ，而第二种情况，即弱纳什均衡，则还需要进一步满足边界情况，即使突变个体和原始个体在博弈时收益还不错，但突变个体之间博弈收益极低。</p><p>这两个定义是等价的。</p><blockquote><p><em><strong>持续更新中…</strong></em></p></blockquote></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/notes-about-model-context-protocol/</id>
      <title type="text">MCP协议，换皮的工具调用？</title>
      <published>2025-04-04T13:25:00.000Z</published>
      <updated>2025-04-04T13:25:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/notes-about-model-context-protocol/"/>
      <summary type="text">假如有一个函数 getweather(location) ，传统的客服机器人的做法是： LLM 提供了强大的自然语言理解能力，而 Function Calling 的目标就是非结构化的自然语言描述 函数调用。现在的处理流程变为： Function Calling 虽然阐述了如何与大模型进行信息交换，但没有明确，还停留…</summary>
      <content type="html"><![CDATA[<section><h2>0. MCP 的前世: Function Calling<a href="#0-mcp-的前世-function-calling"><span>#</span></a></h2><p>假如有一个函数 <code>get_weather(location)</code> ，传统的客服机器人的做法是：</p><ol>
<li>接收用户输入（e.g. 帮我查一下杭州今天的天气）；</li>
<li>关键词提取/匹配或 NLP 等较为繁琐的转换来匹配程序员写的后端逻辑</li>
</ol><ul>
<li>e.g.<code>if (keyword === 'weather') return await get_weather("杭州");</code>）</li>
</ul><ol>
<li>后端调用 <code>get_weather()</code> 返回结果给前端。</li>
</ol><p>LLM 提供了强大的自然语言理解能力，而 Function Calling 的目标就是<strong>非结构化的自然语言描述 <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> 函数调用</strong>。现在的处理流程变为：</p><ol>
<li>前端接受用户输入；</li>
<li>后端携带用户提示和工具参数给 LLM；</li>
<li>LLM 返回需要被调用的工具列表（意图识别）；</li>
<li>后端执行工具的具体逻辑，并将返回结果和用户输入再传递给 LLM；</li>
<li>LLM 润色后返回最终结果给后端；</li>
<li>用户接收到输出；</li>
</ol></section>
<section><h2>1. MCP 时代<a href="#1-mcp-时代"><span>#</span></a></h2><section><h3>1.1 Why MCP?<a href="#11-why-mcp"><span>#</span></a></h3><p>Function Calling 虽然阐述了如何与大模型进行信息交换，但没有明确，还停留在具体情景具体代码实现层面。例如，假如你要开发一个 LLM Chat APP，你需要在你的 Server 上实现工具来调用外部服务，例如天气查询 API，同时你的工具还需要注册到大模型服务商的API 中，来完成工具调用的整个流程，工具注册、工具调用、结果返回等都需要你自己去实现，开发者需要关注的点过多，开发成本较高。</p><p>相比之下，MCP 提出了一个 Protocol 来定义，这样开发者只需要关注 MCP 工具包的接口，不需要关注具体数据交换的数据流了。APP 的 Server 充当 MCP Client 的角色，工具注册和调用的流程由 MCP Server 来完成，面向应用开发者，无需关注 MCP Server 内部如何实现。</p><p>正如标题所言，MCP 协议被不少人诟病的点是其似乎只是对 Function Calling 的换皮，但不能完全等同两者，两者是互补的，关键区别在于工具由谁来实现。</p></section><section><h3>1.2 What MCP do?<a href="#12-what-mcp-do"><span>#</span></a></h3><p>MCP 的目的主要是方便进行工具调用，我们先通过以下时序图回顾一下 MCP 协议做了什么。</p><p><img src="https://webp-pic.yokumi.cn/2025/09/20250915102603588.png" alt="" /></p><p>当前 MCP 主要做的是通过标准化 MCP Server 实现，使 LLM 能够与本地和远程资源进行交互。但 MCP 本身还主要围绕在对服务器的规定，包括客户端如何发现和注册工具（上图中的工具注册阶段）和如何调用工具。</p><p>作为开发者，亟待解决的一点是标准化 MCP Client 与 MCP Server 的交互方式（例如 Standard I/O, HTTP or WebSocket），当前 MCP 协议并没有对这一部分进行规范，导致开发者需要适配不同 MCP Server 的接口来实现工具调用的功能。</p><p>从上图中的数据通路可以看出，MCP Client 和 MCP Server 间主要有以下几种消息功能：</p><ul>
<li>ListToolsRequest/ListToolsResult：工具注册和发现的请求和结果；</li>
<li>CallToolRequest/CallToolResult：工具调用的请求和结果；</li>
</ul><p>下图示例会更加直观：</p><p><img src="https://webp-pic.yokumi.cn/2026/03/20260330125210869.png" alt="" /></p></section><section><h3>1.3 MCP 的局限性<a href="#13-mcp-的局限性"><span>#</span></a></h3><ul>
<li>MCP Client 还需要主动去适配这些 MCP Server，无疑增加了复杂度。</li>
<li>实际场景中往往不止需要一次、或者一类工具调用，可能需要循环与用户对话，并调用多个 MCP Server 的工具，但 MCP Client 与 MCP Server 的交互，MCP Client 与 LLM 交互的标准也没有统一，MCP 协议只是关注于标准化单次的工具调用。</li>
</ul><p><strong>Thinking:</strong></p><ul>
<li>统一 MCP Server 暴露的接口，类似 HTTP Stream。</li>
<li>统一大模型与 Client 交互的方式，比如 Prompt 的格式。</li>
<li>大模型并不调用工具，而是由 MCP Server 或软件系统调用工具执行获得结果再传递给大模型。这里事实上涉及两次与 LLM 的交互，第一次是获得需要调用的工具和参数（意图识别），第二次是获得结果再传递给 LLM 生成回答。统一暴露给客户端的接口，即生成完整的回答，同时这种方式还有没有优化空间？</li>
</ul></section></section>
<section><h2>2. How to develop MCP?<a href="#2-how-to-develop-mcp"><span>#</span></a></h2><p>很多主流编程语言都已经开发了针对 MCP 协议的 SDK，这里以 Python SDK 为例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>@mcp_tool</span><span>(</span></div></div><div><div><div>2</div></div><div><span>  </span><span>name</span><span>=</span><span>"get_weather"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>description</span><span>=</span><span>"Get the weather information for a given location"</span></div></div><div><div><div>4</div></div><div><span>)</span></div></div><div><div><div>5</div></div><div><span>def</span><span> </span><span>get_weather</span><span>(</span><span>location</span><span>:</span><span> </span><span>str</span><span>) -&gt; </span><span>str</span><span>:</span></div></div><div><div><div>6</div></div><div><span>  </span><span># 这里是工具的具体实现逻辑，例如调用天气 API 获取天气信息</span></div></div><div><div><div>7</div></div><div><span>  </span><span>return</span><span> </span><span>f</span><span>"The weather in </span><span>{</span><span>location</span><span>}</span><span> is sunny with a high of 25°C."</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>3. 针对 MCP 的常见误区<a href="#3-针对-mcp-的常见误区"><span>#</span></a></h2><ol>
<li>MCP Server 的几个关键要素：</li>
</ol><ul>
<li>工具由模型控制：模型决定何时调用工具，并使用工具返回的结果；</li>
<li>资源由 APP 控制：APP 负责提供资源；</li>
<li>Prompt 由用户控制：用户决定是否要使用提示词；</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/rag-retrieval-augmented-generation-personal-study-notes/</id>
      <title type="text">RAG（Retrieval-augmented generation）个人学习笔记</title>
      <published>2025-02-22T08:54:00.000Z</published>
      <updated>2025-02-22T08:54:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/rag-retrieval-augmented-generation-personal-study-notes/"/>
      <summary type="text">传统的 LLM 模型主要存在以下 2 个问题： RAG 可以通过结合基于检索和生成模型的优势来提供更准确和上下文相关的响应，可以添加自己的预料构建知识库。 理论上，对于需要外挂知识库，即需要处理大量文本数据的场景，不通过微调，将这些文本数据全部塞进 Prompt 未尝不可，但：</summary>
      <content type="html"><![CDATA[<section><h2>为什么需要 RAG？<a href="#为什么需要-rag"><span>#</span></a></h2><p>传统的 LLM 模型主要存在以下 2 个问题：</p><ul>
<li>幻觉</li>
<li>外部知识/最新知识的缺失。</li>
</ul><p>RAG 可以通过结合基于检索和生成模型的优势来提供更准确和上下文相关的响应，可以添加自己的预料构建知识库。</p><p></p><figure><img src="https://webp-pic.yokumi.cn/2026/01/20260101160018936.jpg" alt="RAG vs. fine-tuning a LLM model" /><figcaption>RAG vs. fine-tuning a LLM model</figcaption></figure><p></p><ol>
<li>RAG 更适合知识库需要动态更新的情景，这部分数据不作为用于 LLM 的训练数据，而是通过 LLM 的能力进行检索。</li>
<li>RAG 由于采用检索的形式，数据来源是可知的，而 LLM 是黑箱。</li>
<li>RAG 可以对生成过程提供更多控制。</li>
</ol></section>
<section><h2>Before RAG?<a href="#before-rag"><span>#</span></a></h2><p>理论上，对于需要外挂知识库，即需要处理大量文本数据的场景，不通过微调，将这些文本数据全部塞进 Prompt 未尝不可，但：</p><ul>
<li>超出 LLM 的上下文窗口 / 输入 Token 限制；</li>
<li>烧钱；</li>
<li>LLM 对于长文本的理解能力有限，可能无法正确理解和利用这些文本数据；</li>
<li>处理和响应的效率较低。</li>
</ul><p>从目标来看，我们需要的是大量文本数据中能够回答用户问题的相关信息，而不是让 LLM 直接处理这些文本数据。RAG 的出现正是为了满足这一需求，其分为两步：</p><ol>
<li>分块：将大量文本数据分成更小的块，便于处理和检索。</li>
<li>检索：根据用户的查询，从这些块中检索出相关块放入 Prompt 中，供 LLM 生成回答。</li>
</ol></section>
<section><h2>RAG 的实现<a href="#rag-的实现"><span>#</span></a></h2><section><h3>Text Chunking<a href="#text-chunking"><span>#</span></a></h3><ol>
<li>Size-based chunking：根据文本块的大小进行分块；
<ul>
<li>优点：简单易行；</li>
<li>缺点：可能会切断句子或段落，导致信息丢失或上下文不完整，因此实现上会在句子间引入一定的重叠。</li>
</ul>
</li>
<li>Structure-based chunking：根据文本的结构进行分块，例如段落、章节等；
<ul>
<li>优点：保持文本的完整性和上下文关系；</li>
<li>缺点：可能会导致块的大小不均匀，某些块可能过大或过小。并且实际应用中文本并不一定有明确的结构。</li>
</ul>
</li>
<li>Semantic-based chunking：根据文本的语义内容进行分块，例如使用自然语言处理技术识别主题或概念；
<ul>
<li>优点：可以更好地捕捉文本的语义信息和上下文关系；</li>
<li>缺点：实现复杂，可能需要更多的计算资源和时间。</li>
</ul>
</li>
</ol></section><section><h3>Semantic Search<a href="#semantic-search"><span>#</span></a></h3><p>语义搜索，通过将文本块和用户查询转换为向量表示（Text Embeddings），并使用向量相似度来检索相关块。</p></section><section><h3>a basis Pipeline for RAG<a href="#a-basis-pipeline-for-rag"><span>#</span></a></h3><ol>
<li>Chunk source text.</li>
<li>Generate embeddings.</li>
<li>Store embeddings in a vector database.</li>
<li>Embed user query, find closest chunks.
<ul>
<li>通过余弦相似度计算用户查询与文本块之间的相似度，检索出相关块。</li>
<li>范围为 -1 到 1，值越大表示越相似。</li>
</ul>
</li>
<li>Add related chunks to the prompt, generate answer.</li>
</ol></section><section><h3>Integrating with Lexical Search<a href="#integrating-with-lexical-search"><span>#</span></a></h3><p>语义搜索虽然能够捕捉文本的语义信息，但在某些情况下可能会遗漏一些重要的细节信息。为了弥补这一不足，可以将语义搜索与词汇搜索（Lexical Search）结合起来，常用方法是 BM25 算法，其基本流程如下：</p><ol>
<li>Tokenize the user query.</li>
<li>For each text chunk, calculate the BM25 score based on the frequency of query tokens in the chunk and the overall token distribution in the corpus.</li>
<li>Rank the chunks based on their BM25 scores and select the top N chunks for further processing.</li>
</ol></section><section><h3>a hybrid Pipeline for RAG<a href="#a-hybrid-pipeline-for-rag"><span>#</span></a></h3><ul>
<li>Retriever（检索器）：处理用户查询，使用语义搜索 + 词汇搜索来检索相关文本块。</li>
</ul><p>将语义搜索和词汇搜索得到的结果混合需要进行一定的排序，常用方法是 Reciprocal Rank Fusion（倒数排序），其计算方式如下：</p><span><span><span>RRF(d)=∑i=1n1k+ranki(d)RRF(d) = \sum_{i=1}^{n} \frac{1}{k + rank_i(d)}</span><span><span><span></span><span>R</span><span>R</span><span>F</span><span>(</span><span>d</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>i</span><span>=</span><span>1</span></span></span></span><span><span></span><span><span>∑</span></span></span><span><span></span><span><span><span>n</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>k</span><span></span><span>+</span><span></span><span>r</span><span>an</span><span><span>k</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>d</span><span>)</span></span></span><span><span></span><span></span></span><span><span></span><span><span>1</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span></span></span></span></span><p>其中，<span><span>RRF(d)RRF(d)</span><span><span><span></span><span>R</span><span>R</span><span>F</span><span>(</span><span>d</span><span>)</span></span></span></span> 是文档 <span><span>dd</span><span><span><span></span><span>d</span></span></span></span> 的最终得分，<span><span>nn</span><span><span><span></span><span>n</span></span></span></span> 是搜索方法的数量，<span><span>ranki(d)rank_i(d)</span><span><span><span></span><span>r</span><span>an</span><span><span>k</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>d</span><span>)</span></span></span></span> 是文档 <span><span>dd</span><span><span><span></span><span>d</span></span></span></span> 在第 <span><span>ii</span><span><span><span></span><span>i</span></span></span></span> 个搜索方法中的排名，<span><span>kk</span><span><span><span></span><span>k</span></span></span></span> 是一个常数（通常设置为 60）以避免排名为 0 的情况。分数越高表示文档越相关。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/nsfc-data-crawling/</id>
      <title type="text">NSFC 数据爬取</title>
      <published>2025-01-22T12:47:00.000Z</published>
      <updated>2025-01-22T12:47:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/nsfc-data-crawling/"/>
      <summary type="text">从国家自然科学基金大数据知识管理服务门户爬取论文数据。 在网页的 JavaScript 文件中，尝试寻找相关的解密代码，最终在app.17b88e26.js中找到以下内容（注释为后期补充）： 使用 Python 的 pycryptodome 库来实现解密：</summary>
      <content type="html"><![CDATA[<section><h1>需求<a href="#需求"><span>#</span></a></h1><p>从国家自然科学基金大数据知识管理服务门户爬取论文数据。</p></section>
<section><h1>搜索API<a href="#搜索api"><span>#</span></a></h1><ol>
<li>打开浏览器的<strong>开发者工具</strong>（如 Chrome 的 F12）；</li>
<li>在网络（Network）选项卡中，监控搜索的请求；</li>
<li>找到接口()[<a href="https://kd.nsfc.cn/api/baseQuery/completionQueryResultsData%5D%EF%BC%9B" target="_blank">https://kd.nsfc.cn/api/baseQuery/completionQueryResultsData]；</a></li>
<li>其请求体如下：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span><span>"code"</span><span>:</span><span>"A01"</span><span>,</span><span>"fuzzyKeyword"</span><span>:</span><span>""</span><span>,</span><span>"complete"</span><span>:</span><span>true</span><span>,</span><span>"isFuzzySearch"</span><span>:</span><span>false</span><span>,</span><span>"conclusionYear"</span><span>:</span><span>"2020"</span><span>,</span><span>"dependUnit"</span><span>:</span><span>""</span><span>,</span><span>"keywords"</span><span>:</span><span>""</span><span>,</span><span>"pageNum"</span><span>:</span><span>0</span><span>,</span><span>"pageSize"</span><span>:</span><span>10</span><span>,</span><span>"personInCharge"</span><span>:</span><span>""</span><span>,</span><span>"projectName"</span><span>:</span><span>""</span><span>,</span><span>"projectType"</span><span>:</span><span>"218"</span><span>,</span><span>"subPType"</span><span>:</span><span>""</span><span>,</span><span>"psPType"</span><span>:</span><span>""</span><span>,</span><span>"ratifyNo"</span><span>:</span><span>""</span><span>,</span><span>"ratifyYear"</span><span>:</span><span>""</span><span>,</span><span>"order"</span><span>:</span><span>"enddate"</span><span>,</span><span>"ordering"</span><span>:</span><span>"desc"</span><span>,</span><span>"codeScreening"</span><span>:</span><span>""</span><span>,</span><span>"dependUnitScreening"</span><span>:</span><span>""</span><span>,</span><span>"keywordsScreening"</span><span>:</span><span>""</span><span>,</span><span>"projectTypeNameScreening"</span><span>:</span><span>""</span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>需要用到的字段只有：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>code </span><span>// 申请代码</span></div></div><div><div><div>2</div></div><div><span>conclusionYear </span><span>// 结题年度</span></div></div><div><div><div>3</div></div><div><span>projectType </span><span>// 资助类别</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>观察其返回内容：<img src="https://webp-pic.yokumi.cn/2026/01/20260101154458269.png" alt="" />为一串无规律的长字符串，初步判断是经过了某种加密；</li>
</ol></section>
<section><h1>解密<a href="#解密"><span>#</span></a></h1><p>页面的HTML文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>body</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>&lt;</span><span>noscript</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>&lt;</span><span>strong</span><span>&gt;We're sorry but 国家自然科学基金大数据知识管理服务门户 doesn't work properly without JavaScript enabled. Please enable it to continue.&lt;/</span><span>strong</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;/</span><span>noscript</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"app"</span><span>&gt;&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>&lt;</span><span>script</span><span> </span><span>src</span><span>=</span><span>"/js/chunk-vendors.559d99e0.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>&lt;</span><span>script</span><span> </span><span>src</span><span>=</span><span>"/js/app.17b88e26.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;/</span><span>body</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在网页的 JavaScript 文件中，尝试寻找相关的解密代码，最终在app.17b88e26.js中找到以下内容（注释为后期补充）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>function</span><span> </span><span>g</span><span><span>(</span><span>e</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>// 定义密钥</span></div></div><div><div><div>3</div></div><div><span>    </span><span>var</span><span><span> </span><span>t</span><span> </span></span><span>=</span><span><span> </span><span>p</span><span>.</span></span><span>a</span><span>.</span><span>enc</span><span>.</span><span>Utf8</span><span>.</span><span>parse</span><span>(</span><span>"IFROMC86"</span><span>);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>// 使用 DES 解密方法</span></div></div><div><div><div>6</div></div><div><span>    </span><span>var</span><span><span> </span><span>n</span><span> </span></span><span>=</span><span><span> </span><span>p</span><span>.</span></span><span>a</span><span>.</span><span>DES</span><span>.</span><span>decrypt</span><span>(</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>8</div></div><div><span>            </span><span>ciphertext</span><span>:</span><span><span> </span><span>p</span><span>.</span></span><span>a</span><span>.</span><span>enc</span><span>.</span><span>Base64</span><span>.</span><span>parse</span><span><span>(</span><span>e</span><span>) </span></span><span>// 解析 Base64 编码的密文</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>t</span><span>, </span><span>// 解密密钥</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>12</div></div><div><span>            </span><span>mode</span><span>:</span><span><span> </span><span>p</span><span>.</span></span><span>a</span><span>.</span><span>mode</span><span>.</span><span>ECB</span><span>, </span><span>// 使用 ECB 模式</span></div></div><div><div><div>13</div></div><div><span>            </span><span>padding</span><span>:</span><span><span> </span><span>p</span><span>.</span></span><span>a</span><span>.</span><span>pad</span><span>.</span><span>Pkcs7</span><span> </span><span>// 使用 PKCS7 填充方式</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// 将解密后的内容解析为 UTF-8 字符串并转为 JSON</span></div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>n</span><span>.</span></span><span>toString</span><span><span>(</span><span>p</span><span>.</span></span><span>a</span><span>.</span><span>enc</span><span>.</span><span>Utf8</span><span>));</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用 Python 的 pycryptodome 库来实现解密：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pip</span><span> </span><span>install</span><span> </span><span>pycryptodome</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>from</span><span> Crypto.Cipher </span><span>import</span><span><span> </span><span>DES</span></span></div></div><div><div><div>2</div></div><div><span>from</span><span> Crypto.Util.Padding </span><span>import</span><span> unpad</span></div></div><div><div><div>3</div></div><div><span>import</span><span> base64</span></div></div><div><div><div>4</div></div><div><span>import</span><span> json</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>def</span><span> </span><span>decrypt_des_ecb</span><span>(</span><span>ciphertext</span><span>):</span></div></div><div><div><div>7</div></div><div><span>    </span><span># 密钥</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>key </span><span>=</span><span> </span><span>b</span><span>"IFROMC86"</span></div></div><div><div><div>9</div></div><div><span>    </span><span># Base64 解码</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>encrypted_data </span><span>=</span><span> base64.</span><span>b64decode</span><span>(ciphertext)</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span># 初始化 DES 解密器</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>cipher </span><span>=</span><span> </span><span>DES</span><span>.</span><span>new</span><span>(key, </span><span>DES</span><span>.</span><span>MODE_ECB</span><span>)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span># 解密数据并移除填充</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>decrypted_data </span><span>=</span><span> </span><span>unpad</span><span>(cipher.</span><span>decrypt</span><span>(encrypted_data), </span><span>DES</span><span>.block_size)</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span># 解密后的字符串转为 JSON 格式</span></div></div><div><div><div>19</div></div><div><span>    </span><span>return</span><span><span> json.</span><span>loads</span><span>(decrypted_data.</span><span>decode</span><span>(</span></span><span>'utf-8'</span><span>))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对网页发送请求，成功得到解密后的JSON文件：
<img src="https://webp-pic.yokumi.cn/2026/01/20260101154500349.png" alt="" /></p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.yokumi.cn/posts/free-alibaba-cloud-server-setup-basic-development-environment-deploy-blog-services/</id>
      <title type="text">一文白嫖阿里云服务器+配置基本开发环境+博客等服务部署</title>
      <published>2025-01-17T08:20:00.000Z</published>
      <updated>2025-01-17T08:20:00.000Z</updated>
      <author><name>Yokumi</name></author>
      <link rel="alternate" href="https://blog.yokumi.cn/posts/free-alibaba-cloud-server-setup-basic-development-environment-deploy-blog-services/"/>
      <summary type="text">阿里云的学生优惠会送一张无门槛300元代金券（传送门： 阿里云高校计划云工开物助力高校科研与教育加速-阿里云 ），刚好够白嫖用券专区中的小服务器一整年（还多出15块可以买个20g的流量包）；个人配置如下： 由于本人采用密钥对进行认证，在创建并绑定密钥对后会自动下载一个 .</summary>
      <content type="html"><![CDATA[<section><h1>零、 （白嫖） 步骤<a href="#零-白嫖-步骤"><span>#</span></a></h1><p>阿里云的学生优惠会送一张无门槛300元代金券（传送门： <a href="https://university.aliyun.com/" target="_blank">阿里云高校计划_云工开物_助力高校科研与教育加速-阿里云</a> ），刚好够白嫖用券专区中的小服务器一整年（还多出15块可以买个20g的流量包）；个人配置如下：</p><ul>
<li><strong>实例规格</strong>：<a href="https://help.aliyun.com/document_detail/25378.html?spm=5176.ecscore_server.0.0.38314df5CTmW3g#e" target="_blank">ecs.e-c1m1.large</a>；</li>
<li><strong>CPU&amp;内存</strong>：2核2G；</li>
<li><strong>操作系统</strong>：Ubuntu 22.04 LTS 64位；</li>
<li><strong>带宽计费方式</strong>：按使用流量；</li>
<li><strong>公网带宽</strong>：5 Mbps；</li>
</ul><blockquote><p><strong>注</strong>：别选固定带宽，这是另外的价钱；</p></blockquote><p>白嫖完后，进入配置；</p></section>
<section><h1>一、本地远程连接<a href="#一本地远程连接"><span>#</span></a></h1><section><h2>1.1 密钥对认证<a href="#11-密钥对认证"><span>#</span></a></h2><p>由于本人采用密钥对进行认证，在创建并绑定密钥对后会自动下载一个 <code>.pem</code> 的文件到本地，ssh连接需要采用以下方式：</p><ol>
<li>直接连接：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>-i</span><span> </span><span>~/.ssh/你的私钥文件</span><span> </span><span>用户名@服务器IP</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>简化连接方式</li>
</ol><p>每次手动输入私钥文件显然过于不便了，我们可以将私钥文件添加到SSH配置文件中；</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>nano</span><span> </span><span>~/.ssh/config</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>增加配置如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Host 服务器别名(随便取)</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>HostName 服务器IP</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>User 用户名</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>IdentityFile ~/.ssh/你的私钥文件</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>Port 22</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>按下 <code>^x</code> 和 <code>y</code> 保存后，可以直接采用服务器别名连接：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>服务器别名</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>1.2 常见问题<a href="#12-常见问题"><span>#</span></a></h2><ol>
<li>连接时报错：</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Warning: Permanently added '你的服务器IP' (ED25519) to the list of known hosts. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Permissions 0644 for '你的私钥文件地址' are too open. It is required that your private key files are NOT accessible by others. This private key will be ignored. Load key "你的私钥文件地址": bad permissions 用户名@服务器IP: Permission denied (publickey).</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这是因为SSH出于安全考虑，要求私钥文件的权限必须足够严格，不允许其他用户访问。你需要修改私钥文件的权限，确保只有你自己可以读取和写入：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>chmod</span><span> </span><span>600</span><span> </span><span>~/.ssh/你的私钥文件</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h1>二、基本环境配置<a href="#二基本环境配置"><span>#</span></a></h1><section><h2>2.1 基本工具安装<a href="#21-基本工具安装"><span>#</span></a></h2><ol>
<li>系统更新</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>upgrade</span><span> </span><span>-y</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>基本工具安装（有些已经自带）</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>build-essential</span><span> </span><span>curl</span><span> </span><span>wget</span><span> </span><span>git</span><span> </span><span>vim</span><span> </span><span>htop</span><span> </span><span>net-tools</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>防火墙配置</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>ufw</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span><span> </span><span>ssh</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span><span> </span><span>http</span></div></div><div><div><div>4</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>allow</span><span> </span><span>https</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>ufw</span><span> </span><span>enable</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.2 安装Docker<a href="#22-安装docker"><span>#</span></a></h2><p>以下步骤也可以直接参考 <a href="https://docs.docker.com/engine/install/ubuntu/" target="_blank">Docker 官方文档</a>；</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装所需依赖</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>apt-transport-https</span><span> </span><span>ca-certificates</span><span> </span><span>gnupg</span><span> </span><span>lsb-release</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 添加 Docker 的官方 GPG 密钥（通过阿里云镜像）</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/etc/apt/keyrings</span><span> &amp;&amp; </span><span>\</span></div></div><div><div><div>6</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg</span><span> | </span><span>sudo</span><span> </span><span>gpg</span><span> </span><span>--dearmor</span><span> </span><span>-o</span><span> </span><span>/etc/apt/keyrings/docker.gpg</span><span> &amp;&amp; </span><span>\</span></div></div><div><div><div>7</div></div><div><span>echo</span><span> </span><span>"deb [arch=$(</span><span>dpkg</span><span> </span><span>--print-architecture</span><span>) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(</span><span>lsb_release</span><span> </span><span>-cs</span><span>) stable"</span><span> | </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/apt/sources.list.d/docker.list</span><span> &gt; </span><span>/dev/null</span><span> &amp;&amp; </span><span>\</span></div></div><div><div><div>8</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>update</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 安装最新的 Docker</span></div></div><div><div><div>11</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>docker-ce</span><span> </span><span>docker-ce-cli</span><span> </span><span>containerd.io</span><span> </span><span>docker-buildx-plugin</span><span> </span><span>docker-compose-plugin</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>常见问题<a href="#常见问题"><span>#</span></a></h3><p>由于运营商网络等不稳定因素可能导致镜像加速器无法成功拉取到指定版本的容器镜像，阿里云提供了免费的镜像加速器，配置步骤如下：</p><ol>
<li>登录<a href="https://cr.console.aliyun.com/?spm=a2c4g.11186623.0.0.27881d82EVUzaG" target="_blank">容器镜像服务控制台</a>。</li>
<li>在左侧导航栏选择<strong>镜像工具</strong> &gt; <strong>镜像加速器</strong></li>
<li>在<strong>镜像加速器</strong>页面获取<strong>加速器地址</strong>。</li>
</ol><p>接下来，在云服务器上更新 Docker 配置：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/etc/docker</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/docker/daemon.json</span><span> &lt;&lt;-</span><span>'EOF'</span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>"registry-mirrors": ["你的镜像加速器地址"]</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>EOF</span></div></div><div><div><div>7</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>daemon-reload</span></div></div><div><div><div>8</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>restart</span><span> </span><span>docker</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>最后，验证是否可以成功拉取 Docker Hub镜像：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo docker run hello-world</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果看到以下输出，说明成功拉取！</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Hello from Docker!</span></div></div><div><div><div>2</div></div><div><span>This message shows that your installation appears to be working correctly.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>2.3 配置 git<a href="#23-配置-git"><span>#</span></a></h2><ol>
<li>这里采用使用 SSH 密钥认证，首先在云服务器上生成 SSH 密钥对；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh-keygen</span><span> </span><span>-t</span><span> </span><span>rsa</span><span> </span><span>-b</span><span> </span><span>4096</span><span> </span><span>-C</span><span> </span><span>"your_email@example.com"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可以直接一路回车到底，默认情况下，密钥会保存到 <code>~/.ssh/id_rsa</code>；</p><ol>
<li>将公钥添加到 GitHub 账户</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看公钥内容</span></div></div><div><div><div>2</div></div><div><span>cat</span><span> </span><span>~/.ssh/id_rsa.pub</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>复制后，登录到 <strong>GitHub</strong>，进入 <strong>Settings</strong> -&gt; <strong>SSH and GPG keys</strong>，点击 <strong>New SSH key</strong>，将公钥复制到 GitHub 的文本框中并保存；</p><ol>
<li>验证连接</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 测试连接</span></div></div><div><div><div>2</div></div><div><span>ssh</span><span> </span><span>-T</span><span> </span><span>git@github.com</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果配置成功，会观察到输出如下信息：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Hi username! You've successfully authenticated, but GitHub does not provide shell access.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.4 安装 Anaconda<a href="#24-安装-anaconda"><span>#</span></a></h2><ol>
<li>下载 Anaconda 的安装包</li>
</ol><p>你可以在 <a href="https://repo.anaconda.com/archive/" target="_blank">官网</a> 自行查找需要的版本，替换以下链接即可：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wget</span><span> </span><span>https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Linux-x86_64.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>安装anaconda</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 授予可执行权限</span></div></div><div><div><div>2</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>Anaconda3-2024.10-1-Linux-x86_64.sh</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 执行</span></div></div><div><div><div>5</div></div><div><span>./Anaconda3-2024.10-1-Linux-x86_64.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后疯狂回车（中间需要输入一次yes）即可；</p><ol>
<li>验证安装</li>
</ol><p>打开新的终端，输入：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>conda</span><span> </span><span>--version</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果输出正确的 conda 版本，则说明安装成功并且正确配置环境变量；</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>conda 24.9.2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.5 安装 MySQL<a href="#25-安装-mysql"><span>#</span></a></h2><ol>
<li>安装 MySQL APT 存储库</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>wget</span><span> </span><span>https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>安装 MySQL 服务器</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>#安装 MySQL 服务器</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>mysql-server</span></div></div><div><div><div>3</div></div><div><span>#启动 MySQL 服务</span></div></div><div><div><div>4</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>start</span><span> </span><span>mysql</span></div></div><div><div><div>5</div></div><div><span>#检查启动状态</span></div></div><div><div><div>6</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>status</span><span> </span><span>mysql</span></div></div><div><div><div>7</div></div><div><span>#在系统启动时自动启动。</span></div></div><div><div><div>8</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>enable</span><span> </span><span>mysql</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>安装安全向导（可选）</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>mysql_secure_installation</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>它会让你自信选择安全策略，包括如下内容：</p><ul>
<li>Would you like to setup VALIDATE PASSWORD component?
<ul>
<li>是否需要启用 <code>VALIDATE PASSWORD</code> 组件？</li>
</ul>
</li>
<li>There are three levels of password validation policy:
<ul>
<li>LOW    Length &gt;= 8</li>
<li>MEDIUM Length &gt;= 8, numeric, mixed case, and special characters</li>
<li>STRONG Length &gt;= 8, numeric, mixed case, special characters and dictionary file</li>
<li>这里选择输入 2 ，表示设置密码策略要求密码至少包含一个大写字母、一个小写字母、一个数字和一个特殊字符，并且密码总长度至少为8个字符；</li>
</ul>
</li>
<li>Remove anonymous users?
<ul>
<li>是否移除匿名用户？</li>
<li>这里输入 y ，表示移除；</li>
</ul>
</li>
<li>Disallow root login remotely?
<ul>
<li>禁止MySQL的 <code>root</code> 用户从远程登录；</li>
<li>这里选择 y；</li>
</ul>
</li>
<li>Remove test database and access to it?
<ul>
<li>是否需要移除MySQL自带的 <code>test</code> 数据库？</li>
<li>无所谓，删了吧；</li>
</ul>
</li>
<li>Reload privilege tables now?
<ul>
<li>输入 y ，重新加载权限表，使得更改生效；</li>
</ul>
</li>
</ul><ol>
<li>配置安全组规则</li>
</ol><p>配置安全组时，MySQL 默认使用3306端口。确保实例安全组的入站规则开放3306端口。如果选择了不同的端口，请根据实际情况调整安全组设置。具体步骤请参考<a href="https://help.aliyun.com/zh/ecs/user-guide/add-a-security-group-rule#concept-sm5-2wz-xdb" target="_blank">添加安全组规则</a>。</p><ol>
<li>添加用于远程访问的用户</li>
</ol><p>将以下输入的 <code>&lt;username&gt;</code> 和 <code>&lt;password&gt;</code> 部分自行修改；</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>mysql</span><span> </span><span>-uroot</span><span> </span><span>-p</span><span> </span><span>\</span></div></div><div><div><div>2</div></div><div><span>-e </span><span>"CREATE USER '&lt;username&gt;'@'%' IDENTIFIED BY '&lt;password&gt;';"</span><span> </span><span>\</span></div></div><div><div><div>3</div></div><div><span>-e </span><span>"GRANT ALL PRIVILEGES ON *.* TO '&lt;username&gt;'@'%' WITH GRANT OPTION;"</span><span> </span><span>\</span></div></div><div><div><div>4</div></div><div><span>-e </span><span>"FLUSH PRIVILEGES;"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>执行后需要输入 root 账户的密码；</p><ol>
<li>登录 mysql 账户验证</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>mysql</span><span> </span><span>-u</span><span> </span><span>&lt;username&gt;</span><span> </span><span>-p</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输入密码后，进入 mysql 环境，成功！</p><p>输入以下内容可以退出 mysql 环境：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>exit;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.6 配置 Node.js 开发环境<a href="#26-配置-nodejs-开发环境"><span>#</span></a></h2><ol>
<li>下载 NVM 源码</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>https://gitee.com/mirrors/nvm.git</span><span> </span><span>~/.nvm</span><span> &amp;&amp; </span><span>cd</span><span> </span><span>~/.nvm</span><span> &amp;&amp; </span><span>git</span><span> </span><span>checkout</span><span> </span><span>`</span><span>git</span><span> describe </span><span>--abbrev=0</span><span> </span><span>--tags</span><span>`</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>配置环境变量</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>sh</span><span> </span><span>-c</span><span> </span><span>'echo ". ~/.nvm/nvm.sh" &gt;&gt; /etc/profile'</span></div></div><div><div><div>2</div></div><div><span>source</span><span> </span><span>/etc/profile</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>设置镜像源</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>NVM_NODEJS_ORG_MIRROR</span><span>=</span><span>https</span><span>://</span><span>npmmirror</span><span>.</span><span>com</span><span>/</span><span>mirrors</span><span>/</span><span>node</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>自行下载所需版本，例如：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>v23.3.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>若观察到以下输出，说明安装成功：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Computing checksum with sha256sum</span></div></div><div><div><div>2</div></div><div><span>Checksums matched!</span></div></div><div><div><div>3</div></div><div><span>Now using node v23.3.0 (npm v10.9.0)</span></div></div><div><div><div>4</div></div><div><span>Creating default alias: default -&gt; v23.3.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.7 配置 Java 环境<a href="#27-配置-java-环境"><span>#</span></a></h2><ol>
<li>安装所需版本的JDK（以Java 1.8 为例）：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>openjdk-8-jdk</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果需要下载其他版本，可以运行以下命令查看：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>search</span><span> </span><span>openjdk</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>验证安装</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>java</span><span> </span><span>-version</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.8 Screen 工具<a href="#28-screen-工具"><span>#</span></a></h2><p>在通过 Scp 等远程传输文件时，特别是大文件时，往往因为 SSH 连接不稳定而导致中断。而 Screen 工具提供了从单个 SSH 会话中使用多个 shell 窗口的能力。当会话被分离或网络中断时 Screen 会话中启动的进程仍将运行，我们可以随时重新连接到 Screen 会话。</p><ol>
<li>安装 Screen</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>screen</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>创建一个会话</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>screen</span><span> </span><span>-S</span><span> </span><span>test</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>挂起一个会话</li>
</ol><p>按下 <code>Ctrl</code> + <code>a</code> + <code>d</code> 即可保持这个 Screen 到后台并回到我们 SSH 的主终端；</p><ol>
<li>恢复一个会话</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>screen</span><span> </span><span>-r</span><span> </span><span>-d</span><span> </span><span>test</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>即可重新进入该 Screen；</p><ol>
<li>查看已经存在的 Screen 终端</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>screen</span><span> </span><span>-ls</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>2.9 安装 Nginx 环境<a href="#29-安装-nginx-环境"><span>#</span></a></h2><ol>
<li>使用 Nginx 官方源安装；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>#更新系统已安装软件并更新包管理工具</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span><span> </span><span>-y</span></div></div><div><div><div>3</div></div><div><span>#Nginx安装前必要环境</span></div></div><div><div><div>4</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>curl</span><span> </span><span>gnupg2</span><span> </span><span>ca-certificates</span><span> </span><span>lsb-release</span><span> </span><span>ubuntu-keyring</span></div></div><div><div><div>5</div></div><div><span>#导入官方Nginx签名密钥</span></div></div><div><div><div>6</div></div><div><span>curl</span><span> </span><span>https://nginx.org/keys/nginx_signing.key</span><span> | </span><span>gpg</span><span> </span><span>--dearmor</span><span> | </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/usr/share/keyrings/nginx-archive-keyring.gpg</span><span> &gt;</span><span>/dev/null</span></div></div><div><div><div>7</div></div><div><span>#设置apt仓库</span></div></div><div><div><div>8</div></div><div><span>echo</span><span> </span><span>"deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu `</span><span>lsb_release</span><span> </span><span>-cs</span><span>` nginx"</span><span> | </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/apt/sources.list.d/nginx.list</span></div></div><div><div><div>9</div></div><div><span>#安装nginx</span></div></div><div><div><div>10</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>nginx</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>接下来根据实际需要修改配置文件；</p></section></section>
<section><h1>三、搭建 Cloudreve 云盘<a href="#三搭建-cloudreve-云盘"><span>#</span></a></h1><section><h2>3.1 快速部署<a href="#31-快速部署"><span>#</span></a></h2><p>下载 Cloudreve 发布版本，你可以自行从 <a href="https://github.com/cloudreve/Cloudreve/releases" target="_blank">Release</a> 选择适合你的操作系统的；</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wget</span><span> </span><span>https://github.com/cloudreve/Cloudreve/releases/download/3.8.3/cloudreve_3.8.3_linux_amd64.tar.gz</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 创建程序目录</span></div></div><div><div><div>4</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/usr/local/cloudreve</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 解压获取到的主程序到所在目录</span></div></div><div><div><div>7</div></div><div><span>tar</span><span> </span><span>-zxvf</span><span> </span><span>cloudreve_3.8.3_linux_amd64.tar.gz</span><span> </span><span>-C</span><span> </span><span>/usr/local/cloudreve</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>cd</span><span> </span><span>/usr/local/cloudreve</span></div></div><div><div><div>10</div></div><div><span># 赋予执行权限</span></div></div><div><div><div>11</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>./cloudreve</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># 启动 Cloudreve</span></div></div><div><div><div>14</div></div><div><span>./cloudreve</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>观察到以下输出，说明启动成功：<img src="https://webp-pic.yokumi.cn/2025/07/20250729201055001.png" alt="" /></p><p>在首次启动时，会创建初始管理员账号，请注意保管下方的管理员密码，<em><strong>此密码只会在首次启动时出现</strong></em>；</p><p>Cloudreve 默认会监听<code>5212</code>端口，你可以在浏览器中访问 <code>http://服务器IP:5212</code> 进入 Cloudreve。注意需要增加阿里云服务器的安全组规则。</p></section><section><h2>3.2 启动进程守护<a href="#32-启动进程守护"><span>#</span></a></h2><ol>
<li>编辑配置文件</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vim</span><span> </span><span>/usr/lib/systemd/system/cloudreve.service</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>[Unit]</span></div></div><div><div><div>4</div></div><div><span>Description</span><span>=</span><span>Cloudreve</span></div></div><div><div><div>5</div></div><div><span>Documentation</span><span>=</span><span>https://docs.cloudreve.org</span></div></div><div><div><div>6</div></div><div><span>After</span><span>=</span><span>network.target</span></div></div><div><div><div>7</div></div><div><span>After</span><span>=</span><span>mysqld.service</span></div></div><div><div><div>8</div></div><div><span>Wants</span><span>=</span><span>network.target</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>[Service]</span></div></div><div><div><div>11</div></div><div><span>WorkingDirectory</span><span>=</span><span>/usr/local/cloudreve</span></div></div><div><div><div>12</div></div><div><span>ExecStart</span><span>=</span><span>/usr/local/cloudreve/cloudreve</span></div></div><div><div><div>13</div></div><div><span>Restart</span><span>=</span><span>on-abnormal</span></div></div><div><div><div>14</div></div><div><span>RestartSec</span><span>=</span><span>5s</span></div></div><div><div><div>15</div></div><div><span>KillMode</span><span>=</span><span>mixed</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>StandardOutput</span><span>=</span><span>null</span></div></div><div><div><div>18</div></div><div><span>StandardError</span><span>=</span><span>syslog</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>[Install]</span></div></div><div><div><div>21</div></div><div><span>WantedBy</span><span>=</span><span>multi-user.target</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>更新配置并启动</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 更新配置</span></div></div><div><div><div>2</div></div><div><span>systemctl</span><span> </span><span>daemon-reload</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 启动服务</span></div></div><div><div><div>5</div></div><div><span>systemctl</span><span> </span><span>start</span><span> </span><span>cloudreve</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 设置开机启动</span></div></div><div><div><div>8</div></div><div><span>systemctl</span><span> </span><span>enable</span><span> </span><span>cloudreve</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>管理命令</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 启动服务</span></div></div><div><div><div>2</div></div><div><span>systemctl</span><span> </span><span>start</span><span> </span><span>cloudreve</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 停止服务</span></div></div><div><div><div>5</div></div><div><span>systemctl</span><span> </span><span>stop</span><span> </span><span>cloudreve</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 重启服务</span></div></div><div><div><div>8</div></div><div><span>systemctl</span><span> </span><span>restart</span><span> </span><span>cloudreve</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 查看状态</span></div></div><div><div><div>11</div></div><div><span>systemctl</span><span> </span><span>status</span><span> </span><span>cloudreve</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>访问前端服务</li>
</ol><p>通过  <code>https://你的服务器IP:5212</code> 即可访问；</p></section></section>
<section><h1>四、部署 hexo 博客<a href="#四部署-hexo-博客"><span>#</span></a></h1><blockquote><p>纯纯自用罢了；</p></blockquote><section><h2>4.1 服务端 git 的有关配置<a href="#41-服务端-git-的有关配置"><span>#</span></a></h2><ol>
<li>创建一个单独的用户用于管理仓库；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>adduser</span><span> </span><span>git</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>passwd</span><span> </span><span>git</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>设置 git 用户 sudo 权限；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>chmod</span><span> </span><span>740</span><span> </span><span>/etc/sudoers</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>vim</span><span> </span><span>/etc/sudoers</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>添加一行：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>git ALL=(ALL) ALL</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>按下 <code>:wq</code> 保存更改，然后：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>chmod</span><span> </span><span>400</span><span> </span><span>/etc/sudoers</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>上传公钥到服务器并添加到 git 用户</li>
</ol><p>在本机执行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>scp</span><span> </span><span>~/.ssh/id_rsa.pub</span><span> </span><span>root@&lt;你的服务器IP&gt;:/home/git/tempkey.pub</span><span> </span><span>-i</span><span> </span><span>~/.ssh/你的私钥文件.pem</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后登录服务器：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>你的服务器别名</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>su</span><span> </span><span>-</span><span> </span><span>git</span></div></div><div><div><div>3</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>~/.ssh</span></div></div><div><div><div>4</div></div><div><span>cat</span><span> </span><span>~/tempkey.pub</span><span> &gt;&gt; </span><span>~/.ssh/authorized_keys</span></div></div><div><div><div>5</div></div><div><span>chmod</span><span> </span><span>600</span><span> </span><span>~/.ssh/authorized_keys</span></div></div><div><div><div>6</div></div><div><span>chmod</span><span> </span><span>700</span><span> </span><span>~/.ssh</span></div></div><div><div><div>7</div></div><div><span>exit</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你同样可以在本机化简配置：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Host 你的服务器别名_2</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>HostName 你的服务器公网IP</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>User git</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>IdentityFile ~/.ssh/id_rsa</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>Port 22</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>4.2 配置 Git 仓库和部署钩子<a href="#42-配置-git-仓库和部署钩子"><span>#</span></a></h2><ol>
<li>初始化一个裸仓库；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>su</span><span> </span><span>git</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>~</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>init</span><span> </span><span>--bare</span><span> </span><span>hexo.git</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>添加 post-receive 自动部署钩子；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>vi</span><span> </span><span>~/hexo.git/hooks/post-receive</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输入以下内容并保存退出（）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/bin/bash</span></div></div><div><div><div>2</div></div><div><span>GIT_WORK_TREE=/data/hexo git checkout -f</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>赋予执行权限：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>~/hexo.git/hooks/post-receive</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>创建部署目录并授权</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/data/hexo</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>chown</span><span> </span><span>-R</span><span> </span><span>git:git</span><span> </span><span>/data/hexo</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>chmod</span><span> </span><span>-R</span><span> </span><span>755</span><span> </span><span>/data/hexo</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>4.3 Hexo 配置<a href="#43-hexo-配置"><span>#</span></a></h2><section><h3>4.3.x 关于资源文件夹的问题<a href="#43x-关于资源文件夹的问题"><span>#</span></a></h3><p>当然，使用图床等工具，直接在 markdown 中插入外链是最方便的，并且不用担心在博客迁移时发生问题。不过如果你想要保存图片在本地，那么可以参考如下方法：</p><ol>
<li>保存图片在 <code>source/</code> 文件夹下；</li>
</ol><p>这是肯定的，区别只是在于后面的引用格式问题；</p><ol>
<li>如果图片的目录为 <code>source/img/example.png</code> ；</li>
</ol><p>那么，默认配置下，你的引用格式应该为</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>![]</span><span><span>(</span><span>img/example.png</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你希望不同文章的图片保存在不同目录，你可以自行修改保存路径和相应的引用格式（<strong>注意是相对于 source 文件夹的路径</strong>）；当然，你也可以使用 Hexo 提供的配置方式，将配置文件 <code>_config.yml</code> 修改为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>post_asset_folder</span><span>: </span><span>true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但你每次通过命令行新建一个文章时，它会自动在 <code>_posts</code> 文件夹下生成一个同名文件夹，你可以将文章图片都放在该文件夹中，此时引用格式为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>![]</span><span><span>(</span><span>example.png</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是，这并不是特别方便，特别是对于需要通过别的阅读器，比如 Typora 等进行阅读。如果你希望维持相对路径格式又能对图片进行分类，你可以通过修改 Typora 设置来进行，但我推荐使用插件 <code>hexo-asset-link</code> 插件，这个插件可以保留你原本写的路径结构，即：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>![]</span><span><span>(</span><span>./文章名/example.png</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>安装和配置方法如下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-asset-link</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 <code>_config.yml</code> 中添加：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>asset_link:</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>enable: true</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>post_asset_folder: true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>重新生成并部署即可！</p><blockquote><p>其余部分暂略；</p></blockquote></section></section><section><h2>4.4 部署 Hexo 博客<a href="#44-部署-hexo-博客"><span>#</span></a></h2><section><h3>4.4.x 部署 Hexo 博客到云服务器<a href="#44x-部署-hexo-博客到云服务器"><span>#</span></a></h3><ol>
<li>安装 <code>hexo-deployer-git</code> 插件；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-deployer-git</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>修改 <code>_config.yml</code> 中的 deploy 部分；</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>deploy:</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>type: git</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>repo: git@你的服务器别名_2:/home/git/hexo.git</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>branch: master</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>运行以下命令进行部署；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hexo</span><span> </span><span>clean</span></div></div><div><div><div>2</div></div><div><span>hexo</span><span> </span><span>generate</span></div></div><div><div><div>3</div></div><div><span>hexo</span><span> </span><span>deploy</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或直接写成：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hexo</span><span> </span><span>c</span><span> &amp;&amp; </span><span>hexo</span><span> </span><span>g</span><span> &amp;&amp; </span><span>hexo</span><span> </span><span>d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>修改 Nginx 配置文件；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>vim</span><span> </span><span>/etc/nginx/nginx.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>user  nginx;</span></div></div><div><div><div>2</div></div><div><span>worker_processes  auto;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>error_log  /var/log/nginx/error.log notice;</span></div></div><div><div><div>5</div></div><div><span>pid        /var/run/nginx.pid;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>events {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>worker_connections  1024;</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>http {</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>include       /etc/nginx/mime.types;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>default_type  application/octet-stream;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '</span></div></div><div><div><div>16</div></div><div><span><span>                      </span></span><span>'$status $body_bytes_sent "$http_referer" '</span></div></div><div><div><div>17</div></div><div><span><span>                      </span></span><span>'"$http_user_agent" "$http_x_forwarded_for"';</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>access_log  /var/log/nginx/access.log  main;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>sendfile        on;</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>keepalive_timeout  65;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>include /etc/nginx/conf.d/*.conf;</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>修改虚拟主机配置；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>vim</span><span> </span><span>/etc/nginx/conf.d/default.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果没有域名，则修改为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>listen        80;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>listen   [::]:80;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>server_name  你的公网IP;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>location / {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>root   /data/hexo;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>index  index.html index.htm;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>error_page   500 502 503 504  /50x.html;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>location = /50x.html {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>root   /usr/share/nginx/html;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果仅有域名，则修改为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>listen        80;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>listen   [::]:80;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>server_name  yourdomain.com;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>location / {</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>root   /data/hexo;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>index  index.html index.htm;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>error_page   500 502 503 504  /50x.html;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>location = /50x.html {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>root   /usr/share/nginx/html;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你有域名并申请过 SSL 安全证书，则修改为：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>listen        80;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>listen   [::]:80;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>server_name  yourdomain.com;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>rewrite ^(.*)$ https://${server_name}$1 permanent;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>server {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>listen 443 ssl;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>server_name yourdomain.com;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>ssl_certificate     /path/to/your/cert.pem;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>ssl_certificate_key /path/to/your/key.pem;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>ssl_session_cache   shared:SSL:1m;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>ssl_session_timeout 5m;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>ssl_ciphers         HIGH:!aNULL:!MD5;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>ssl_prefer_server_ciphers on;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>location / {</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>root   /data/hexo;</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>index  index.html index.htm;</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>proxy_set_header   X-Real-IP        $remote_addr;</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>proxy_set_header   Host             $http_host;</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>error_page   500 502 503 504  /50x.html;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>location = /50x.html {</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>root   /usr/share/nginx/html;</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>33</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>注意将上面的域名、SSL 证书的路径字段替换为你实际的。</p><ol>
<li>启动 Nginx 进程；</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>start</span><span> </span><span>nginx</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>status</span><span> </span><span>nginx</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>systemctl</span><span> </span><span>reload</span><span> </span><span>nginx</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>阿里云服务器一般默认开放了 80 和 443 端口，如果服务商不同，请自行开放防火墙；</p><ol>
<li>配置域名解析（如果没有域名，则无需进行这一步）；</li>
</ol><p>如果你使用的是阿里云的 DNS，那么你需要登录域名管理后台，添加一条 A 记录：</p>

<table><thead><tr><th><strong>主机记录</strong></th><th><strong>类型</strong></th><th><strong>记录值</strong></th><th><strong>TTL</strong></th></tr></thead><tbody><tr><td>blog</td><td>A</td><td>你的公网IP</td><td>600(s)</td></tr></tbody></table><p>解析是否生效可以通过以下命令进行验证：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ping</span><span> </span><span>yourdomain.com</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你的域名 DNS 指向了非服务商，比如我使用了 <strong>Cloudflare 的 DNS</strong>，那么你需要在 Cloudflare 的控制面板中设置，添加 A 记录：</p>

<table><thead><tr><th><strong>类型</strong></th><th><strong>主机名</strong></th><th><strong>内容（IP 地址）</strong></th><th><strong>TTL</strong></th><th><strong>代理状态</strong></th></tr></thead><tbody><tr><td>A</td><td>blog</td><td>你的服务器公网IP</td><td>自动</td><td>关闭小云朵（点一下变灰）</td></tr></tbody></table><p>说明：如果你只是做博客访问，<strong>把“代理状态”的小云朵关闭（变灰）</strong>，否则 Nginx 会收到 Cloudflare 的 IP 而不是你用户的真实 IP；</p><p><strong>特别说明：</strong></p><blockquote><p>如果你使用的是中国大陆的服务器，那么需要提交备案申请，<strong>未备案域名将无法通过 HTTP/HTTPS 正常访问</strong>，云服务商会限制端口 80 和 443。如果你想通过云服务器部署个人博客，请遵循有关规定。 不过我懒。</p></blockquote><p><strong>补充：</strong></p><blockquote><p>备案时应该要求你的域名 DNS 指向阿里云 DNS，否则存在不通过的风险。（我记得备案的时候用途就不能写个人博客来着）</p></blockquote></section><section><h3>4.4.y 配置服务到 Github Page 或其他托管平台<a href="#44y-配置服务到-github-page-或其他托管平台"><span>#</span></a></h3><p>把 Hexo 部署到 GitHub Pages 或其他托管平台并绑定你的域名也同样可行且省事，而且完全免费、无需备案。</p><ol>
<li>新建一个 Github 仓库；</li>
</ol><p>命名为：（啥也不用勾选，空的公开仓库即可）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>XXX.github.io</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>修改 Hexo 的配置文件；</li>
</ol><p>将 deploy 部分改为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>deploy:</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>type: git</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>repo: 你的仓库地址</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>branch: main</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>如果你默认 GitHub 用 master 分支，那就把 branch 改为 master 即可；</p></blockquote><ol>
<li>绑定域名；</li>
</ol><p>首先，添加一个名为 CNAME 的文件到 source 目录下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>echo</span><span> </span><span>"yourdomain.com"</span><span> &gt; </span><span>source/CNAME</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>登录 Cloudflare ，添加如下记录：</p>

<table><thead><tr><th><strong>类型</strong></th><th><strong>名称</strong></th><th><strong>值</strong></th></tr></thead><tbody><tr><td>CNAME</td><td>blog</td><td>XXX.github.io</td></tr></tbody></table><blockquote><p>确保开启了 <strong>小云朵（代理）图标</strong>，启用 CDN 与 HTTPS；</p></blockquote><p>重新部署博客：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hexo</span><span> </span><span>clean</span><span> &amp;&amp; </span><span>hexo</span><span> </span><span>g</span><span> &amp;&amp; </span><span>hexo</span><span> </span><span>d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>设置 GitHub Pages 部署配置；</li>
</ol><p>在你的仓库中打开「Settings」→「Pages」，设置以下内容：</p><ul>
<li><strong>Source</strong> 设为：main 分支（或者 master，看你哪个在用）</li>
<li><strong>Branch folder</strong> 选的是 / (root)</li>
<li><strong>Custom domain</strong> 应该已填写为：yourdomain.com</li>
</ul><p>在重新部署一次，如果 DNS 已生效，那么就可以正常浏览你的博客了！</p></section></section></section>
<section><h1>五、WebDav 网盘服务部署<a href="#五webdav-网盘服务部署"><span>#</span></a></h1><section><h2>5.1 说明<a href="#51-说明"><span>#</span></a></h2><p>Zotero 默认提供 300M 大小的云同步空间，但同步较慢，且文献一多（主要是附件占用，不同步附件基本上用不完）空间就不够用了，好在它还提供了第三方云盘服务支持，本体直接支持 WebDav（坚果云等），或者使用插件来使用第三方网盘服务（比如百度云这种），区别如下：（来源：<a href="https://zotero-chinese.com/user-guide/sync" target="_blank">Zotero中文社区</a>）</p>

<table><thead><tr><th>对比项</th><th>Zotero 官方存储空间 / WebDAV 同步</th><th>Attanger + 同步盘 方案</th></tr></thead><tbody><tr><td>配置难度</td><td>简单</td><td>复杂</td></tr><tr><td>多台电脑上同步附件</td><td>是</td><td>是</td></tr><tr><td>更改附件存储位置/自定义存放附件的文件夹名</td><td>否</td><td>是</td></tr><tr><td>支持的网盘种类</td><td>很少</td><td>较多</td></tr><tr><td>是否可以在移动端 Zotero/Papership 打开附件</td><td>是</td><td>否</td></tr></tbody></table></section><section><h2>5.2 部署步骤<a href="#52-部署步骤"><span>#</span></a></h2><ol>
<li><strong>安装完整的 Nginx 模块</strong>：</li>
</ol><p>之前已经介绍并且配置过 Nginx 做端口转发，不过默认的 Nginx 并不提供 WebDav 模块，需要安装完整版：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>install</span><span> </span><span>nginx-full</span><span> </span><span>apache2-utils</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>创建存储目录</strong>：</li>
</ol><p>注意需要使用 www-data 用户组，它是 Nginx 的默认运行用户，具有读写权限，权限范围仅有 Web 服务需要访问的目录，确保安全性。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/var/www/zotero</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>chown</span><span> </span><span>-R</span><span> </span><span>www-data:www-data</span><span> </span><span>/var/www/zotero</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>创建 WebDav 用户</strong>：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 创建用户</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>htpasswd</span><span> </span><span>-c</span><span> </span><span>/etc/nginx/webdav.passwd</span><span> </span><span>username</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 之后添加用户</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>htpasswd</span><span> </span><span>/etc/nginx/webdav.passwd</span><span> </span><span>anotherusername</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>回车后会让你输出密码。</p><ol>
<li><strong>配置 Nginx</strong>：</li>
</ol><p>编辑配置文件：（也可以在默认配置文件 <code>/etc/nginx/sites-available/default</code> 中编辑）</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>nano</span><span> </span><span>/etc/nginx/conf.d/webdav.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>具体内容可参考：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>listen 80;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>server_name yourdomainname.com;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>location /zotero/ {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>alias /var/www/zotero/;</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>autoindex on;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>dav_methods PUT DELETE MKCOL COPY MOVE;</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>dav_ext_methods PROPFIND OPTIONS;</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>create_full_put_path on;</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>dav_access user:rw group:rw all:r;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>auth_basic "Restricted";</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>auth_basic_user_file /etc/nginx/webdav.passwd;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>client_max_body_size 2G;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>limit_except GET OPTIONS {</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>allow all;</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li><strong>在 Zotero 中修改配置并验证是否生效</strong>：</li>
</ol><p>打开 Zotero，在“设置” <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> “同步” <span><span>→\rightarrow</span><span><span><span></span><span>→</span></span></span></span> “文件同步”，选择 WebDav，填入你的服务器配置以及用户认证信息即可。</p><p><img src="https://webp-pic.yokumi.cn/2025/08/20250809161958089.png" alt="" /></p><ol>
<li>启用 HTTPS 以及证书认证；</li>
</ol><p>可选，由于我的服务器没备案，这一步就省略了。</p><blockquote><p><strong>注</strong>：不定期随缘更新中…</p></blockquote></section></section>]]></content>
    </entry>
</feed>
