<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/feed.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>youkiの博客</title><description>记录技术与生活</description><link>https://youki.fun</link><language>ko</language><item><title>现代前端基础：Vanilla 与数据驱动 UI</title><link>https://youki.fun/ko/post/vanilla-data-driven-ui</link><guid isPermaLink="false">ko:vanilla-data-driven-ui</guid><description>Vanilla 的含义、数据驱动 UI 与数据界面分离的思路，以及 React 状态驱动更新的核心概念。</description><pubDate>Mon, 24 Aug 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这篇笔记整理数据驱动 UI 的基础概念：vanilla、数据驱动界面、数据与界面分离、React 状态。&lt;/p&gt;
&lt;h2&gt;什么是 Vanilla&lt;a href=&quot;#什么是-vanilla&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Vanilla&lt;/strong&gt; 本义”香草”，软件行业借指”原味的、没有额外包装的版本”。&lt;strong&gt;Vanilla 前端&lt;/strong&gt;即：不依赖 React、Vue 这类框架，只用浏览器原生支持的 HTML、CSS、JavaScript 构建页面。&lt;/p&gt;
&lt;p&gt;边界不必较真——“用了第三方库还算不算 vanilla”没有定论，理解为”没有使用前端框架的写法”即可。&lt;/p&gt;
&lt;h2&gt;数据驱动界面&lt;a href=&quot;#数据驱动界面&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;核心定义：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;界面长什么样，不写死在组件里，而是照着”喂进去的值”来显示。值变了，显示就变。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;两个例子（示意）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// PageHeading 只负责排版，标题文字由外部传入&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;PageHeading&lt;/span&gt;&lt;span&gt; title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;关于我&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;     &lt;/span&gt;&lt;span&gt;// 个人主页&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;PageHeading&lt;/span&gt;&lt;span&gt; title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;文字实验室&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt; &lt;/span&gt;&lt;span&gt;// 文字实验室页&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;App&lt;/code&gt; 的页面切换同理：用户点导航栏，“当前页面”这个值变成 &lt;code&gt;&quot;home&quot;&lt;/code&gt; 或 &lt;code&gt;&quot;textlab&quot;&lt;/code&gt;，&lt;code&gt;App&lt;/code&gt; 据此决定渲染 &lt;code&gt;HomePage&lt;/code&gt; 还是 &lt;code&gt;TextLabPage&lt;/code&gt;。区别只在值的来路——前者由外部传入，后者由组件自己持有。&lt;/p&gt;
&lt;h2&gt;数据与界面分离&lt;a href=&quot;#数据与界面分离&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：文案直接写在组件 JSX 里，改一句”关于我”就得打开组件代码、在一堆标签中间找。而项目建成后数据的变化频率远高于界面结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法&lt;/strong&gt;：把要显示的内容集中到独立的数据文件：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// src/data/site.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; home&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	heroTitle: &lt;/span&gt;&lt;span&gt;&quot;关于我&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	heroSubtitle: &lt;/span&gt;&lt;span&gt;&quot;项目，创意，灵感，心得，我的作品&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	featuredWork: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;		kicker: &lt;/span&gt;&lt;span&gt;&quot;作品&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;		title: &lt;/span&gt;&lt;span&gt;&quot;文字实验室&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;		copy: &lt;/span&gt;&lt;span&gt;&quot;拼音和情绪，挖掘中文里的细节&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;		linkLabel: &lt;/span&gt;&lt;span&gt;&quot;打开作品&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	identity: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;		motto: &lt;/span&gt;&lt;span&gt;&quot;已识乾坤大，尤怜草木青&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;		learning: &lt;/span&gt;&lt;span&gt;&quot;零到全栈&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; textLab&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	heroTitle: &lt;/span&gt;&lt;span&gt;&quot;文字实验室&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	heroSubtitle: &lt;/span&gt;&lt;span&gt;&quot;拼音和情绪，挖掘中文里的细节&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这基本是一张&lt;strong&gt;纯内容清单&lt;/strong&gt;——网站上要显示的字都在这儿。它和 JSON 很像，区别在于对象最后一个成员的结尾允许带逗号。&lt;/p&gt;
&lt;p&gt;组件不再写死任何文案，只负责到清单里取值显示（示意）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// HomePage.jsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { home } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &quot;../data/site&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;{home.heroTitle}&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;职责划分：&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;只管什么&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;组件&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;”怎么显示”——排版、样式、结构&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;&lt;code&gt;site.js&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;”显示什么内容”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;收益&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;改文案只动 &lt;code&gt;site.js&lt;/code&gt;，组件一行不改；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多语言几乎免费&lt;/strong&gt;：再备一份英文清单，组件按当前语言读对应那张表即可，排版、结构、样式全部复用；&lt;/li&gt;
&lt;li&gt;将来引入后端 API，只需把数据来源换成接口返回，组件依然不需要改动。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;验证方式：改 &lt;code&gt;site.js&lt;/code&gt; 里任意字段并保存，页面当场更新，组件文件一个字没动。&lt;/p&gt;
&lt;h2&gt;状态驱动 UI&lt;a href=&quot;#状态驱动-ui&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;前面例子里的值有两个局限：从外面喂进来（哪怕集中进了 &lt;code&gt;site.js&lt;/code&gt;，也是父组件读了再传下去）；本身定死，不主动改就不会变。&lt;/p&gt;
&lt;p&gt;有些数据需要在用户操作过程中自己变。这种&lt;strong&gt;会变、且一变显示就跟&lt;/strong&gt;的值，在 React 里称为组件的&lt;strong&gt;状态（state）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由组件&lt;strong&gt;自己持有、自己管理&lt;/strong&gt;，不从外部传入（区别于 props）；&lt;/li&gt;
&lt;li&gt;有初始默认值；&lt;/li&gt;
&lt;li&gt;界面显示什么，完全照着这个值；&lt;/li&gt;
&lt;li&gt;允许被修改，且一改，界面当场跟着变。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;典型例子：输入框下方实时显示”已输入 N 字”。输入框内容是一个 state——打字就是在改这个值，字数照着它计算，值一改数字立刻刷新。&lt;/p&gt;
&lt;p&gt;实现原理可以先不深究：React 内部负责盯着这个值，值一变就自动刷新界面。需要认出的只有现象：&lt;strong&gt;state 一变，界面自动跟变。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;前文的导航切换就是一例：&lt;code&gt;App&lt;/code&gt; 里”现在在哪一页”就是一个 state。&lt;/p&gt;
&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;vanilla&lt;/strong&gt; = 不依赖框架、只用浏览器原生能力的写法；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据驱动界面&lt;/strong&gt;：界面照着”值”来显示，值变则显示变；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据与界面分离&lt;/strong&gt;：内容集中进独立数据文件，组件管”怎么显示”、数据表管”显示什么”，多语言这类需求因此近乎免费；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态驱动 UI&lt;/strong&gt;：state 是组件自己持有的可变数据，一变界面就跟，是 React 响应式更新的核心机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;a href=&quot;#参考&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://xn--ygr25xpohxwz.com/zero-to-fullstack/lessons/module-4-4/&quot;&gt;零基础到全栈 · 数据驱动课程原文&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:前端</category><category>tag:React</category><category>tag:数据驱动</category><category>tag:前端</category></item><item><title>现代前端基础：模块化、npm 与 Vite</title><link>https://youki.fun/ko/post/modularity-vite-npm</link><guid isPermaLink="false">ko:modularity-vite-npm</guid><description>从 script 引入与全局污染讲起，理解 ES 模块、构建工具与 npm 的定位，最后用 Vite 跑通 dev / build / preview 全流程。</description><pubDate>Sun, 23 Aug 2026 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;为什么需要模块化&lt;a href=&quot;#为什么需要模块化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;传统 script 引入&lt;a href=&quot;#传统-script-引入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script src=&quot;js/nav.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt; 引入的是本地文件的相对路径，含义是把当前目录下 &lt;code&gt;js&lt;/code&gt; 目录中的 &lt;code&gt;nav.js&lt;/code&gt; 引入页面。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 标签也可以引用发布在互联网上的 &lt;code&gt;.js&lt;/code&gt; 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://cdn.jsdelivr.net/npm/animejs@4/lib/anime.iife.min.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面这个地址指向 anime.js——一个专门提供动画方案的现成脚本。这类由他人编写、发布到互联网上供复用的脚本，统称为&lt;strong&gt;第三方库&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;全局污染&lt;a href=&quot;#全局污染&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cards.js&lt;/code&gt; 依赖 &lt;code&gt;anime.animate&lt;/code&gt;、&lt;code&gt;anime.stagger&lt;/code&gt; 这些名字才能运行。这是传统 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 引入方式的固有约定：&lt;strong&gt;库向页面全局提供自己的名字，使用者从全局取用&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;随着项目变大，引入的库不再只有一个——图表库、日期处理库、ajax 工具库……每个库都靠”往全局挂名字”的方式提供功能。而 JavaScript 的全局空间中，一个名字只能存一个值：一旦两个库挂载了同名成员，后挂载的会静默覆盖先挂载的，没有任何警告。&lt;/p&gt;
&lt;p&gt;这种现象称为&lt;strong&gt;全局污染&lt;/strong&gt;（global pollution）。小项目尚能靠记忆规避；项目一旦长大，撞名只是时间问题，而每一次撞名都是一个难以排查的 bug。&lt;/p&gt;
&lt;h3&gt;出路：工程化&lt;a href=&quot;#出路工程化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 手动排序与全局污染表面上是两个问题，根源相同：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“全部堆在一起 + 全局名称（变量）+ 手动排序”的传统组织方式，本来就不是为严肃项目设计的。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这不是工程师水平的问题，而是工具本身达到了能力上限。全球前端开发者都撞过这堵墙，并由此发展出一整套跨越它的方法，统称&lt;strong&gt;工程化&lt;/strong&gt;。工程化的第一招，就是&lt;strong&gt;模块化&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;模块化与 ES 模块&lt;a href=&quot;#模块化与-es-模块&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;export 与 import&lt;a href=&quot;#export-与-import&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;模块化与传统方式只在一个关键点上不同：&lt;strong&gt;共享的方式&lt;/strong&gt;。&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;传统方式&lt;/th&gt;&lt;th&gt;模块化&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;可见性&lt;/td&gt;&lt;td&gt;代码默认”全世界都看得见”（都堆在 &lt;code&gt;window&lt;/code&gt; 全局空间里）&lt;/td&gt;&lt;td&gt;代码默认”只有自己看得见”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提供功能&lt;/td&gt;&lt;td&gt;库向全局挂名，共享靠”公共”&lt;/td&gt;&lt;td&gt;必须显式标注 &lt;code&gt;export&lt;/code&gt;，共享靠”明文声明”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;使用功能&lt;/td&gt;&lt;td&gt;直接从全局取用&lt;/td&gt;&lt;td&gt;必须显式写 &lt;code&gt;import&lt;/code&gt;，不写就用不到&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;export&lt;/code&gt; 与 &lt;code&gt;import&lt;/code&gt; 是模块化的两个核心关键字：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;export&lt;/code&gt;&lt;/strong&gt;：给文件内的某个成员打上”对外可用”的标记；未标记的，外部拿不到。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;import&lt;/code&gt;&lt;/strong&gt;：在文件顶部明文声明”要使用某个文件里的某个成员”；不声明的，用不到。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;名词：ES 与 ECMAScript&lt;a href=&quot;#名词es-与-ecmascript&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;ES 是 ECMAScript 的缩写&lt;/strong&gt;，即 JavaScript 这门语言的官方标准规范。所有浏览器、所有 JS 引擎都必须按这份规范实现 JavaScript。日常所说的”JavaScript”，本质上就是”按 ECMAScript 规范实现的那套东西”。&lt;/p&gt;
&lt;p&gt;由此派生两个常见说法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ES 模块&lt;/strong&gt;：JavaScript 标准中定义的那套模块系统。它不是另一种语言，也不是新东西，而是 JavaScript 自带的官方模块方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ES6&lt;/strong&gt;：ECMAScript 的第 6 版（2015 年发布）。&lt;code&gt;import&lt;/code&gt; / &lt;code&gt;export&lt;/code&gt; 正式定义于这一版。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：&lt;strong&gt;ES 模块文件必须通过服务器提供才能加载&lt;/strong&gt;，浏览器不允许直接从 &lt;code&gt;file://&lt;/code&gt; 读取。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;构建工具&lt;a href=&quot;#构建工具&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;三方诉求的矛盾&lt;a href=&quot;#三方诉求的矛盾&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;工程治理、开发体验、用户体验，三方在互相拉扯：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工程治理&lt;/strong&gt;：希望项目规范、安全、好维护、好扩展（模块化、拆文件都是为此服务）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开发者&lt;/strong&gt;：希望写得简单、顺手、舒服；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;浏览器与最终用户&lt;/strong&gt;：希望加载更快、资源更少、性能更高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三者常常彼此冲突：为了规范牺牲开发便利，为了开发便利又影响运行效率，很难同时满足。构建工具就是化解这一矛盾的方案——&lt;strong&gt;让一类工具夹在中间做”翻译”&lt;/strong&gt;：工程的规矩照守，开发者按自己舒服的方式编写，最后由工具把源码&lt;strong&gt;转换成浏览器想要的样子&lt;/strong&gt;。这个转换过程叫&lt;strong&gt;前端构建&lt;/strong&gt;，这类工具叫&lt;strong&gt;构建工具&lt;/strong&gt;（build tool）。&lt;/p&gt;
&lt;h3&gt;构建工具的职责&lt;a href=&quot;#构建工具的职责&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;本地开发服务器 + 热更新&lt;/strong&gt;：改完一保存，浏览器自动更新，无需手动刷新；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;合并碎文件&lt;/strong&gt;：多个 CSS 合成 1 个、多个 JS 模块合成 1 个，请求数从十几个降到几个；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件名加 hash 指纹&lt;/strong&gt;：内容一变、文件名就变，浏览器立刻知道需要重新拉取，缓存随之失效。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;npm：项目级包管理&lt;a href=&quot;#npm项目级包管理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;npm 与 apt 的区别&lt;a href=&quot;#npm-与-apt-的区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;npm 与 apt 都是”应用商店”，但管辖范围不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;apt 给整台电脑装东西&lt;/strong&gt;：装一个 nginx，全系统都能用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;npm 默认给当前项目装东西&lt;/strong&gt;：A 项目用 anime.js 4.4、B 项目用 anime.js 3.0，两边各装各的、互不干扰。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正因为按项目管理，使用前必须先明确声明”这里有一个项目”，这个动作叫&lt;strong&gt;初始化&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;初始化与安装&lt;a href=&quot;#初始化与安装&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在项目根目录（与 &lt;code&gt;index.html&lt;/code&gt; 同级）运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; init&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该命令生成 &lt;code&gt;package.json&lt;/code&gt; 项目档案。有了 npm 与 &lt;code&gt;package.json&lt;/code&gt;，安装 Vite 只需一行（在 &lt;code&gt;index.html&lt;/code&gt;、&lt;code&gt;package.json&lt;/code&gt; 同级目录运行）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; -D&lt;/span&gt;&lt;span&gt; vite&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;-D&lt;/code&gt; 即 &lt;code&gt;--save-dev&lt;/code&gt;，表示把 Vite 装成&lt;strong&gt;开发依赖&lt;/strong&gt;——只在开发阶段使用，不进入最终产物。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Vite 实操：dev / build / preview&lt;a href=&quot;#vite-实操dev--build--preview&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Vite 的可执行文件位于 &lt;code&gt;node_modules/.bin/vite&lt;/code&gt;，通过该路径直接运行。&lt;/p&gt;
&lt;h3&gt;dev：开发服务器&lt;a href=&quot;#dev开发服务器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;./node_modules/.bin/vite&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;随改随看，保存即热更新。&lt;/p&gt;
&lt;h3&gt;build：打包上线版本&lt;a href=&quot;#build打包上线版本&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;./node_modules/.bin/vite&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行后生成 &lt;code&gt;dist&lt;/code&gt; 目录：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;dist/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── index.html&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── main-Beya6efK.css     ← 多个 CSS 合并成的 1 个文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └── main-_go62SDo.js      ← 多个 JS 模块合并成的 1 个文件&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;preview：上线前本地预览&lt;a href=&quot;#preview上线前本地预览&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;./node_modules/.bin/vite&lt;/span&gt;&lt;span&gt; preview&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动一个专门伺服 &lt;code&gt;dist&lt;/code&gt; 的服务器。打开后按 F12 查看 Network 面板——干净利落，只有寥寥几个请求。&lt;/p&gt;
&lt;h2&gt;参考&lt;a href=&quot;#参考&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://xn--ygr25xpohxwz.com/zero-to-fullstack/lessons/module-4-1/&quot;&gt;零基础到全栈 · 模块化课程原文&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:前端</category><category>tag:前端工程化</category><category>tag:ES Modules</category><category>tag:Vite</category><category>tag:npm</category></item><item><title>SSH 为什么会连到错误的服务器？一次 FlClash 核心异常排查</title><link>https://youki.fun/ko/post/flclash-ssh-misroute</link><guid isPermaLink="false">ko:flclash-ssh-misroute</guid><description>从 SSH 主机身份异常、TLS 连接重置到 FlClash 核心日志，复盘一次本地代理链路故障。</description><pubDate>Tue, 04 Aug 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近把一个旧服务器的 CPA/CPAMP 服务完整迁移到了新的服务器。Docker Compose、Caddy、HTTPS、历史数据和插件都已经验证正常，API 也可以正常调用。&lt;/p&gt;
&lt;p&gt;服务器迁移完成以后，我准备通过 SSH 继续维护旧服务器，却遇到了一个很奇怪的问题：SSH 有时直接提示连接被关闭，有时提示 &lt;code&gt;Permission denied (publickey)&lt;/code&gt;。更奇怪的是，显式指定另一把私钥后，连接似乎又成功了。&lt;/p&gt;
&lt;p&gt;后来我发现，问题并不是服务器突然忘记了公钥，也不是 CPA 或 Caddy 的配置错误，而是本地 FlClash 核心异常后，SSH 流量走到了错误的路径。&lt;/p&gt;
&lt;h2&gt;诡异的错误提示&lt;a href=&quot;#诡异的错误提示&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最开始看到的是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Connection closed by &amp;lt;SERVER_IP&amp;gt; port &amp;lt;SSH_PORT&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Permission denied (publickey)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这两条信息很容易把排查方向转向到服务器端：检查安全组、检查 SSH 服务、检查 &lt;code&gt;authorized_keys&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但 SSH 的认证错误有一个前提：客户端必须先连接到了正确的服务器。如果连接目标本身就错了，那么服务器返回的认证错误没有分析价值。此时继续修改密钥，只是在错误的机器上重复尝试。&lt;/p&gt;
&lt;p&gt;因此，第一步应该是确认服务器身份。&lt;/p&gt;
&lt;p&gt;先检查别名最终使用的地址和端口：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ssh &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;G &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ALIAS&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再使用详细模式连接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ssh &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;vvv &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ALIAS&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重点看三个信息：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最终连接的 IP 地址；&lt;/li&gt;
&lt;li&gt;最终连接的端口；&lt;/li&gt;
&lt;li&gt;服务器发送的 SSH 主机指纹。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;为什么监控网站还是正常的&lt;a href=&quot;#为什么监控网站还是正常的&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;排查过程中，监控面板和普通网站都可以打开，所以我一度以为服务器网络没有问题。&lt;/p&gt;
&lt;p&gt;其实这两个结论并不矛盾。浏览器访问网站通常经过域名解析、HTTPS 和浏览器代理；SSH 别名可能直接连接一个固定 IP 和自定义端口。它们经过的 DNS、规则匹配、代理模式和路由路径都可能不同。&lt;/p&gt;
&lt;h2&gt;用另一条路径确认问题在哪里&lt;a href=&quot;#用另一条路径确认问题在哪里&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我随后从另一台可以正常连接的服务器发起跳转，使用旧服务器对应的密钥和端口连接。通过跳板机访问时，看到的是旧服务器真实的主机信息，SSH 服务本身也能正常接受密钥。&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;检查对象&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;th&gt;结论&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;服务器 SSH 服务&lt;/td&gt;&lt;td&gt;正常&lt;/td&gt;&lt;td&gt;不是 &lt;code&gt;sshd&lt;/code&gt; 故障&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;服务器公钥认证&lt;/td&gt;&lt;td&gt;通过跳板机正常&lt;/td&gt;&lt;td&gt;不是 &lt;code&gt;authorized_keys&lt;/code&gt; 丢失&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DNS 解析&lt;/td&gt;&lt;td&gt;指向预期地址&lt;/td&gt;&lt;td&gt;DNS 不是主要原因&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;浏览器监控页面&lt;/td&gt;&lt;td&gt;正常&lt;/td&gt;&lt;td&gt;Web 路径仍可用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;本地直连 SSH&lt;/td&gt;&lt;td&gt;指纹和预期不一致&lt;/td&gt;&lt;td&gt;本地连接路径异常&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;FlClash 核心异常的证据&lt;a href=&quot;#flclash-核心异常的证据&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我导出了 FlClash 的日志。日志中多次出现下面的前端错误：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;IPC error: read error: failed to fill whole buffer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;IPC Disconnected&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这表示 FlClash 前端与核心之间的 IPC 通道没有读完一条完整消息就断开了。能够证明前端与核心之间发生过异常断连。&lt;/p&gt;
&lt;p&gt;时间线也能和网络现象对应起来：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IPC 断开时，多条 IP 检查请求同时出现 TLS handshake 失败或连接未收完响应头；&lt;/li&gt;
&lt;li&gt;核心重新连接后，IP 检查又恢复成功；&lt;/li&gt;
&lt;li&gt;手动重启 FlClash 核心后，SSH 和 HTTPS 连接也恢复正常。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;已确认的事实是：FlClash 前端记录到了 IPC 断开，SSH 的实际连接身份异常，并且重启核心后恢复。&lt;/p&gt;
&lt;p&gt;尚未确认的是：核心为什么会进入这个状态，以及 TUN 内部具体哪一步没有正确重建。原始故障发生时没能提前保存完整的核心实时流，因此根因没有确定。&lt;/p&gt;
&lt;h2&gt;最终修复：重启核心&lt;a href=&quot;#最终修复重启核心&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;直接重启 FlClash 核心后，问题恢复了。这个动作会让当前核心重新建立 IPC、TUN 和运行时网络状态，清理掉本次异常留下的临时状态。&lt;/p&gt;
&lt;p&gt;重启以后：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SSH 服务器主机指纹恢复为预期值；&lt;/li&gt;
&lt;li&gt;使用正确的私钥可以完成认证；&lt;/li&gt;
&lt;li&gt;HTTPS 请求不再出现握手阶段被重置；&lt;/li&gt;
&lt;li&gt;CPA/CPAMP 服务继续正常运行；&lt;/li&gt;
&lt;li&gt;没有因为排查而修改历史数据或服务器端密钥。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这次故障最容易误导人的地方，是错误看起来非常像 SSH 密钥失效：先是连接被关闭，然后又出现公钥拒绝（当时真以为服务器被盗了）。最后才发现真正的问题是虚拟网卡导致的 ip 转发（？）错误，正常很难发现。以后遇到问题得先重启核心了。（下次再遇到再补充，目前还是不知道具体核心发生了什么）&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:网络与架构</category><category>tag:FlClash</category><category>tag:SSH</category><category>tag:TUN</category><category>tag:Fake-IP</category><category>tag:网络排查</category></item><item><title>如何使用好Codex等ai工具</title><link>https://youki.fun/ko/post/codex-best-practices</link><guid isPermaLink="false">ko:codex-best-practices</guid><description>面向有编程基础的 Codex 用户，介绍上下文、需求研究、Goal、AGENTS.md、Subagent、会话管理和 Review 的实用方法。</description><pubDate>Sat, 01 Aug 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在我最开始使用 Codex 等 ai 工具时，最先研究的是 Prompt。&lt;/p&gt;
&lt;p&gt;应该怎么描述需求？要不要指定角色？要不要让模型先思考？要不要写一份很长的系统提示词？&lt;/p&gt;
&lt;p&gt;这些问题当然都有意义，但它们很容易把注意力带到一个不太重要的地方：我们开始研究如何控制模型，却忘了先告诉模型我们在做什么。&lt;/p&gt;
&lt;p&gt;一项真实的软件任务，通常不只是“写一个新的组件”这么简单。它可能涉及旧接口、历史数据、部署环境、测试习惯、兼容性要求，以及一些没有写进文档、但团队默认不能碰的东西。&lt;/p&gt;
&lt;p&gt;Codex 可以很快写出一段看起来合理的代码，但它不一定知道当前项目的真实边界，也不一定知道什么结果才算完成。&lt;/p&gt;
&lt;p&gt;所以，使用 Codex 最重要的能力我觉得应该是把任务变成一个可以被理解、执行和验收的工作。&lt;/p&gt;
&lt;h2&gt;给上下文，而不是堆 Prompt&lt;a href=&quot;#给上下文而不是堆-prompt&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenAI 当前的 Codex 指南把一个有效任务拆成了几类信息：目标、上下文、约束和完成标准。[1]&lt;/p&gt;
&lt;p&gt;这四类信息比“请你扮演一名资深工程师”更重要。&lt;/p&gt;
&lt;p&gt;一个简单的任务可以这样写：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;目标：修复邮箱登录失败的问题。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;上下文：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 后端入口位于 backend/auth/；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 错误发生在 POST /api/login；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 手机号登录目前正常；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 相关测试位于 backend/tests/auth/。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;约束：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 保持现有 API 响应格式；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 不更换数据库；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 不删除或跳过现有测试；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 不修改与登录无关的页面。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;完成标准：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 正确邮箱和密码可以登录；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 错误密码返回 401；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 手机号登录测试仍然通过；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 完整后端测试套件通过。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这段文字没有什么复杂技巧，但它告诉了 Codex 四件关键的事情：想改变什么，当前实现是什么，哪些东西不能动，以及如何证明任务完成。（其实现在也没必要这么复杂，能力越强的模型对于这些状态的感知越强）&lt;/p&gt;
&lt;p&gt;如果任务涉及陌生代码库，不应该一开始就要求 Codex 修改。可以先让它只读调查：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;先不要修改文件。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;请检查与这个问题有关的入口、配置、依赖、测试和复现步骤，并输出：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. 已确认的事实；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. 你的假设；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. 相关文件；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. 可能原因；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. 仍然无法从代码中确定的问题；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6. 建议的下一步。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;可以从代码、配置或测试中确认的信息，不要询问我。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Codex 最容易出错的地方，是在错误的上下文里快速写出了局部最优的方案，但是却可能没有真正解决问题。&lt;/p&gt;
&lt;h2&gt;新模型不缺少规划能力&lt;a href=&quot;#新模型不缺少规划能力&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;现在的 Codex 已经可以自己阅读代码、选择工具、安排多个步骤，并根据测试结果继续推进。把每一条命令和中间动作都提前写死，反而可能限制模型寻找更好路径的能力。&lt;/p&gt;
&lt;p&gt;更适合当前 Agent 工作流的原则是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;少写操作指令，多提供工作现场；少规定路径，多定义结果边界。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;真正需要人类确认的，应该集中在任务范围、兼容性要求、外部副作用和验收标准。具体拆成几个阶段、先读哪一个文件、是否调用工具，尽量留给模型判断。&lt;/p&gt;
&lt;h2&gt;先让 Codex 研究，再生成 Goal&lt;a href=&quot;#先让-codex-研究再生成-goal&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;当任务本身还不清楚时，直接写 &lt;code&gt;/goal&lt;/code&gt; 可能会把错误理解固定成持续目标。&lt;/p&gt;
&lt;p&gt;因为 Goal 会成为 Codex 持续工作的目标和完成标准。如果一开始写的是“把这个模块优化一下”，模型就很难知道什么叫优化完成。&lt;/p&gt;
&lt;p&gt;一个很实用的社区工作流是：先让 Codex 做 requirements gathering 和 research，不要急着人工填写 Goal；等它了解代码、配置和测试后，再让它生成并设置合适的 Goal。这样目标建立在项目事实和真实约束上，通常比一开始凭猜测写目标更可靠。[8]&lt;/p&gt;
&lt;p&gt;更稳妥的方式，是先让 Codex 进入 Plan mode，调查代码并采访你：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/plan&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;我有一个可能不完整的需求，请先不要修改文件。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;请先阅读相关代码、配置和测试，了解当前实现。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;然后通过提问澄清目标、范围、约束和验收标准。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;要求：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 只询问真正会影响实现的问题；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 可以从代码中确认的信息不要问我；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 区分已确认事实、假设和待决定事项；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 最后生成一段可以直接用于 /goal 的 Goal；&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 在我确认之前不要开始实现。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;等 Codex 完成调查后，可以让它整理最终目标：&lt;/p&gt;
&lt;p&gt;确认没有问题后，让它输出一段包含 Outcome、Scope、Constraints 和 Verification 的最终目标，并执行 goal（在我自己实践中，发现全让 ai 托管可能会走偏，还是要时不时看一下的）&lt;/p&gt;
&lt;p&gt;大型重构或跨多个模块的项目，则可以把执行计划保存为 &lt;code&gt;PLANS.md&lt;/code&gt; 或 ExecPlan。这样计划不再只是聊天中的一次性内容，而是一个可以随着技术决策不断更新的项目文档。[3]&lt;/p&gt;
&lt;p&gt;官方当前文档也建议，在结果还不明确时，先让 Codex 通过提问识别约束，再把结果变成带有可验证完成标准的 Goal。[2] Goal 不需要写成完整技术设计，只要说明最终结果和判断完成的证据即可。&lt;/p&gt;
&lt;h2&gt;给模型路径自由，但保留必要边界&lt;a href=&quot;#给模型路径自由但保留必要边界&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;新模型拥有更强的自主性，并不意味着人类应该放弃边界。&lt;/p&gt;
&lt;p&gt;真正值得保留的边界通常只有几类：不要扩大范围、不要破坏兼容性、不要通过跳过测试制造成功，不要未经确认执行数据删除或生产发布，发现现实约束冲突时暂停询问。&lt;/p&gt;
&lt;p&gt;这些边界描述的是交付责任，而不是限制模型思考路径。具体阶段、工具和实现顺序，如果不涉及风险，就不必提前写死。&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; 是长期上下文&lt;a href=&quot;#agentsmd-是长期上下文&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果每次都要告诉 Codex 项目如何启动、测试如何运行、哪些目录不能修改，那么这些信息就不应该一直停留在聊天记录里。&lt;/p&gt;
&lt;p&gt;它们应该进入 &lt;code&gt;AGENTS.md&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; 适合保存仓库布局、启动和测试命令、工程约定、限制条件，以及“什么叫完成”。&lt;/p&gt;
&lt;p&gt;它还支持按范围分层。全局默认使用 &lt;code&gt;&amp;lt;CODEX_HOME&amp;gt;/AGENTS.md&lt;/code&gt;，默认位置是 &lt;code&gt;~/.codex&lt;/code&gt;；仓库根目录可以存放团队规范；子目录可以存放某个服务或模块的本地规则。[4]&lt;/p&gt;
&lt;p&gt;当前 Codex 会从项目根目录向当前工作目录逐级发现指导文件，并按从上到下的顺序拼接。更靠近当前目录的规则出现在后面，因此通常具有更高优先级。全局范围还可以使用 &lt;code&gt;AGENTS.override.md&lt;/code&gt;，项目和子目录同样支持 override 文件。&lt;/p&gt;
&lt;p&gt;每个目录最多纳入一个指导文件，默认项目指导内容总上限为 32 KiB；如果规则太多，就应该拆到更具体的子目录。&lt;/p&gt;
&lt;p&gt;推荐先使用&lt;code&gt;/init&lt;/code&gt; 生成初始模板，生成后人工再调整。&lt;/p&gt;
&lt;h2&gt;Subagent 不是免费的加速按钮&lt;a href=&quot;#subagent-不是免费的加速按钮&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Subagent 更像是用额外 Token 换取并行等待时间。&lt;/p&gt;
&lt;p&gt;每个 Subagent 都会独立运行模型和工具调用，因此并行工作流通常比单 Agent 消耗更多 Token。[5]&lt;/p&gt;
&lt;p&gt;代码探索、测试检查、日志分析和资料整理都是比较适合并行的读密集型任务（read-heavy tasks）。写密集型任务（write-heavy tasks）则要谨慎，因为多个 Agent 同时修改代码会增加冲突和协调成本；范围明确的局部修改，留在主线程里通常更划算。&lt;/p&gt;
&lt;p&gt;可以把判断标准写成一句话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果任务的主要成本是等待不同信息被读出来，可以考虑并行；如果任务的主要成本是协调多个修改，就先不要为了并行而并行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;官方文档还用“上下文污染”（Context pollution）描述中间输出淹没关键决策，用“上下文腐化”（Context rot）描述无关内容变多后任务表现下降。[5] Subagent 的价值之一，就是把这些噪音留在其他线程，只把总结带回主线程。&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;/compact&lt;/code&gt; 和 &lt;code&gt;/fork&lt;/code&gt;&lt;a href=&quot;#compact-和-fork&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/compact&lt;/code&gt;会把早期对话压缩成摘要，释放上下文空间，同时保留关键细节。[6] 实践中，在一个里程碑完成并验证通过后再压缩，通常比在关键决策尚未确认时压缩更稳。&lt;/p&gt;
&lt;p&gt;如果要探索两条不同的技术路线，可以使用 &lt;code&gt;/fork&lt;/code&gt; 保留主线并创建新的聊天副本。原聊天不会被修改，分叉可以用来验证另一个方案。[6]&lt;/p&gt;
&lt;p&gt;需要注意，&lt;code&gt;/fork&lt;/code&gt; 复制的是聊天上下文，不是自动创建 Git 分支或 worktree。如果两个分叉都要修改代码，还需要额外的工作树隔离，否则两个会话仍然可能修改同一份文件。&lt;/p&gt;
&lt;h2&gt;Review 是另一个阶段&lt;a href=&quot;#review-是另一个阶段&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;让 Codex 检查自己刚写的代码，通常不如让它换一个角色重新检查。实现结束后可以使用 &lt;code&gt;/review&lt;/code&gt;，或者直接发送：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;请以严格 reviewer 的身份检查当前 diff，重点关注回归、边界、错误处理、权限、安全、兼容性、测试遗漏和范围外修改。只报告有具体证据的问题，并给出文件、位置、原因和验证方式。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Review 应该回答哪里可能出错、为什么可能出错，以及如何复现。[7]&lt;/p&gt;
&lt;p&gt;Review 没有发现问题，也只说明在当前上下文和检查范围内没有发现问题。&lt;/p&gt;
&lt;h2&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;使用 Codex 最容易陷入的误区，是把注意力放在“它能不能写出能运行代码”上。更困难的问题是：它是否理解当前项目，知道哪些东西不能改，知道什么结果才算完成，并且会验证自己的修改。&lt;/p&gt;
&lt;p&gt;真正用好 Codex 方法并不是一套神奇 Prompt。我们应该 先给上下文，再定义结果；让模型自由选择路径，但保留范围、权限和外部副作用的边界；用测试、真实冒烟和 Review 验收；把长期经验沉淀进 &lt;code&gt;AGENTS.md&lt;/code&gt; 和 Skill，并且只在并行真正减少等待时使用 Subagent。&lt;/p&gt;
&lt;p&gt;Codex 可以替你写代码，但只有在你把工作现场和完成标准说清楚之后，它才有机会真正替你完成一项工作。&lt;/p&gt;
&lt;h2&gt;参考资料&lt;a href=&quot;#参考资料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本文资料核验于 2026 年 8 月 2 日。&lt;/p&gt;
&lt;p&gt;[1] OpenAI，Codex Best practices：&lt;a href=&quot;https://learn.chatgpt.com/guides/best-practices&quot;&gt;https://learn.chatgpt.com/guides/best-practices&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[2] OpenAI，Long-running work：&lt;a href=&quot;https://learn.chatgpt.com/docs/long-running-work&quot;&gt;https://learn.chatgpt.com/docs/long-running-work&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[3] OpenAI Cookbook，Codex Execution Plans：&lt;a href=&quot;https://developers.openai.com/cookbook/articles/codex_exec_plans&quot;&gt;https://developers.openai.com/cookbook/articles/codex_exec_plans&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[4] OpenAI，Custom instructions with AGENTS.md：&lt;a href=&quot;https://learn.chatgpt.com/docs/agent-configuration/agents-md&quot;&gt;https://learn.chatgpt.com/docs/agent-configuration/agents-md&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[5] OpenAI，Subagents：&lt;a href=&quot;https://learn.chatgpt.com/docs/agent-configuration/subagents&quot;&gt;https://learn.chatgpt.com/docs/agent-configuration/subagents&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[6] OpenAI，Slash commands：&lt;a href=&quot;https://learn.chatgpt.com/docs/reference/slash-commands&quot;&gt;https://learn.chatgpt.com/docs/reference/slash-commands&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[7] OpenAI，Code review：&lt;a href=&quot;https://learn.chatgpt.com/docs/code-review?surface=app&quot;&gt;https://learn.chatgpt.com/docs/code-review?surface=app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[8] reach_vb，Codex tip：&lt;a href=&quot;https://x.com/reach_vb/status/2076813989598662816&quot;&gt;https://x.com/reach_vb/status/2076813989598662816&lt;/a&gt;&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:工具</category><category>tag:Codex</category><category>tag:AI</category></item><item><title>CTF今后到底会变成什么样子——ISCC赛后总结</title><link>https://youki.fun/ko/post/ctf-iscc-2026</link><guid isPermaLink="false">ko:ctf-iscc-2026</guid><description>一个第一次参加 ISCC 的新手，借助 AI 做题、写 WP，并观察 CTF、工具链和赛事组织变化的赛后记录。</description><pubDate>Thu, 30 Jul 2026 16:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;初识 CTF&lt;a href=&quot;#初识-ctf&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;4 月初，我开始知道有 iscc 这个比赛，学校加分多，老师推荐参加。想着既然要参加，那就先查查这个赛事的流程和要点。因为是第一次参加 CTF 这类的比赛，想着做一点准备，就去&lt;a href=&quot;https://hello-ctf.com/&quot;&gt;helloCTF&lt;/a&gt; 上看看这个东西的流程，结果是开幕雷击
&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;开场雷击：AI 学习曲线截图&lt;/figcaption&gt;&lt;/figure&gt;
当时的我感觉已经魔怔了，感觉 ai 无所不能，也不太看得起这些技术（当时装系统刚刚被 ai 代装各种环境震惊到）。可能就在下图这个橙线的附近（笑
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;自信曲线截图&lt;/figcaption&gt;&lt;/figure&gt;
之后也认真看了几天 helloCTF 的每个模块的内容。也和 ai 做了一些题，自己实在是没思路，只能依葫芦画瓢。
其实我是知道一些方向的题目类型的，网上冲浪就见过不少，不过系统的学习总归还是不习惯，题目也完全没心思做，脑子全是 ai 怎么秒掉。所以没几天就全部放弃了。
之后在 b 站搜索时就看见这个传奇的&lt;a href=&quot;https://www.bilibili.com/video/BV1SfERzKEHv&quot;&gt;视频&lt;/a&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;B站视频截图&lt;/figcaption&gt;&lt;/figure&gt;
对于这个比赛的预期也就降低了很多。
到了 5 月 1 日,校赛开始，也是噩梦开始，当时的我还不知道到底面对的是什么恐怖的比赛。
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;Kali 中的 CodeBuddy 安全提示&lt;/figcaption&gt;&lt;/figure&gt;
图为前一天调试 kali，装的是 codebuddy，想着免费国产 ai 凑合一下，因为感觉 agent 在 pwsh 经常出错，且听说 kali 内置了很多工具，所以选的（但是后来处理 word 给我带来了很多麻烦&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;ai 挺能做题&lt;a href=&quot;#ai-挺能做题&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;校赛开始，首先就是平台进不去，卡住了大部分人，当我 ai 做出 1 题时，看见校榜有人只差 2 题了，开始感觉有点难绷，想着大家都这么强。&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;AI 长时间卡住的状态&lt;/figcaption&gt;&lt;/figure&gt;
被 ai 逼疯中&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;校赛题目列表&lt;/figcaption&gt;&lt;/figure&gt;
在下午之前也是靠 ai 顺利完成大部分题，misc 太吃脑电波了，ai 解不动，最后是靠网络的 wp 解的（小众变态比赛特有的渠道）&lt;p&gt;&lt;/p&gt;
&lt;p&gt;5 月 2 日，官方群里也是充满了快活的气氛，我也渐渐感觉这个比赛也不是很权威了
&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;校赛群聊截图一&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;校赛群聊截图二&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;校赛群聊截图三&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;群聊整活截图&lt;/figcaption&gt;&lt;/figure&gt;
致敬传奇整活王 z 神&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;比赛开始越来越抽象&lt;a href=&quot;#比赛开始越来越抽象&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;5 月 3 日，官方突发恶疾，在没有任何通知的情况下出了一道机器学习的题&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;机器学习题目页面&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;机器学习题规则截图&lt;/figcaption&gt;&lt;/figure&gt;
这个题的评分也十分猎奇， 数据量不太足，数据集里感觉还有噪声，用普通的模型准确度都不太高，但是却只能提交 5 次，越做越低这一块&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;机器学习题提交记录&lt;/figcaption&gt;&lt;/figure&gt;
下午 2 点，突然集体开智&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;机器学习题排行榜变化&lt;/figcaption&gt;&lt;/figure&gt;
排行榜前面的每个人都是全对了&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;机器学习题排行榜截图&lt;/figcaption&gt;&lt;/figure&gt;
群里的焚诀，但是我用了还是没过，也不知道是不是真的
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;群聊中的机器学习解法讨论&lt;/figcaption&gt;&lt;/figure&gt;
群里怨声载道&lt;p&gt;&lt;/p&gt;
&lt;p&gt;下午 4 点，又是突然一个公告&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;机器学习题相关公告&lt;/figcaption&gt;&lt;/figure&gt;
莫名其妙就结束了，浪费大家假期一个下午的时间。&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;校赛群聊截图&lt;/figcaption&gt;&lt;/figure&gt;
5 月 4 日，这个比赛已经初现端倪了，这时我已经逃离学校，美美旅游了（但是这游戏本是真的重，旅行带太降体验了，比赛挑假期这一块）&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;省赛—八仙过海&lt;a href=&quot;#省赛八仙过海&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;省赛 5 月 9 日 8 点开始，但是 15 分钟还是没进&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;省赛题目页面&lt;/figcaption&gt;&lt;/figure&gt;
骗你的，进了也看不见题&lt;p&gt;&lt;/p&gt;
&lt;p&gt;那几天我捣鼓了中转站，准备用 GPT 大战一番，结果：
&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;GPT 拒绝处理 CTF 请求&lt;/figcaption&gt;&lt;/figure&gt;
5.5 太安全了🤣&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;省赛做题统计一&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;省赛做题统计二&lt;/figcaption&gt;&lt;/figure&gt;
这时感觉自己还不赖（GPT 还是强）&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;官方群欢乐多&lt;a href=&quot;#官方群欢乐多&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;官方群聊截图一&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;官方群聊截图二&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;官方群聊截图三&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;官方群聊截图四&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;官方群聊截图五&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;官方群聊截图六&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;这几天虽然没怎么认真做题，但是分数还够看，也开始了 wp 的编写和提交。web 的复现还是比较容易的，我每题都花了 30min 左右，其实自己复现也发现题目也没有很难，只是做的太少，不能第一时间马上想到而已。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;群聊中的 ban 讨论&lt;/figcaption&gt;&lt;/figure&gt;
为后来我被 ban 埋下伏笔（不是）&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;flag 数据异常截图&lt;/figcaption&gt;&lt;/figure&gt;
图为有师傅打到云实例（？）发现 flag 之前没有，但是有 10 多个人已经交成功了&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;IDA 逆向分析截图&lt;/figcaption&gt;&lt;/figure&gt;
汇编真的好难懂 不知道前人是怎么学逆向的    有了 IDA 还是得需要借助 agent 分析。自己找好难&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;题目来源争议公告&lt;/figcaption&gt;&lt;/figure&gt;
草台班子这一块&lt;p&gt;&lt;/p&gt;
&lt;p&gt;5 月 13 日，又出现有一波人提前扫端口扫到题目，一大波秒提交。我已经麻木了😴。从省赛过后，每天都有擂台题，已经整不动了，还加上上课，即使做出来也比其他人慢，往往最后只有 70% 60%的分数了（难绷的分数衰减）&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;临时加题公告&lt;/figcaption&gt;&lt;/figure&gt;
神秘主办方又偷偷加题，且是又没有提前通知，当时真有点受不了了，折磨学生大半个月，每天心惊肉跳。&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;官方群解散公告&lt;/figcaption&gt;&lt;/figure&gt;
之后直接演都不演了，解散了所有的群聊，像我这样单兵作战的直接没了信息来源，各种题的问题只能自己琢磨了，像是进入了黑暗森林。也因为没有客服的通知，我在国赛当天都忘记开始了这回事，排名马上到后面去了。
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;Token 消耗统计&lt;/figcaption&gt;&lt;/figure&gt;
当时的 token 消耗（纪念一下）&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;PWN 服务器遭攻击公告&lt;/figcaption&gt;&lt;/figure&gt;
iscc 笑传之查查爆，这个题从 8 点进不去 一直到下午 15 点 30 才开放，让很多人苦苦等在电脑前面浪费时间（包括我）&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;GPT 能做题，WP 还是得自己写&lt;a href=&quot;#gpt-能做题wp-还是得自己写&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;之后就是痛苦的写 wp 的时间了，因为我只对 web misc 还比较熟悉 其他的几乎是看不懂的状态，各种工具我还是借助 agent 一步一步的学习使用的，所以复现非常的痛苦又耗时，最后直接摆烂 ai 写文档我再小改了，在最后有惊无险的提交了 47 篇 wp。&lt;/p&gt;
&lt;h3&gt;赛后总结？&lt;a href=&quot;#赛后总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;经过漫长的等待，最终结果是我国赛的成绩 ban 了（随机 ban 人名不虚传），只有省三（差 1.. 省二）
这次比赛倒是让我的视野得到了极大的拓展，原来线上 CTF 已经变成科技大赛了，ai 的出现极大的降低了 CTF 的门槛，我这种几乎零基础的新手也可以快速上手各种题目，这也让我的学习的路径变得“ai 原生”。ai 成了不可缺少的一部分，这变相减少了要网上翻各种资料和论坛的麻烦。
但是 ai 始终还是不能替代人类的判断，有几次明明 ai 都快拿到 flag 了，但还在做各种校验，这时只要引导一下 ai，马上就可以得到答案。而我缺少的一些经验以及围绕模型建立的工具链（要不然 ai 会降级或者反复尝试安装导致浪费时间）也是和最快的师傅？拉开差距的一部分。
ai 挖洞其实已经有可行性了（现在已经看见批量挖洞的了）。也弱化了我对于 Linux 的刻板印象，ai 在 Linux 简直是如鱼得水，各种配置都能靠命令搞定，有种一切尽在掌握的感觉，且软件间的壁垒也很弱，很容易实现通信协作。&lt;/p&gt;</content:encoded><category>category:随笔</category><category>tag:CTF</category><category>tag:ISCC</category><category>tag:AI</category><category>tag:网络安全</category></item><item><title>初识 Kubernetes：从 Service 与 CoreDNS 理解服务发现</title><link>https://youki.fun/ko/post/intro-to-k8s</link><guid isPermaLink="false">ko:intro-to-k8s</guid><description>从 Service、CoreDNS 到完整调用链，理解 K8s 服务发现的核心机制与单机部署的本质区别</description><pubDate>Tue, 26 May 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近在 K8s 集群里部署多服务时，最让人意外的是&lt;strong&gt;服务之间居然可以不写 IP、不改代码地互相找到对方&lt;/strong&gt;。这篇笔记拆解这套机制是怎么实现的：Service 如何当稳定的”门牌号”、CoreDNS 怎么当集群内的”黄页”、以及这套设计与单机 &lt;code&gt;localhost&lt;/code&gt; 部署的本质区别。&lt;/p&gt;
&lt;h2&gt;1. 先理清 K8s 的几个基本对象&lt;a href=&quot;#1-先理清-k8s-的几个基本对象&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;讲服务发现之前，先把几个会反复出现的对象过一遍，避免后面混在一起。&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;对象&lt;/th&gt;&lt;th&gt;一句话解释&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Pod&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;K8s 调度的最小单位，里面跑着一个或多个容器。每个 Pod 有自己的 IP，但这个 IP &lt;strong&gt;不稳定&lt;/strong&gt;——重启就换。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;用来声明”我要跑 N 个相同的 Pod”，K8s 会自动维护这个数量（挂了会重启）。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;一组 Pod 的&lt;strong&gt;稳定访问入口&lt;/strong&gt;。给它一个名字 + ClusterIP，后面所有 Pod 都通过这个名字访问它。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;NodePort / Ingress&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;把 Service 暴露到集群外部的两种方式（前者用节点端口，后者用 HTTP 路由）。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;记住一句话：&lt;strong&gt;Pod 是临时的，Service 是稳定的。&lt;/strong&gt; 后面所有机制都围绕这个差异展开。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Service：一个虚拟的、稳定的”门牌号”&lt;a href=&quot;#2-service一个虚拟的稳定的门牌号&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;创建 &lt;code&gt;Service&lt;/code&gt; 时，Kubernetes 会在集群内部为它分配一个&lt;strong&gt;固定的虚拟 IP&lt;/strong&gt;（ClusterIP）。这个 IP 永远不会变——只要 Service 存在，它就在那里。&lt;/p&gt;
&lt;p&gt;假设后端被部署成 3 份 Pod：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;apiVersion&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;apps/v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Deployment&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;metadata&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  replicas&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;   # 3 个相同的 Pod&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  template&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    spec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      containers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;my-backend:1.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;每个 Pod 启动后会被分到自己的随机 IP，比如 &lt;code&gt;10.244.1.5&lt;/code&gt;、&lt;code&gt;10.244.2.3&lt;/code&gt;、&lt;code&gt;10.244.3.8&lt;/code&gt;。如果前端直接写这些 IP，&lt;strong&gt;任何一个 Pod 重启，IP 就变了，前端就找不到了&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;解决办法是再创建一个 &lt;code&gt;Service&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;apiVersion&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;metadata&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  selector&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    app&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;span&gt;         # 选中上面那 3 个 Pod&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - &lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      targetPort&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;8080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;K8s 会做两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;给这个 Service 分一个&lt;strong&gt;固定 ClusterIP&lt;/strong&gt;（比如 &lt;code&gt;10.96.100.1&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;在背后维护一个&lt;strong&gt;负载均衡器&lt;/strong&gt;，把发往 &lt;code&gt;10.96.100.1:80&lt;/code&gt; 的请求，均匀转发到那 3 个 Pod 上。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;于是前端只需要记住 &lt;code&gt;backend:80&lt;/code&gt;（或者 &lt;code&gt;10.96.100.1:80&lt;/code&gt;）这一个地址，就能访问到后端的任意副本。&lt;strong&gt;后端 Pod 增减、重启、IP 变化，前端完全不感知。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. CoreDNS：一个内置的”黄页”&lt;a href=&quot;#3-coredns一个内置的黄页&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;让前端去记 &lt;code&gt;10.96.100.1&lt;/code&gt; 这种纯数字地址不现实。Kubernetes 内建了一个 DNS 服务（CoreDNS），相当于&lt;strong&gt;集群内部的电话本&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建一个叫 &lt;code&gt;backend&lt;/code&gt; 的 Service 后，CoreDNS 会自动注册一条记录：&lt;code&gt;backend&lt;/code&gt; → &lt;code&gt;10.96.100.1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;前端 Pod 里的应用发起 &lt;code&gt;http://backend:80&lt;/code&gt; 请求时，容器里的 DNS 解析器会自动把 &lt;code&gt;backend&lt;/code&gt; 翻译成 &lt;code&gt;10.96.100.1&lt;/code&gt;，流量就精准抵达了后端的 Service。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;前端 Pod 里的应用：GET http://backend:80/api&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   ↓ DNS 解析&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CoreDNS：backend → 10.96.100.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   ↓ 流量转发&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;backend Service (10.96.100.1) → 负载均衡到某个后端 Pod&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;这就是”服务发现”的本质：调用方只需要知道服务名，不需要知道它有几个副本、IP 是什么、跑在哪台机器上。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 一个完整的调用链路&lt;a href=&quot;#4-一个完整的调用链路&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;假设集群里有 3 个服务：数据库、后端、前端。完整通信过程是这样的：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 1. 数据库&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;apiVersion&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;metadata&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;db&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  selector&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    app&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;mysql&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - &lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3306&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 2. 后端（3 副本）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;apiVersion&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;metadata&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  selector&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    app&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - &lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      targetPort&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;8080&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 3. 前端（NodePort 暴露给外部）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;apiVersion&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;metadata&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;frontend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;NodePort&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  selector&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    app&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;frontend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - &lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      nodePort&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;30080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;后端代码里，&lt;strong&gt;数据库连接地址直接写成 &lt;code&gt;db:3306&lt;/code&gt;&lt;/strong&gt;，不用关心 MySQL 的 Pod IP：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 后端代码示例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; mysql.connector&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;conn &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mysql.connector.connect(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    host&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;db&quot;&lt;/span&gt;&lt;span&gt;,       &lt;/span&gt;&lt;span&gt;# 直接用 Service 名&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    port&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;3306&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    user&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;app&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    password&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;xxx&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    database&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;app_db&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;前端代码里，&lt;strong&gt;API 请求地址直接写成 &lt;code&gt;backend:80&lt;/code&gt;&lt;/strong&gt;，也不用关心后端有几个副本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 前端代码示例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;http://backend:80/api/users&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; res.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(data));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用户从浏览器访问 &lt;code&gt;http://Master节点IP:30080&lt;/code&gt; 时，完整的链路是：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;用户浏览器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ↓ http://MasterIP:30080&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;前端 Service (NodePort)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ↓ 负载均衡到某个前端 Pod&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;前端 Pod 发起 GET http://backend:80/api&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ↓ CoreDNS 解析 backend → 10.96.100.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;后端 Service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ↓ 负载均衡到某个后端 Pod&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;后端 Pod 连接 mysql -h db&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ↓ CoreDNS 解析 db → 10.96.x.x&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;数据库 Service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ↓ 连上 MySQL Pod&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;整个过程中，没有一个地方出现了具体的 Pod IP 或节点 IP。&lt;/strong&gt; 服务之间完全通过 Service 名称通信，K8s 自动处理负载均衡、高可用和故障恢复。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 和单机 &lt;code&gt;localhost&lt;/code&gt; 部署的本质区别&lt;a href=&quot;#5-和单机-localhost-部署的本质区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;单机上跑多服务时，连接地址通常是这样写的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 单机部署：写死 localhost&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;conn &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mysql.connector.connect(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    host&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span&gt;# 必须是本机&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    port&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;3306&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要把后端拆到另一台机器上，要改的东西很多：IP 地址、端口、可能还有防火墙、配置中心……&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;维度&lt;/th&gt;&lt;th&gt;单机 &lt;code&gt;localhost&lt;/code&gt; 方式&lt;/th&gt;&lt;th&gt;Kubernetes Service 方式&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;服务地址&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;固定写死 IP/端口&lt;/td&gt;&lt;td&gt;通过虚拟 IP + DNS 动态发现&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;扩展性&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;无法横向扩展（端口冲突）&lt;/td&gt;&lt;td&gt;随时增减副本，前端无感知&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;故障恢复&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;进程挂了就断了&lt;/td&gt;&lt;td&gt;副本自动重新调度，Service 自动转发到健康实例&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;部署灵活性&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;所有服务绑死在同一台机器&lt;/td&gt;&lt;td&gt;服务可以分布在集群的任何节点上&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;代码改动&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;迁移机器要改 IP/配置&lt;/td&gt;&lt;td&gt;一行不用改，Service 名不变即可&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;用一句话总结这个差异：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;单机部署是”我告诉你地址”，Kubernetes 是”告诉我你叫什么，我帮你找到地址”。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;后者让服务之间的通信变得松耦合、高可用，也更符合微服务架构的理念。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 写给自己的小结&lt;a href=&quot;#6-写给自己的小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这次在集群里做多服务部署实验，&lt;strong&gt;最关键的认知升级不是”我会写 yaml 了”，而是理解了”为什么 Pod IP 不重要”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;只要 Service 还在、名字没变，Pod 怎么重建、IP 怎么换、节点怎么调度，对调用方来说都是透明的。这种”调用方不关心被调用方物理位置”的设计，是分布式系统能横向扩展的基石。&lt;/p&gt;
&lt;p&gt;K8s 把这套机制内化到了平台层——&lt;strong&gt;调用方一行代码不用改，平台就把寻址、负载均衡、故障恢复全做了&lt;/strong&gt;。这在单台服务器上根本无法做到：单机部署时，进程地址是写死的、端口是冲突的、机器是绑死的，&lt;strong&gt;这套”靠名字找服务”的能力在单台服务器上根本无从发挥&lt;/strong&gt;。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:工具</category><category>tag:Kubernetes</category></item><item><title>CLI Agent 实践指南：从 Claude Code 到通用方法论</title><link>https://youki.fun/ko/post/cli-practice-guide</link><guid isPermaLink="false">ko:cli-practice-guide</guid><description>以 Claude Code 为主例，对比 Codex CLI / GitHub Copilot CLI，提炼 CLI Agent 编程助手的通用方法：Plan 模式、必学命令、上下文管理、安全权限、实践经验</description><pubDate>Fri, 24 Apr 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这一两年里 CLI Agent 编程助手换了好几轮——从 Claude Code 到 Codex ，中间还试过 Copilot CLI——但用久之后发现，三家的”骨架”几乎一样：plan 模式、文件注入、shell、压缩、恢复、回退、模型切换、权限管控。基本是通用的，差异只在命令名。&lt;/p&gt;
&lt;p&gt;这篇以 Claude Code 为主线，对照 Codex CLI 和 GitHub Copilot CLI，把用得上的方法整理成 5 节：Plan 模式、命令、上下文管理、安全权限、实践经验。读完之后应该能在任何一个 CLI Agent 上快速上手。&lt;/p&gt;
&lt;h2&gt;0. 三家命令速查&lt;a href=&quot;#0-三家命令速查&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;先把骨架放在一起。下面的表格在三家之间通用，命令名差异不大。&lt;/p&gt;











































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;能力&lt;/th&gt;&lt;th&gt;Claude Code&lt;/th&gt;&lt;th&gt;Codex CLI&lt;/th&gt;&lt;th&gt;Copilot CLI&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;进入 Plan 模式&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shift+Tab&lt;/code&gt; → &lt;code&gt;plan&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shift+Tab&lt;/code&gt; → &lt;code&gt;Read-only&lt;/code&gt;（Approval mode）&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shift+Tab&lt;/code&gt; 或 &lt;code&gt;/plan &amp;lt;描述&amp;gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;外置编辑器写计划&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Ctrl+G&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Ctrl+G&lt;/code&gt;（&lt;code&gt;$EDITOR&lt;/code&gt;/&lt;code&gt;$VISUAL&lt;/code&gt;）&lt;/td&gt;&lt;td&gt;&lt;code&gt;Ctrl+Y&lt;/code&gt;（Markdown 编辑器）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;注入文件&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;@文件路径&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;@&lt;/code&gt; + &lt;code&gt;Tab&lt;/code&gt; 模糊搜索&lt;/td&gt;&lt;td&gt;&lt;code&gt;@路径&lt;/code&gt; 或 &lt;code&gt;@图片.png&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;会话内跑 shell&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;!命令&lt;/code&gt; + &lt;code&gt;Ctrl+B&lt;/code&gt; 后台&lt;/td&gt;&lt;td&gt;&lt;code&gt;!命令&lt;/code&gt;（受 Approval mode 约束）&lt;/td&gt;&lt;td&gt;通过 &lt;code&gt;shell(...)&lt;/code&gt; 权限调用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;清空上下文&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/clear&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/clear&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/clear&lt;/code&gt; 或 &lt;code&gt;/new&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;侧边问（不污染主历史）&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/btw&lt;/code&gt;&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;摘要压缩&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/compact [focus...]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;API 层自动&lt;/td&gt;&lt;td&gt;&lt;code&gt;/compact&lt;/code&gt;（手动）/ 默认自动&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;恢复会话&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/resume&lt;/code&gt;、&lt;code&gt;claude -c&lt;/code&gt;、&lt;code&gt;claude -r&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;codex resume&lt;/code&gt; / &lt;code&gt;codex resume --last&lt;/code&gt; / &lt;code&gt;codex resume &amp;lt;id&amp;gt;&lt;/code&gt; / &lt;code&gt;codex exec resume&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/session&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;回退 checkpoint&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/rewind&lt;/code&gt; + &lt;code&gt;Esc+Esc&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Esc&lt;/code&gt;×2 编辑上一条消息（=fork 入口）&lt;/td&gt;&lt;td&gt;&lt;code&gt;/session checkpoints&lt;/code&gt; + &lt;code&gt;双 Esc&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;切换模型&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/model&lt;/code&gt; + &lt;code&gt;Alt+P&lt;/code&gt; + &lt;code&gt;--effort&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/model&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/model&lt;/code&gt;（含 Auto / Opus 4.5 / Sonnet 4.5 / GPT-5.2 Codex）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;看上下文用量&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/statusline&lt;/code&gt; 自定义&lt;/td&gt;&lt;td&gt;&lt;code&gt;/status&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/context&lt;/code&gt; + &lt;code&gt;/session files&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;权限管理&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/permissions&lt;/code&gt; + &lt;code&gt;settings.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/permissions&lt;/code&gt; 切 Approval mode + &lt;code&gt;config.toml&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;--allow-tool/--deny-tool/--available-tools&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;跳过所有权限&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;--yolo&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;--yolo&lt;/code&gt; / &lt;code&gt;--allow-all&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;多仓库/多目录&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;--add-dir&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;codex --cd&lt;/code&gt; + &lt;code&gt;--add-dir&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/add-dir&lt;/code&gt; + &lt;code&gt;/list-dirs&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;并行子代理&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;claude agents&lt;/code&gt; + agent teams&lt;/td&gt;&lt;td&gt;&lt;code&gt;codex agents&lt;/code&gt; + &lt;code&gt;/fork&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;/fleet&lt;/code&gt; + &lt;code&gt;/task&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;会话存档位置&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.claude/projects/&amp;lt;project&amp;gt;/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.codex/sessions/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.copilot/session-state/{id}/&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;后面所有方法论都围绕这张表展开。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Plan 模式：所有工作的起点&lt;a href=&quot;#1-plan-模式所有工作的起点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1.1 决策树：能用一句话描述 diff 吗？&lt;a href=&quot;#11-决策树能用一句话描述-diff-吗&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;能&lt;/strong&gt; → 不用进 Plan，直接让 Agent 改。例：修 typo、加日志、改变量名、调整一个 import。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不能&lt;/strong&gt;（涉及多文件、跨模块、不熟悉的代码、不确定的方案）→ &lt;strong&gt;必须进 Plan 模式&lt;/strong&gt;让 Agent 先只读、不写。&lt;/p&gt;
&lt;p&gt;Plan 模式的核心价值是&lt;strong&gt;把”问题理解”和”动手”在时间上分开&lt;/strong&gt;——这两件事分得越开，返工率越低。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Shift+Tab&lt;/span&gt;&lt;span&gt;  # 在 default / acceptEdits / plan / auto / dontAsk / bypassPermissions 之间循环&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Codex&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Shift+Tab&lt;/span&gt;&lt;span&gt;  # 在 Read-only / Auto / Full Access 之间循环，选 Read-only = Plan 模式&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Copilot&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Shift+Tab&lt;/span&gt;&lt;span&gt;  # 或 /plan Implement password reset flow with email link&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.2 Plan 阶段的 3 个高阶玩法&lt;a href=&quot;#12-plan-阶段的-3-个高阶玩法&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;1）用外置编辑器把计划当合同&lt;a href=&quot;#1用外置编辑器把计划当合同&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;三家都能在 Plan 阶段用外置编辑器写计划。意义在于：把”硬约束”直接写进计划文件里，让计划成为可审查的合同。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# Claude Code / Codex&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Ctrl+G&lt;/span&gt;&lt;span&gt;  # 打开 $EDITOR 编辑当前 plan&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Copilot&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Ctrl+Y&lt;/span&gt;&lt;span&gt;  # 在 Markdown 编辑器中打开 plan.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;典型约束写法：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;## Plan: 实现密码重置流程&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 涉及文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/api/auth.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/services/email.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; tests/auth/test_reset.py&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 约束（必须遵守）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 保留向后兼容（旧的 /login 流程不能动）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 先写测试再写实现&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 不要改数据库 schema&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 使用项目里现成的 token-manager.ts，不要引入 passport.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 验证&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; `pytest tests/auth/ -k reset`&lt;/span&gt;&lt;span&gt; 全部通过&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; `mypy src/auth/`&lt;/span&gt;&lt;span&gt; 无错误&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;写完之后保存退出，Agent 会按”合同”实现。&lt;/p&gt;
&lt;h4&gt;2）计划里显式列验证步骤&lt;a href=&quot;#2计划里显式列验证步骤&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;没写验收标准的计划 = 100% 会变成”看起来做完了”。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;### 验证&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; Run &lt;/span&gt;&lt;span&gt;`pytest tests/auth/ -k reset`&lt;/span&gt;&lt;span&gt; and ensure all 3 new cases pass.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; Run &lt;/span&gt;&lt;span&gt;`mypy src/auth/`&lt;/span&gt;&lt;span&gt; to confirm no type errors.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; Open a PR with title &quot;feat(auth): password reset flow&quot;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agent 擅长”对照 plan 自检”——前提是把验证步骤写进去。&lt;/p&gt;
&lt;h3&gt;1.3 三家 Plan 命令对照&lt;a href=&quot;#13-三家-plan-命令对照&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;步骤&lt;/th&gt;&lt;th&gt;Claude Code&lt;/th&gt;&lt;th&gt;Codex&lt;/th&gt;&lt;th&gt;Copilot&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;进 Plan&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shift+Tab&lt;/code&gt; → &lt;code&gt;plan&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shift+Tab&lt;/code&gt; → &lt;code&gt;Read-only&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shift+Tab&lt;/code&gt; 或 &lt;code&gt;/plan &amp;lt;描述&amp;gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;写计划&lt;/td&gt;&lt;td&gt;&lt;code&gt;Ctrl+G&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Ctrl+G&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Ctrl+Y&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;让 Agent 继续&lt;/td&gt;&lt;td&gt;”Proceed with the plan&quot;&lt;/td&gt;&lt;td&gt;&quot;proceed” / “go&quot;&lt;/td&gt;&lt;td&gt;&quot;Proceed with the plan”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;中止 Plan&lt;/td&gt;&lt;td&gt;再按 &lt;code&gt;Shift+Tab&lt;/code&gt; 退出&lt;/td&gt;&lt;td&gt;切到 &lt;code&gt;Auto&lt;/code&gt; 或 &lt;code&gt;Full Access&lt;/code&gt;&lt;/td&gt;&lt;td&gt;再按 &lt;code&gt;Shift+Tab&lt;/code&gt; 退出&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 需要掌握的 7 个命令&lt;a href=&quot;#2-需要掌握的-7-个命令&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;按使用频率排序。这 7 个命令覆盖 90% 的日常使用，其余命令是”知道有就行，需要时查 &lt;code&gt;/help&lt;/code&gt;”。&lt;/p&gt;
&lt;h3&gt;2.1 &lt;code&gt;@文件&lt;/code&gt; / &lt;code&gt;@路径/&lt;/code&gt; — 精准注入上下文&lt;a href=&quot;#21-文件--路径--精准注入上下文&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@src/api/auth.ts&lt;/span&gt;&lt;span&gt;       # 单文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@src/api/auth/&lt;/span&gt;&lt;span&gt;         # 整个目录&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@../docs/spec.md&lt;/span&gt;&lt;span&gt;       # 项目外的文件&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以直接拖进 cli，都支持得不错&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心原则：让 Agent 按需读相关部分。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CLAUDE.md / AGENTS.md 里也支持 &lt;code&gt;@docs/git-instructions.md&lt;/code&gt; 语法引用其他文件，最多 4 层引用。三家都有这个特性，行为一致。&lt;/p&gt;
&lt;h3&gt;2.2 &lt;code&gt;!命令&lt;/code&gt; — 会话内跑 shell&lt;a href=&quot;#22-命令--会话内跑-shell&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; npm&lt;/span&gt;&lt;span&gt; test&lt;/span&gt;&lt;span&gt;              # Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; git&lt;/span&gt;&lt;span&gt; status&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; ls&lt;/span&gt;&lt;span&gt; -la&lt;/span&gt;&lt;span&gt; src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 进阶&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; long-running-task&lt;/span&gt;&lt;span&gt;     # 长任务&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Ctrl+B&lt;/span&gt;&lt;span&gt;                  # 转后台（Claude Code 特有），不阻塞主对话&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;上下文行为&lt;/strong&gt;：输出会回到主对话——只在确实需要 Agent 看到结果时用；纯查看用 &lt;code&gt;!&lt;/code&gt; 更顺手。&lt;/li&gt;
&lt;li&gt;粘贴以 &lt;code&gt;!&lt;/code&gt; 开头的内容会自动进入 shell 模式（Claude Code 行为）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Codex 和 Copilot 都有 shell 工具，能用 &lt;code&gt;!git diff&lt;/code&gt;、&lt;code&gt;!cat file&lt;/code&gt; 直接验证。&lt;/p&gt;
&lt;h3&gt;2.3 &lt;code&gt;/clear&lt;/code&gt; — 切任务时清空上下文&lt;a href=&quot;#23-clear--切任务时清空上下文&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/clear&lt;/span&gt;&lt;span&gt;                  # 清空 + 当前对话进 /resume 选择器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/clear&lt;/span&gt;&lt;span&gt; feature-auth-work&lt;/span&gt;&lt;span&gt;   # 给被清掉的对话打标签（Claude Code 特有）&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;使用节奏：每完成一个独立任务 → &lt;code&gt;/clear&lt;/code&gt;。一次会话只做一件事。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;官方原话警告过一种反模式——“kitchen sink session”：一个会话混入不相关任务，等要清理 context 时已经一团糟。&lt;/p&gt;
&lt;h3&gt;2.4 &lt;code&gt;/compact [focus...]&lt;/code&gt; — 长会话摘要压缩&lt;a href=&quot;#24-compact-focus--长会话摘要压缩&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/compact&lt;/span&gt;&lt;span&gt;                            # 压缩整个对话&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/compact&lt;/span&gt;&lt;span&gt; focus&lt;/span&gt;&lt;span&gt; on&lt;/span&gt;&lt;span&gt; the&lt;/span&gt;&lt;span&gt; API&lt;/span&gt;&lt;span&gt; design&lt;/span&gt;&lt;span&gt;    # 带方向压缩&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;关键事实&lt;/strong&gt;（Claude Code 文档原话）：“Project-root CLAUDE.md survives compaction: after &lt;code&gt;/compact&lt;/code&gt;, Claude re-reads it from disk and re-inject it.” —— 项目根的 &lt;code&gt;CLAUDE.md&lt;/code&gt; &lt;strong&gt;不会&lt;/strong&gt;因压缩丢失；但&lt;strong&gt;子目录的&lt;/strong&gt; CLAUDE.md &lt;strong&gt;会&lt;/strong&gt;丢失，直到下次读到该目录才重新加载。&lt;/p&gt;
&lt;p&gt;何时用 &lt;code&gt;/compact&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对话长了但任务没完 → &lt;code&gt;/compact focus on X&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;同一任务上&lt;strong&gt;修 Agent 同一错误超过 2 次&lt;/strong&gt; → 立刻 &lt;code&gt;/clear&lt;/code&gt;（用更好的 prompt 重开，不要继续累积修正）。&lt;/li&gt;
&lt;li&gt;Codex 隐式做了这件事，没有显式 &lt;code&gt;/compact&lt;/code&gt;，但可以 &lt;code&gt;/fork&lt;/code&gt; 派生新会话。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.5 &lt;code&gt;/resume&lt;/code&gt; — 恢复历史会话&lt;a href=&quot;#25-resume--恢复历史会话&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/resume&lt;/span&gt;&lt;span&gt;                       # 打开选择器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/resume&lt;/span&gt;&lt;span&gt; feature-auth&lt;/span&gt;&lt;span&gt;          # 按名恢复&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;claude&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;span&gt;                     # 续当前目录下最近对话&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;claude&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;                # 按 ID 恢复&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Codex&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;codex&lt;/span&gt;&lt;span&gt; resume&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;codex&lt;/span&gt;&lt;span&gt; resume&lt;/span&gt;&lt;span&gt; --last&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;codex&lt;/span&gt;&lt;span&gt; resume&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Copilot&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/resume&lt;/span&gt;&lt;span&gt;                       # 打开 ~/.copilot/session-state/ 选择器&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;配合 git worktree 的高阶用法&lt;/strong&gt;：每个 worktree 一个 &lt;code&gt;codex resume&lt;/code&gt; / &lt;code&gt;claude -r &amp;lt;id&amp;gt;&lt;/code&gt; 会话。切分支 = 切会话，context 完全隔离。&lt;/p&gt;
&lt;h3&gt;2.6 &lt;code&gt;/rewind&lt;/code&gt; / &lt;code&gt;/session&lt;/code&gt; — 回退到 checkpoint&lt;a href=&quot;#26-rewind--session--回退到-checkpoint&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/rewind&lt;/span&gt;&lt;span&gt;          # 弹出检查点选择器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Esc+Esc&lt;/span&gt;&lt;span&gt;          # 空输入时弹 rewind 菜单（含 &quot;Summarize from here&quot; / &quot;Summarize up to here&quot; 选项）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Copilot&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/session&lt;/span&gt;&lt;span&gt;                  # 查看当前会话信息&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/session&lt;/span&gt;&lt;span&gt; checkpoints&lt;/span&gt;&lt;span&gt;      # 列出所有检查点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/session&lt;/span&gt;&lt;span&gt; checkpoints&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt;    # 查看第 3 个检查点内容&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/session&lt;/span&gt;&lt;span&gt; files&lt;/span&gt;&lt;span&gt;            # 列出当前会话临时文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;双&lt;/span&gt;&lt;span&gt; Esc&lt;/span&gt;&lt;span&gt;                     # 在 composer 为空时打开 rewind picker&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Codex&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt; ×&lt;/span&gt;&lt;span&gt; 2（composer&lt;/span&gt;&lt;span&gt; 为空时）&lt;/span&gt;&lt;span&gt;  # **编辑上一条用户消息** —— 这也是 fork / 分叉的入口&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Copilot 警告&lt;/strong&gt;：“Rewinding cannot be undone. Once you roll back to a snapshot, all snapshots and session history after that point are permanently removed.” —— 回退是&lt;strong&gt;不可逆&lt;/strong&gt;的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：&lt;code&gt;/rewind&lt;/code&gt; 会&lt;strong&gt;同时回退 Agent 改的、手动改的、shell 命令产生的所有变更&lt;/strong&gt;，包括之后新增的文件。回退前先 &lt;code&gt;git status&lt;/code&gt; 确认工作区状态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Codex 关键差异&lt;/strong&gt;：Codex 没有 &lt;code&gt;/rewind&lt;/code&gt; 概念，而是把”分叉”和”回退”合在一起——&lt;code&gt;Esc&lt;/code&gt;×2 编辑上一条消息后，从那个时间点分叉新会话；不满意就丢，老会话仍在 &lt;code&gt;/resume&lt;/code&gt; 里。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2.7 &lt;code&gt;/model&lt;/code&gt; + &lt;code&gt;--effort&lt;/code&gt; — 切模型与思考强度&lt;a href=&quot;#27-model----effort--切模型与思考强度&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/model&lt;/span&gt;&lt;span&gt;                # 打开选择器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Alt+P&lt;/span&gt;&lt;span&gt;                 # 切换模型不丢失当前 prompt（更快的快捷键）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--effort&lt;/span&gt;&lt;span&gt; low/medium/high/xhigh/max&lt;/span&gt;&lt;span&gt;    # 调节思考强度&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Codex&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/model&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;codex&lt;/span&gt;&lt;span&gt; --model&lt;/span&gt;&lt;span&gt; gpt-5.5&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Copilot&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/model&lt;/span&gt;&lt;span&gt;                # 含 Auto / Opus 4.5 / Sonnet 4.5 / GPT-5.2 Codex&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;核心技巧&lt;/strong&gt;：让主模型去调度 subagent，subagent 可以指定使用不同的模型，节约用量&lt;/p&gt;
&lt;h3&gt;2.8 锦上添花&lt;a href=&quot;#28-锦上添花&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;命令&lt;/th&gt;&lt;th&gt;哪家&lt;/th&gt;&lt;th&gt;用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/btw&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;&lt;strong&gt;侧边问题&lt;/strong&gt;，答案不进入主对话历史；纯提问用，访问不到工具——完美的”等下，X 那个 Y 是否考虑过”场景&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/statusline&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;自定义状态栏持续追踪上下文用量&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/goal&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;跨会话条件门控（评估器每轮重新检查）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/diff&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;交互式 diff 查看器，左右键切”当前 git diff”和”单 turn diff”，审 PR 前必看&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/code-review&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;内置代码评审（在全新子代理中评审当前 diff）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/recap&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;按需生成会话摘要（默认自动每 3 turn 触发）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/delegate&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Copilot&lt;/td&gt;&lt;td&gt;把当前会话转给 &lt;strong&gt;GitHub 云端 Copilot&lt;/strong&gt;，云端代理会创建 PR&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/fleet&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Copilot&lt;/td&gt;&lt;td&gt;启动&lt;strong&gt;并行子代理&lt;/strong&gt;加速大任务&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/task&lt;/code&gt; / &lt;code&gt;/review&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Copilot&lt;/td&gt;&lt;td&gt;显式触发子代理（review 有 4 种预设：base branch / uncommitted / commit / custom）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;claude -p &quot;...&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;&lt;strong&gt;非交互模式&lt;/strong&gt;，配 &lt;code&gt;--output-format json&lt;/code&gt; / &lt;code&gt;--output-format stream-json --verbose&lt;/code&gt; 给 CI、pre-commit、脚本用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;codex exec &quot;...&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Codex&lt;/td&gt;&lt;td&gt;非交互执行；&lt;code&gt;codex exec resume --last &quot;Fix race&quot;&lt;/code&gt; 续上次&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;codex cloud exec --attempts 3 &quot;...&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Codex&lt;/td&gt;&lt;td&gt;&lt;strong&gt;云端 best-of-N&lt;/strong&gt;（1-4 次并发尝试）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;codex features enable unified_exec&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Codex&lt;/td&gt;&lt;td&gt;启用新功能（如 &lt;code&gt;unified_exec&lt;/code&gt; 执行模式、&lt;code&gt;shell_snapshot&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;codex app-server --listen ws://...&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Codex&lt;/td&gt;&lt;td&gt;启动 App Server，远程 TUI 通过 WebSocket 连接&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;claude agents&lt;/code&gt; / &lt;code&gt;codex agents&lt;/code&gt;&lt;/td&gt;&lt;td&gt;三家&lt;/td&gt;&lt;td&gt;打开多代理视图，监控并行后台会话&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;claude -c&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;续当前目录下最近对话（最快的工作流快捷方式）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/add-dir&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Copilot / Codex&lt;/td&gt;&lt;td&gt;多仓库/多目录工作流，加额外目录进 Agent 可访问范围&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 上下文管理：context window&lt;a href=&quot;#3-上下文管理context-window&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Anthropic 官方：“Most best practices are based on one constraint: Claude’s context window fills up fast, and performance degrades as it fills.”&lt;/p&gt;
&lt;p&gt;所有”上下文最佳实践”都围绕”别浪费它”展开。&lt;/p&gt;
&lt;h3&gt;3.1 4 种”喂”上下文的姿势&lt;a href=&quot;#31-4-种喂上下文的姿势&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;从高到低：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;@文件路径&lt;/code&gt;&lt;/strong&gt;（最优）—— 引用文件，Agent 按需读相关部分。&lt;strong&gt;Claude Code 与 Copilot&lt;/strong&gt; 支持 &lt;code&gt;@path/to/file&lt;/code&gt; 完整路径；&lt;strong&gt;Codex&lt;/strong&gt; 的 &lt;code&gt;@&lt;/code&gt; 触发的是&lt;strong&gt;模糊文件搜索&lt;/strong&gt;（&lt;code&gt;@&lt;/code&gt; + &lt;code&gt;Tab&lt;/code&gt; 选中），再 &lt;code&gt;Enter&lt;/code&gt; 填入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;粘贴图片/截图/错误堆栈&lt;/strong&gt; —— OCR 不必，人类也读图。三家都支持&lt;strong&gt;拖放图片&lt;/strong&gt;到 CLI 输入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cat error.log | claude&lt;/code&gt;&lt;/strong&gt;（管道输入） —— 适合一次性文本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;整文件复制&lt;/strong&gt;（最差）—— 1000 行代码塞进 prompt，挤占 context。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Claude Code 特有技巧&lt;/strong&gt;：&lt;code&gt;/permissions&lt;/code&gt; 把常用文档域名（&lt;code&gt;docs.example.com&lt;/code&gt;）加白名单，让 Agent 自己去拉。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;通用技巧&lt;/strong&gt;：&lt;code&gt;gh&lt;/code&gt; CLI 是 GitHub 上下文最高效的获取方式——Anthropic 文档原话：“Without &lt;code&gt;gh&lt;/code&gt;, Claude can still use the GitHub API, but unauthenticated requests often hit rate limits.”&lt;/p&gt;
&lt;h3&gt;3.2 3 个清理层次&lt;a href=&quot;#32-3-个清理层次&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;触发场景&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;th&gt;效果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Agent 走偏了一点&lt;/td&gt;&lt;td&gt;&lt;code&gt;Esc+Esc&lt;/code&gt; 局部回退 + 改 prompt 继续&lt;/td&gt;&lt;td&gt;撤回最近几轮（Codex 是 fork 入口）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;想问个不相关的边角问题&lt;/td&gt;&lt;td&gt;&lt;code&gt;/btw&lt;/code&gt;&lt;/td&gt;&lt;td&gt;侧边问，&lt;strong&gt;答案不进入主对话历史&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;对话长了但任务没完&lt;/td&gt;&lt;td&gt;&lt;code&gt;/compact focus on X&lt;/code&gt;（Claude/Copilot）&lt;/td&gt;&lt;td&gt;保留当前任务，&lt;strong&gt;只压缩历史&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;任务完成要开始新任务&lt;/td&gt;&lt;td&gt;&lt;code&gt;/clear&lt;/code&gt;&lt;/td&gt;&lt;td&gt;完全清空，原对话进 &lt;code&gt;/resume&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键节奏&lt;/strong&gt;：同一任务上&lt;strong&gt;修 Agent 同一错误超过 2 次&lt;/strong&gt; → 立刻 &lt;code&gt;/clear&lt;/code&gt; 用更好的 prompt 重开。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;官方原话：“If you’ve corrected Claude more than twice on the same issue in one session, the context is cluttered with failed approaches.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;干净会话 + 更好的 prompt 永远胜过”长会话 + 累积修正”。&lt;/p&gt;
&lt;h3&gt;3.3 4 层记忆文件 + 一键生成&lt;a href=&quot;#33-4-层记忆文件--一键生成&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;把”每次都要说的事情”挪到磁盘，让 Agent 在每个新会话里”已经知道项目是谁搭的、约定是什么”——这是从”工具”到”协作者”的关键升级。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Claude Code 的一键生成&lt;/strong&gt;：在项目根跑 &lt;code&gt;/init&lt;/code&gt; —— Agent 分析项目结构自动生成 &lt;code&gt;CLAUDE.md&lt;/code&gt; 起始模板。&lt;/p&gt;
&lt;p&gt;加载顺序（从广到窄）：&lt;/p&gt;















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;作用域&lt;/th&gt;&lt;th&gt;Claude Code&lt;/th&gt;&lt;th&gt;Codex&lt;/th&gt;&lt;th&gt;Copilot&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;用户全局&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.codex/AGENTS.md&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.copilot/copilot-instructions.md&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;用户全局覆盖&lt;/td&gt;&lt;td&gt;（local 后缀）&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.codex/AGENTS.override.md&lt;/code&gt;&lt;/td&gt;&lt;td&gt;（&lt;code&gt;.override&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;项目共享&lt;/td&gt;&lt;td&gt;&lt;code&gt;./CLAUDE.md&lt;/code&gt; 或 &lt;code&gt;./.claude/CLAUDE.md&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;./AGENTS.md&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;项目模块化&lt;/td&gt;&lt;td&gt;&lt;code&gt;./.claude/rules/*.md&lt;/code&gt;（支持 &lt;code&gt;paths&lt;/code&gt; frontmatter）&lt;/td&gt;&lt;td&gt;每层一个 AGENTS.md&lt;/td&gt;&lt;td&gt;&lt;code&gt;.github/instructions/**/*.instructions.md&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;项目个人&lt;/td&gt;&lt;td&gt;&lt;code&gt;./CLAUDE.local.md&lt;/code&gt;（gitignore）&lt;/td&gt;&lt;td&gt;子目录 &lt;code&gt;AGENTS.override.md&lt;/code&gt;&lt;/td&gt;&lt;td&gt;本机范围&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;团队组织&lt;/td&gt;&lt;td&gt;Managed policy 文件&lt;/td&gt;&lt;td&gt;&lt;code&gt;project_doc_fallback_filenames&lt;/code&gt;&lt;/td&gt;&lt;td&gt;组织托管规则&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Copilot 加载顺序的关键规则&lt;/strong&gt;：“Repository instructions always take precedence over user instructions”（仓库级指令&lt;strong&gt;始终优先于&lt;/strong&gt;全局指令）。&lt;/p&gt;
&lt;p&gt;加载行为：&lt;strong&gt;不是覆盖而是拼接&lt;/strong&gt;（Anthropic 原话：“All discovered files are concatenated into context rather than overriding each other”）。子目录的 CLAUDE.md &lt;strong&gt;只在 Agent 读该子目录文件时才按需加载&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CLAUDE.md 里支持 &lt;code&gt;@&lt;/code&gt; 导入语法&lt;/strong&gt;：可以在 CLAUDE.md 中写 &lt;code&gt;See @README.md for project overview and @package.json for available npm commands.&lt;/code&gt;，Agent 会按需展开。最多 4 层引用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;大小上限&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude Code &lt;code&gt;MEMORY.md&lt;/code&gt; 启动时只加载&lt;strong&gt;前 200 行 / 25KB&lt;/strong&gt;（自动记忆机制）。&lt;/li&gt;
&lt;li&gt;Codex 合并总大小上限是 &lt;code&gt;project_doc_max_bytes = 32 KiB&lt;/code&gt;（默认）。&lt;/li&gt;
&lt;li&gt;复杂规则不要写进 CLAUDE.md，改用 PreToolUse &lt;strong&gt;hook&lt;/strong&gt; 强制约束。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.4 跨会话：CLAUDE.md vs Auto memory vs Codex Memories&lt;a href=&quot;#34-跨会话claudemd-vs-auto-memory-vs-codex-memories&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt;（人工写）—— 由开发者维护、每会话完整加载。&lt;strong&gt;应只放”普适指令”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Auto memory&lt;/strong&gt;（Claude Code v2.1.59+，Agent 自己学） —— Agent 自己记录 build commands、调试经验、架构笔记。存到 &lt;code&gt;~/.claude/projects/&amp;lt;project&amp;gt;/memory/&lt;/code&gt;，&lt;strong&gt;首次 200 行 / 25KB 启动时加载&lt;/strong&gt;，详情文件按需读。&lt;strong&gt;默认开启&lt;/strong&gt;，可用 &lt;code&gt;/memory&lt;/code&gt; 命令切换。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Codex Memories / Chronicle&lt;/strong&gt;（Codex 0.140+ 新增） —— 文档侧栏已列出 &lt;code&gt;/codex/memories&lt;/code&gt; 和 &lt;code&gt;/codex/memories/chronicle&lt;/code&gt; 入口，对应 OpenAI 的持久记忆子系统。Chronicle 给出”会话历史洞察”。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;关键事实（Claude Code 文档原话）：“Both are loaded at the start of every conversation. Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;记忆文件只是上下文不是规则&lt;/strong&gt;——要强制约束必须用 hook，不是写文档。&lt;/p&gt;
&lt;h3&gt;3.5 长任务的会话状态恢复&lt;a href=&quot;#35-长任务的会话状态恢复&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;方案&lt;/th&gt;&lt;th&gt;适用&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;A. worktree + 会话隔离&lt;/td&gt;&lt;td&gt;大功能 / 长期任务&lt;/td&gt;&lt;td&gt;每个 git worktree 一个 &lt;code&gt;codex resume&lt;/code&gt; / &lt;code&gt;claude -r &amp;lt;id&amp;gt;&lt;/code&gt; 会话。切分支 = 切会话，context 完全隔离&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;B. CLAUDE.md 写”怎么续”&lt;/td&gt;&lt;td&gt;中等任务&lt;/td&gt;&lt;td&gt;在指令文件里写”如果中断了，请先 &lt;code&gt;git log -5&lt;/code&gt; 和 &lt;code&gt;git status&lt;/code&gt; 再继续”——把”怎么续”作为冷启动指令&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C. 连续 5 小时不清&lt;/td&gt;&lt;td&gt;&lt;strong&gt;强烈不推荐&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;后期 Agent 必然开始”忘记”早期约束、变笨、产出重复修复&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 安全：deny 永远第一&lt;a href=&quot;#4-安全deny-永远第一&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;永远不要让 Agent 默认拥有破坏性权限&lt;/strong&gt;。“给它 1 个权限 = 让它能用到 1 个” 是错误模型；正确模型是”默认拒绝 + 显式白名单”。&lt;/p&gt;
&lt;h3&gt;4.1 三级危险模型&lt;a href=&quot;#41-三级危险模型&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;把工具按破坏半径分三档，配权限时心里有数：&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;等级&lt;/th&gt;&lt;th&gt;例子&lt;/th&gt;&lt;th&gt;配权策略&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L1 只读&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Read&lt;/code&gt;、&lt;code&gt;Grep&lt;/code&gt;、&lt;code&gt;Glob&lt;/code&gt;、&lt;code&gt;Bash(ls *)&lt;/code&gt;、&lt;code&gt;Bash(git status)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;几乎无副作用，可以默认开&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L2 局部写&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Edit&lt;/code&gt;、&lt;code&gt;Write&lt;/code&gt;、&lt;code&gt;Bash(npm test)&lt;/code&gt;、&lt;code&gt;Bash(git commit *)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;影响工作区，需要按任务白名单&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L3 系统级&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Bash(rm -rf *)&lt;/code&gt;、&lt;code&gt;Bash(curl *)&lt;/code&gt;、&lt;code&gt;Bash(git push *)&lt;/code&gt;、&lt;code&gt;Bash(sudo *)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;必须显式拒绝或每次确认&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;4.2 deny 永远优先于 allow&lt;a href=&quot;#42-deny-永远优先于-allow&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是 Claude Code / Codex 共同的关键规则（Anthropic 原话）：“Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity does not change the order.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心警告&lt;/strong&gt;：“提示中的指令或 &lt;code&gt;CLAUDE.md&lt;/code&gt; 只能’影响 Claude 尝试做什么’，不能改变 Claude Code 实际允许的内容。”&lt;/p&gt;
&lt;p&gt;也就是说，prompt 写”请不要 rm -rf”是&lt;strong&gt;无效&lt;/strong&gt;的；必须在 &lt;code&gt;settings.json&lt;/code&gt; 里 deny。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;permissions&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;allow&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;Bash(npm run *)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;Bash(git commit *)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;Bash(* --version)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;deny&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;Bash(git push *)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;Bash(rm -rf *)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;Bash(curl *)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;Bash(sudo *)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;复合命令陷阱&lt;/strong&gt;：“Claude Code 识别 shell 操作符，因此 &lt;code&gt;Bash(safe-cmd *)&lt;/code&gt; &lt;strong&gt;不会&lt;/strong&gt;授权执行 &lt;code&gt;safe-cmd &amp;amp;&amp;amp; other-cmd&lt;/code&gt;。” —— 不会被通配符绕过。&lt;/p&gt;
&lt;h3&gt;4.3 三家权限机制对照&lt;a href=&quot;#43-三家权限机制对照&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;维度&lt;/th&gt;&lt;th&gt;Claude Code&lt;/th&gt;&lt;th&gt;Codex&lt;/th&gt;&lt;th&gt;Copilot CLI&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;配置文件&lt;/td&gt;&lt;td&gt;&lt;code&gt;settings.json&lt;/code&gt;（4 层优先级）&lt;/td&gt;&lt;td&gt;&lt;code&gt;~/.codex/config.toml&lt;/code&gt;&lt;/td&gt;&lt;td&gt;CLI 启动参数 + &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;写规则语法&lt;/td&gt;&lt;td&gt;&lt;code&gt;permissions.allow/deny/ask&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Approval mode&lt;/strong&gt;（Auto / Read-only / Full Access）+ &lt;code&gt;sandbox&lt;/code&gt; 配置&lt;/td&gt;&lt;td&gt;&lt;code&gt;--allow-tool&lt;/code&gt; / &lt;code&gt;--deny-tool&lt;/code&gt; / &lt;code&gt;--available-tools&lt;/code&gt; / &lt;code&gt;--excluded-tools&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;工具模式匹配&lt;/td&gt;&lt;td&gt;&lt;code&gt;Bash(git:*)&lt;/code&gt;（冒号）&lt;/td&gt;&lt;td&gt;基于前缀 + shell 元字符&lt;/td&gt;&lt;td&gt;&lt;code&gt;shell(git:*)&lt;/code&gt;（冒号）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;优先级&lt;/td&gt;&lt;td&gt;deny &amp;gt; ask &amp;gt; allow（按列表顺序）&lt;/td&gt;&lt;td&gt;Approval mode 是边界 + 是否提示&lt;/td&gt;&lt;td&gt;&lt;code&gt;--available-tools&lt;/code&gt; 覆盖 &lt;code&gt;--excluded-tools&lt;/code&gt;；&lt;code&gt;--deny-tool&lt;/code&gt; 覆盖 &lt;code&gt;--allow-tool&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;沙箱能力&lt;/td&gt;&lt;td&gt;OS 级 sandbox（&lt;code&gt;/sandbox&lt;/code&gt;，仅 Bash）&lt;/td&gt;&lt;td&gt;&lt;code&gt;sandbox&lt;/code&gt; 配置（结合 Approval mode）&lt;/td&gt;&lt;td&gt;仅基于规则的 allow/deny&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;推荐默认&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;default&lt;/code&gt; 模式 + 编辑过的 &lt;code&gt;settings.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Approval = &lt;code&gt;Auto&lt;/code&gt;，按任务加白名单&lt;/td&gt;&lt;td&gt;&lt;code&gt;--available-tools=&apos;bash,edit,view,grep,glob&apos; --allow-tool=&apos;shell(git:*)&apos; --deny-tool=&apos;shell(git push)&apos;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;4.4 Approval mode 三档&lt;a href=&quot;#44-approval-mode-三档&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Codex 把审批做成了三档显式状态（通过 &lt;code&gt;/permissions&lt;/code&gt; 切换，配置在 &lt;code&gt;config.toml&lt;/code&gt; 的 &lt;code&gt;approval_policy&lt;/code&gt;）：&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Mode&lt;/th&gt;&lt;th&gt;能力&lt;/th&gt;&lt;th&gt;何时用&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Auto&lt;/strong&gt;（默认）&lt;/td&gt;&lt;td&gt;当前工作目录内读/写/执行；越界（目录外、网络）需确认&lt;/td&gt;&lt;td&gt;日常开发&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Read-only&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;只浏览文件，不做修改，需先批准 plan&lt;/td&gt;&lt;td&gt;审 PR、看代码、咨询&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Full Access&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;跨整台机器 + 网络，不再询问&lt;/td&gt;&lt;td&gt;可信仓库的自动化&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Claude Code 有 &lt;strong&gt;OS 级 sandbox&lt;/strong&gt;（&lt;code&gt;/sandbox&lt;/code&gt; 启用，限制 Bash 文件系统和网络访问）。Copilot CLI 没有真沙箱，只能靠 &lt;code&gt;--deny-tool&lt;/code&gt; 黑名单 + &lt;code&gt;--available-tools&lt;/code&gt; 白名单缩小可选工具集。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Codex 的 4 层防御&lt;/strong&gt;（配合使用）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Approval mode（&lt;code&gt;Auto&lt;/code&gt; / &lt;code&gt;Read-only&lt;/code&gt; / &lt;code&gt;Full Access&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sandbox&lt;/code&gt; 配置（文件系统和网络隔离）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.toml&lt;/code&gt; 的 &lt;code&gt;[permissions]&lt;/code&gt; 段（具体规则白/黑名单）&lt;/li&gt;
&lt;li&gt;启动旗标 &lt;code&gt;--yolo&lt;/code&gt; 完全绕过（最后手段）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.5 &lt;code&gt;--yolo&lt;/code&gt; / &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt; 是最后手段&lt;a href=&quot;#45---yolo----dangerously-skip-permissions-是最后手段&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;工具&lt;/th&gt;&lt;th&gt;旗标&lt;/th&gt;&lt;th&gt;备注&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;&lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;（&lt;code&gt;bypassPermissions&lt;/code&gt; 模式）&lt;/td&gt;&lt;td&gt;跳过权限提示，但&lt;strong&gt;强制 ask 规则仍然提示&lt;/strong&gt;；针对根目录与家目录的删除仍会作为熔断机制&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Codex&lt;/td&gt;&lt;td&gt;&lt;code&gt;--yolo&lt;/code&gt; = &lt;code&gt;--dangerously-bypass-approvals-and-sandbox&lt;/code&gt;&lt;/td&gt;&lt;td&gt;”在生产或共享环境使用 &lt;code&gt;--yolo&lt;/code&gt;（除非处于专用沙箱 VM）“是被明令禁止的&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Copilot CLI&lt;/td&gt;&lt;td&gt;&lt;code&gt;--yolo&lt;/code&gt; / &lt;code&gt;--allow-all&lt;/code&gt;&lt;/td&gt;&lt;td&gt;等同于同时启用 &lt;code&gt;--allow-all-tools&lt;/code&gt;、&lt;code&gt;--allow-all-paths&lt;/code&gt;、&lt;code&gt;--allow-all-urls&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;官方一致警告&lt;/strong&gt;：“Only use this mode in isolated environments like containers or VMs”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;替代方案&lt;/strong&gt;：别用 &lt;code&gt;--yolo&lt;/code&gt;，用 &lt;code&gt;--allowedTools &quot;Edit,Bash(git commit *)&quot;&lt;/code&gt; 显式限定权限范围——Anthropic 原话：“The &lt;code&gt;--allowedTools&lt;/code&gt; flag restricts what Claude can do, which matters when you’re running unattended.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Claude Code 还有一个新选项&lt;/strong&gt;：&lt;code&gt;--permission-mode auto&lt;/code&gt;（Auto mode）—— 单独的分类器模型审核命令，拦截范围升级、未知基础设施、恶意驱动操作。比 &lt;code&gt;bypassPermissions&lt;/code&gt; 更安全：&lt;strong&gt;只跳过”低风险”命令的提示，高风险仍询问&lt;/strong&gt;。注意：Auto mode 在非交互 &lt;code&gt;claude -p&lt;/code&gt; 模式下，若分类器反复阻止会&lt;strong&gt;中止&lt;/strong&gt;（无用户兜底）。&lt;/p&gt;
&lt;h2&gt;5. 做 Agent 的产品经理&lt;a href=&quot;#5-做-agent-的产品经理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Agent 时代编程的本质是”做 Agent 的清晰操作员”。Simon Willison 的原话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The ‘agentic’ coding tools we have right now work like this: A skilled individual with both deep domain understanding and deep understanding of the capabilities of the agent…”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;价值 = 领域知识 × 工具熟练度。下面 7 条心法按”反人性”程度排序。&lt;/p&gt;
&lt;h3&gt;5.1 提问要像产品需求文档&lt;a href=&quot;#51-提问要像产品需求文档&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;反例&lt;/strong&gt;：“修一下这个 bug”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正例&lt;/strong&gt;：“登录在 session timeout 后失败，错误堆栈是 [粘贴]。先写一个失败测试复现，再修根因，不要只 suppress 错误。修改后跑 &lt;code&gt;pytest tests/auth/test_session.py&lt;/code&gt; 全部用例验证。”&lt;/p&gt;
&lt;p&gt;黄金模板：&lt;strong&gt;[症状] + [期望行为] + [验证标准] + [禁止做法]&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Anthropic 原话：“Give Claude a way to verify its work. ‘Looks done’ is not a good signal.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;给 Agent 配 4 种”验证门控”（按设置成本从低到高）&lt;a href=&quot;#给-agent-配-4-种验证门控按设置成本从低到高&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;同一个 prompt 内&lt;/strong&gt; —— 在指令里直接要求”运行测试并迭代直到通过”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;/goal&lt;/code&gt;&lt;/strong&gt;（Claude Code） —— 跨会话条件门控，评估器每轮重新检查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop hook&lt;/strong&gt;（Claude Code） —— 脚本执行检查，&lt;strong&gt;阻止 turn 结束&lt;/strong&gt;直到条件满足（注意：连续 8 次阻止后 Claude Code 会强制结束 turn，避免死循环）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verification subagent&lt;/strong&gt;（独立 context）—— 在新上下文中反驳/审阅结论，避免自评偏差。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser screenshot&lt;/strong&gt;（Claude Code + Chrome）—— 截图对比设计稿，验证 UI 改动。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;5.2 增量验证：每改 10 行看一次&lt;a href=&quot;#52-增量验证每改-10-行看一次&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;不要 100 行一次性 commit。每次让 Agent 改完：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git diff&lt;/code&gt; 确认范围正确&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;/diff&lt;/code&gt;（Claude Code）看每 turn 的具体改动&lt;/li&gt;
&lt;li&gt;让 Agent 跑测试&lt;/li&gt;
&lt;li&gt;每次提交独立一个 commit（&lt;code&gt;git add -p&lt;/code&gt;），便于回退和 review&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;反模式&lt;/strong&gt;：“把整个功能一次写完再 review”——一旦 review 出问题，&lt;strong&gt;不知道哪 50 行引入的&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;5.3 任务拆分：一个会话 = 一件事&lt;a href=&quot;#53-任务拆分一个会话--一件事&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;反模式&lt;/strong&gt;：一个会话里做”加 OAuth + 修首页 bug + 重构 utils”，&lt;strong&gt;混完后清理 context 极痛苦&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;会话 1 = “实现 password reset”&lt;/li&gt;
&lt;li&gt;会话 2 = “加 metrics middleware”&lt;/li&gt;
&lt;li&gt;用 git 分支隔离&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方警告：“The kitchen sink session: One session with unrelated tasks. Use &lt;code&gt;/clear&lt;/code&gt; between unrelated tasks.”&lt;/p&gt;
&lt;h3&gt;5.4 两次失败就 &lt;code&gt;/clear&lt;/code&gt; 重来&lt;a href=&quot;#54-两次失败就-clear-重来&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Anthropic 原话：“If you’ve corrected Claude more than twice on the same issue in one session, the context is cluttered with failed approaches. Run &lt;code&gt;/clear&lt;/code&gt; and start fresh with a more specific prompt that incorporates what you learned.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;干净会话 + 更好的 prompt 永远胜过”长会话 + 累积修正”。&lt;/p&gt;
&lt;h3&gt;5.5 子代理是 context 的瑞士军刀&lt;a href=&quot;#55-子代理是-context-的瑞士军刀&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;“Since context is your fundamental constraint, subagents are one of the most powerful tools available.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;子代理在&lt;strong&gt;独立 context&lt;/strong&gt; 中运行、返回摘要，主对话不被”读了 200 个文件”污染。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;典型用法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让 &lt;code&gt;explore&lt;/code&gt; 子代理查 “auth 系统怎么管 token refresh” → 返回一段摘要。&lt;/li&gt;
&lt;li&gt;让审查子代理用&lt;strong&gt;新 context&lt;/strong&gt; 复审刚改的 diff —— &lt;strong&gt;关键洞察&lt;/strong&gt;：“A reviewer running in a fresh subagent context sees only the diff and the criteria you give it, not the reasoning that produced the change, so it evaluates the result on its own terms.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是&lt;strong&gt;对抗性验证&lt;/strong&gt;的标准模式：让一个干净的 context 独立判断结果好坏。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Claude Code 的子代理体系&lt;/strong&gt;（最完整）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;claude agents&lt;/code&gt;：内置多代理视图，监控并行后台会话&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent teams&lt;/strong&gt;（&lt;code&gt;/agent-teams&lt;/code&gt;）：多 session 自动协调 + 共享任务列表&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code intelligence plugin&lt;/strong&gt;：给 typed language 提供精确符号导航和自动错误检测&lt;/li&gt;
&lt;li&gt;子代理配置在 &lt;code&gt;.claude/agents/*.md&lt;/code&gt;，frontmatter 指定 &lt;code&gt;tools&lt;/code&gt; 和 &lt;code&gt;model&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Codex 的子代理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;codex agents&lt;/code&gt; 视图&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;/fork&lt;/code&gt;&lt;/strong&gt; 和 &lt;strong&gt;&lt;code&gt;/side&lt;/code&gt;&lt;/strong&gt;：在历史消息节点分叉/并行会话&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codex 关键差异&lt;/strong&gt;：“Codex only spawns subagents when you &lt;strong&gt;explicitly ask it to&lt;/strong&gt;” —— 不像 Claude Code 那样按需自动派生，token 消耗更可控&lt;/li&gt;
&lt;li&gt;子代理配置在 &lt;code&gt;config.toml&lt;/code&gt; 的 &lt;code&gt;[agents]&lt;/code&gt; 段&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Copilot CLI 的子代理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/fleet&lt;/code&gt;：把大任务&lt;strong&gt;分解为并行子任务&lt;/strong&gt;由子代理执行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/delegate&lt;/code&gt;：把整个工作转移到 &lt;strong&gt;GitHub 云端 Copilot&lt;/strong&gt;，云端代理会创建 PR&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/task&lt;/code&gt;、&lt;code&gt;/review&lt;/code&gt;：显式触发子代理（&lt;code&gt;/review&lt;/code&gt; 有 4 种预设：base branch / uncommitted / commit / custom）&lt;/li&gt;
&lt;li&gt;内置子代理（&lt;code&gt;/review&lt;/code&gt;、&lt;code&gt;/task&lt;/code&gt;、explore、&lt;code&gt;/fleet&lt;/code&gt;）&lt;strong&gt;自动继承&lt;/strong&gt; provider 配置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Writer/Reviewer 模式&lt;/strong&gt;（Claude Code 强调）：用全新上下文的 Reviewer 子代理审 Writer 子代理的产出，避免自评偏差。&lt;/p&gt;
&lt;h3&gt;5.6 测试是 Agent 时代的”免费午餐”&lt;a href=&quot;#56-测试是-agent-时代的免费午餐&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Simon Willison 原话：“Good automated tests which the coding agent can run … pytest … 1500 tests … Claude Code is great at selectively running just the relevant tests for a change, and running the full suite at the end.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;关键技巧&lt;/strong&gt;：“detailed error messages! If a manual or automated test fails the more information you can return back to the model the better” —— &lt;strong&gt;把 assertion 写详细&lt;/strong&gt;，让 Agent 能”自己读错误自己修”。&lt;/p&gt;
&lt;p&gt;差的 assertion：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt; user.is_authenticated()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好的 assertion（Agent 能直接定位问题）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt; user.is_authenticated(), &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Expected authenticated user, got state=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;user.state&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;, session_age=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;user.session_age&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;s, token=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;user.token[:&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;...&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copilot CLI 的 Plan 流程标准最后一步就是 “Run the tests and fix any failures”——把”跑测试”嵌进标准工作流。&lt;/p&gt;
&lt;h3&gt;5.7 给 Agent 一个工具齐全的开发环境&lt;a href=&quot;#57-给-agent-一个工具齐全的开发环境&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Simon Willison 的 HN 回复清单（值得全文照搬）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;好测试&lt;/strong&gt;（pytest + 详细 assertion message）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开发服务器启动说明&lt;/strong&gt;（让 Agent 用 Playwright / curl 交互式验证 UI 改动）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lint + type check + formatter&lt;/strong&gt;（Agent 会自己用）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub issues 列表&lt;/strong&gt;（把 issue URL 直接贴进 prompt，“having great results”）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;反直觉点&lt;/strong&gt;：“I have extensive documentation in all of my projects, but I don’t think it’s particularly useful for coding agents. LLMs can read the code a lot faster than you to figure out how to use it.”&lt;/p&gt;
&lt;p&gt;文档对 Agent 没用，&lt;strong&gt;Agent 读代码比人快&lt;/strong&gt;；文档的作用反而是”让 Agent 检查文档是否需要更新”。&lt;/p&gt;
&lt;h3&gt;5.8 5 条必避反模式&lt;a href=&quot;#58-5-条必避反模式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;反模式&lt;/th&gt;&lt;th&gt;描述&lt;/th&gt;&lt;th&gt;修复&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;The kitchen sink session&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;一个会话混入不相关任务&lt;/td&gt;&lt;td&gt;任务间用 &lt;code&gt;/clear&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Correcting over and over&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;同一问题多次纠正&lt;/td&gt;&lt;td&gt;2 次失败就 &lt;code&gt;/clear&lt;/code&gt; 改 prompt&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;The over-specified CLAUDE.md&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;规则太多淹没有效指令&lt;/td&gt;&lt;td&gt;严格剪枝（200 行内），复杂规则改 hook&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;The infinite exploration&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;”调查”无边界，Agent 读数百文件&lt;/td&gt;&lt;td&gt;缩小范围或用 subagent&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;The trust-then-verify gap&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;实现看起来合理但缺边缘场景&lt;/td&gt;&lt;td&gt;始终要求提供验证（测试 / 脚本 / 截图）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 参考&lt;a href=&quot;#6-参考&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Claude Code: &lt;a href=&quot;https://code.claude.com/docs/en/best-practices&quot;&gt;https://code.claude.com/docs/en/best-practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Codex CLI: &lt;a href=&quot;https://developers.openai.com/codex/cli/features&quot;&gt;https://developers.openai.com/codex/cli/features&lt;/a&gt; + &lt;a href=&quot;https://developers.openai.com/codex/cli/reference&quot;&gt;https://developers.openai.com/codex/cli/reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Copilot CLI: &lt;a href=&quot;https://docs.github.com/en/copilot/how-tos/copilot-cli/cli-best-practices&quot;&gt;https://docs.github.com/en/copilot/how-tos/copilot-cli/cli-best-practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Simon Willison “Setting up a codebase for working with coding agents”（2025-10-25）: &lt;a href=&quot;https://simonwillison.net/2025/Oct/25/coding-agent-tips/&quot;&gt;https://simonwillison.net/2025/Oct/25/coding-agent-tips/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:工具</category><category>tag:CLI</category></item><item><title>VSCode + SSH + Docker 完整配置流程</title><link>https://youki.fun/ko/post/vscode-ssh-docker</link><guid isPermaLink="false">ko:vscode-ssh-docker</guid><description>从全新 Linux 机器到 VSCode 免密管理 Docker 的完整可复用配置流程</description><pubDate>Fri, 24 Apr 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;以下是一份&lt;strong&gt;完整的、可复用的配置流程&lt;/strong&gt;，从一台全新的 Linux 机器到 VSCode 免密管理 Docker，按顺序执行即可。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Linux 端准备&lt;a href=&quot;#1-linux-端准备&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1.1 开启 SSH 服务&lt;a href=&quot;#11-开启-ssh-服务&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Kali Linux：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 启动 SSH&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; /etc/init.d/ssh&lt;/span&gt;&lt;span&gt; start&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 开机自启&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; systemctl&lt;/span&gt;&lt;span&gt; enable&lt;/span&gt;&lt;span&gt; ssh.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ubuntu/Debian：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 安装并启动&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; openssh-server&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; service&lt;/span&gt;&lt;span&gt; ssh&lt;/span&gt;&lt;span&gt; start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.2 查看 IP 地址&lt;a href=&quot;#12-查看-ip-地址&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;记下 &lt;code&gt;inet&lt;/code&gt; 后面的局域网 IP（如 &lt;code&gt;192.168.17.129&lt;/code&gt;）。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Windows 端准备&lt;a href=&quot;#2-windows-端准备&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;2.1 安装 VSCode 插件&lt;a href=&quot;#21-安装-vscode-插件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;打开 VSCode，安装以下扩展：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Remote - SSH&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 生成 SSH 密钥对&lt;a href=&quot;#22-生成-ssh-密钥对&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;打开 &lt;strong&gt;PowerShell&lt;/strong&gt;，执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;keygen &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;t ed25519&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;保存路径：直接回车（默认 &lt;code&gt;C:\Users\你的用户名\.ssh\id_ed25519&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;密码：直接回车（不设密码，实现完全免密）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;生成后确认文件存在：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ls $&lt;/span&gt;&lt;span&gt;env:&lt;/span&gt;&lt;span&gt;USERPROFILE\.ssh\&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应看到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;id_ed25519&lt;/code&gt; —— &lt;strong&gt;私钥&lt;/strong&gt;（留在 Windows）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id_ed25519.pub&lt;/code&gt; —— &lt;strong&gt;公钥&lt;/strong&gt;（放到 Linux）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 配置免密登录（公私钥）&lt;a href=&quot;#3-配置免密登录公私钥&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;3.1 将公钥上传到 Linux&lt;a href=&quot;#31-将公钥上传到-linux&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;方法一：命令自动上传（推荐）&lt;/strong&gt;
在 Windows PowerShell 执行（替换 IP 和用户名）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;type $&lt;/span&gt;&lt;span&gt;env:&lt;/span&gt;&lt;span&gt;USERPROFILE\.ssh\id_ed25519.pub &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; ssh kali@192.168.17.129 &lt;/span&gt;&lt;span&gt;&quot;mkdir -p ~/.ssh &amp;amp;&amp;amp; cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys &amp;amp;&amp;amp; chmod 600 ~/.ssh/authorized_keys &amp;amp;&amp;amp; chmod 700 ~/.ssh&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输入一次密码后，公钥即上传完成。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法二：手动复制&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用记事本打开 &lt;code&gt;C:\Users\你的用户名\.ssh\id_ed25519.pub&lt;/code&gt;，复制全部内容&lt;/li&gt;
&lt;li&gt;在 VSCode 连上的 Linux 终端中：
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;nano&lt;/span&gt;&lt;span&gt; ~/.ssh/authorized_keys&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;粘贴 → &lt;code&gt;Ctrl+O&lt;/code&gt; 回车保存 → &lt;code&gt;Ctrl+X&lt;/code&gt; 退出&lt;/li&gt;
&lt;li&gt;设置权限：
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; 600&lt;/span&gt;&lt;span&gt; ~/.ssh/authorized_keys&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; 700&lt;/span&gt;&lt;span&gt; ~/.ssh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.2 配置 VSCode SSH 使用私钥&lt;a href=&quot;#32-配置-vscode-ssh-使用私钥&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;按 &lt;code&gt;Ctrl+Shift+P&lt;/code&gt; → &lt;code&gt;Remote-SSH: Open SSH Configuration File&lt;/code&gt; → 选择第一个。&lt;/p&gt;
&lt;p&gt;添加或修改配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Host kali&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    HostName 192.168.17.129&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    User kali&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    IdentityFile C:\Users\你的用户名\.ssh\id_ed25519&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;code&gt;IdentityFile&lt;/code&gt; 指向的是&lt;strong&gt;私钥&lt;/strong&gt;（&lt;code&gt;id_ed25519&lt;/code&gt;），不是 &lt;code&gt;.pub&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3.3 修复 Windows 私钥权限（关键！）&lt;a href=&quot;#33-修复-windows-私钥权限关键&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在 PowerShell 执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;cd $&lt;/span&gt;&lt;span&gt;env:&lt;/span&gt;&lt;span&gt;USERPROFILE\.ssh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 移除继承权限，仅保留当前用户读取&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;icacls id_ed25519 &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;inheritance:r&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;icacls id_ed25519 &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;grant:r &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;env:&lt;/span&gt;&lt;span&gt;USERNAME&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:(R)&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 测试免密连接&lt;a href=&quot;#34-测试免密连接&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在 PowerShell 测试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ssh &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;i $&lt;/span&gt;&lt;span&gt;env:&lt;/span&gt;&lt;span&gt;USERPROFILE\.ssh\id_ed25519 kali@192.168.17.129&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果直接连上，说明配置成功。&lt;/p&gt;
&lt;p&gt;然后在 VSCode 中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;点击左下角绿色 &lt;code&gt;&amp;gt;&amp;lt;&lt;/code&gt; 图标&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;Connect to Host...&lt;/code&gt; → &lt;code&gt;kali&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;应直接连上，不再提示密码&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Linux 端安装 Docker&lt;a href=&quot;#4-linux-端安装-docker&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;4.1 安装 Docker&lt;a href=&quot;#41-安装-docker&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;推荐方式（Ubuntu/Debian）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;wget&lt;/span&gt;&lt;span&gt; -qO-&lt;/span&gt;&lt;span&gt; https://get.docker.com/&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Kali Linux（Docker 官方不支持 Kali，需手动）：&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; ca-certificates&lt;/span&gt;&lt;span&gt; curl&lt;/span&gt;&lt;span&gt; gnupg&lt;/span&gt;&lt;span&gt; lsb-release&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 添加 GPG key&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; mkdir&lt;/span&gt;&lt;span&gt; -p&lt;/span&gt;&lt;span&gt; /etc/apt/keyrings&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; -fsSL&lt;/span&gt;&lt;span&gt; https://download.docker.com/linux/debian/gpg&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sudo&lt;/span&gt;&lt;span&gt; gpg&lt;/span&gt;&lt;span&gt; --dearmor&lt;/span&gt;&lt;span&gt; -o&lt;/span&gt;&lt;span&gt; /etc/apt/keyrings/docker.gpg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 使用 Debian 源（Kali 基于 Debian）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &quot;deb [arch=$(&lt;/span&gt;&lt;span&gt;dpkg&lt;/span&gt;&lt;span&gt; --print-architecture&lt;/span&gt;&lt;span&gt;) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian buster stable&quot;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sudo&lt;/span&gt;&lt;span&gt; tee&lt;/span&gt;&lt;span&gt; /etc/apt/sources.list.d/docker.list&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; /dev/null&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 安装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; docker-ce&lt;/span&gt;&lt;span&gt; docker-ce-cli&lt;/span&gt;&lt;span&gt; containerd.io&lt;/span&gt;&lt;span&gt; docker-compose-plugin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;4.2 验证安装&lt;a href=&quot;#42-验证安装&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; docker&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; hello-world&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看到 &lt;code&gt;Hello from Docker!&lt;/code&gt; 即成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 解决 Docker 权限问题&lt;a href=&quot;#5-解决-docker-权限问题&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;VSCode 连上后，Docker 插件可能提示无法访问 &lt;code&gt;/var/run/docker.sock&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一劳永逸的解法：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Linux 终端执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 将当前用户加入 docker 组&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; usermod&lt;/span&gt;&lt;span&gt; -aG&lt;/span&gt;&lt;span&gt; docker&lt;/span&gt;&lt;span&gt; $USER&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 重启 Docker 服务&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; systemctl&lt;/span&gt;&lt;span&gt; restart&lt;/span&gt;&lt;span&gt; docker&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后&lt;strong&gt;断开 VSCode SSH 连接，重新连接&lt;/strong&gt;（或重启 Linux）。&lt;/p&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不再提示权限错误即成功。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. VSCode Docker 插件使用&lt;a href=&quot;#6-vscode-docker-插件使用&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;连接成功后，左侧边栏出现 Docker 图标，常用功能：&lt;/p&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;功能&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;拉取镜像&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Images → 右键 &lt;code&gt;Pull&lt;/code&gt; 或终端执行 &lt;code&gt;docker pull&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;运行容器&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;镜像右键 → &lt;code&gt;Run&lt;/code&gt;（后台）/ &lt;code&gt;Run Interactive&lt;/code&gt;（带日志）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;进入容器终端&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;容器右键 → &lt;code&gt;Attach Shell&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;查看日志&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;容器右键 → &lt;code&gt;View Logs&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;编辑容器文件&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;展开容器 &lt;code&gt;Files&lt;/code&gt; 树，直接双击修改&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;端口转发&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;容器右键 → &lt;code&gt;Forward Port&lt;/code&gt;，自动映射到 Windows 本地&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;浏览器打开&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;端口转发后，右键 &lt;code&gt;Open in Browser&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 快速检查清单&lt;a href=&quot;#7-快速检查清单&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;每次复用或排查问题时，按以下顺序检查：&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;检查项&lt;/th&gt;&lt;th&gt;命令/操作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Linux SSH 是否运行&lt;/td&gt;&lt;td&gt;&lt;code&gt;sudo service ssh status&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Windows 私钥权限&lt;/td&gt;&lt;td&gt;&lt;code&gt;icacls C:\Users\用户名\.ssh\id_ed25519&lt;/code&gt;（应只有当前用户）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Linux 公钥是否正确&lt;/td&gt;&lt;td&gt;&lt;code&gt;cat ~/.ssh/authorized_keys&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Linux .ssh 权限&lt;/td&gt;&lt;td&gt;&lt;code&gt;ls -la ~/.ssh&lt;/code&gt;（700 + 600）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;用户是否在 docker 组&lt;/td&gt;&lt;td&gt;&lt;code&gt;groups&lt;/code&gt;（应看到 &lt;code&gt;docker&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;VSCode 配置路径&lt;/td&gt;&lt;td&gt;&lt;code&gt;IdentityFile&lt;/code&gt; 指向私钥，不是 &lt;code&gt;.pub&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 配置文件模板&lt;a href=&quot;#8-配置文件模板&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Windows SSH 配置&lt;/strong&gt;（&lt;code&gt;C:\Users\用户名\.ssh\config&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Host kali&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    HostName 192.168.17.129&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    User kali&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    IdentityFile C:\Users\你的用户名\.ssh\id_ed25519&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ForwardAgent yes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Linux SSH 配置&lt;/strong&gt;（&lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;，可选加固）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;PubkeyAuthentication yes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PasswordAuthentication no    # 确认密钥可用后再开启，禁用密码登录&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改后重启 SSH：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; service&lt;/span&gt;&lt;span&gt; ssh&lt;/span&gt;&lt;span&gt; restart&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;按这个流程走，任何新的 Kali/Ubuntu/Debian 机器都可以在 5 分钟内配置好完整的远程 Docker 开发环境。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:工具</category><category>tag:VSCode</category><category>tag:SSH</category><category>tag:Docker</category></item><item><title>Word 排版技巧</title><link>https://youki.fun/ko/post/word-layout</link><guid isPermaLink="false">ko:word-layout</guid><description>图片题注、交叉引用、三线表制作、PDF编辑等Word排版实用技巧</description><pubDate>Tue, 21 Apr 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文整理 Word 排版中的实用技巧，包括图片题注、交叉引用、三线表制作等。&lt;/p&gt;
&lt;h2&gt;1. 图片排版&lt;a href=&quot;#1-图片排版&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1.1 粘贴图片显示不全&lt;a href=&quot;#11-粘贴图片显示不全&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：粘贴图片后只显示一行，图片无法完整显示。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;图片显示问题&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;解决方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;选中图片所在段落&lt;/li&gt;
&lt;li&gt;右键 → 段落&lt;/li&gt;
&lt;li&gt;将行间距改为&lt;strong&gt;单倍行距&lt;/strong&gt;或&lt;strong&gt;固定值&lt;/strong&gt;（如 20 磅）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;1.2 添加题注&lt;a href=&quot;#12-添加题注&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;给图片添加题注，方便引用和管理。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;添加题注&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;操作步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;右键图片 → &lt;strong&gt;插入题注&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;点击&lt;strong&gt;新建标签&lt;/strong&gt;，输入”图”&lt;/li&gt;
&lt;li&gt;位置选择”所选项目下方”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;设置编号格式&lt;/strong&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;编号设置&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;点击&lt;strong&gt;编号&lt;/strong&gt;按钮&lt;/li&gt;
&lt;li&gt;勾选”包含章节号”&lt;/li&gt;
&lt;li&gt;选择章节样式（如”标题 1”）&lt;/li&gt;
&lt;li&gt;选择分隔符（如”-“或”.“）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 交叉引用&lt;a href=&quot;#13-交叉引用&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;使用交叉引用，可以在图片增删后&lt;strong&gt;自动更新编号&lt;/strong&gt;。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;交叉引用&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;操作步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;光标定位到引用位置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引用&lt;/strong&gt; → &lt;strong&gt;交叉引用&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;引用类型选择”图”&lt;/li&gt;
&lt;li&gt;选择要引用的题注&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;1.4 修改题注样式&lt;a href=&quot;#14-修改题注样式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;修改题注的字体、字号、对齐方式。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;修改样式&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;样式设置&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;操作步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;开始&lt;/strong&gt; → &lt;strong&gt;样式&lt;/strong&gt; 右下角展开&lt;/li&gt;
&lt;li&gt;找到”题注”样式，右键 → &lt;strong&gt;修改&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;设置字体（如宋体、五号）&lt;/li&gt;
&lt;li&gt;设置对齐方式（如居中）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;段落居中设置&lt;/strong&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;居中设置&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;右键题注 → 段落&lt;/li&gt;
&lt;li&gt;对齐方式选择”居中”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.5 批量更新编号&lt;a href=&quot;#15-批量更新编号&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;当图片增删后，需要批量更新所有编号。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;更新编号&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;操作步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ctrl + A&lt;/strong&gt; 全选文档&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;F9&lt;/strong&gt; 更新域&lt;/li&gt;
&lt;li&gt;所有题注编号自动更新&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;2. 三线表制作&lt;a href=&quot;#2-三线表制作&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;学术论文中常用的三线表样式。&lt;/p&gt;
&lt;h3&gt;2.1 新建表格样式&lt;a href=&quot;#21-新建表格样式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;表格样式&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;操作步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;表格设计&lt;/strong&gt; → &lt;strong&gt;新建表格样式&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;设置样式名称（如”三线表”）&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;样式设置&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;设置边框：
&lt;ul&gt;
&lt;li&gt;顶线：1.5 磅，黑色&lt;/li&gt;
&lt;li&gt;中间线：0.5 磅，黑色&lt;/li&gt;
&lt;li&gt;底线：1.5 磅，黑色&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;边框设置&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;应用范围选择”整张表格”&lt;/li&gt;
&lt;li&gt;点击确定保存&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2.2 应用样式&lt;a href=&quot;#22-应用样式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;选中表格&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表格设计&lt;/strong&gt; → 找到自定义的”三线表”样式&lt;/li&gt;
&lt;li&gt;点击应用&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;3. PDF 编辑&lt;a href=&quot;#3-pdf-编辑&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果需要编辑 PDF 文件，可以使用 Adobe Acrobat。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;Acrobat&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;常用功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PDF 转 Word&lt;/li&gt;
&lt;li&gt;合并/拆分 PDF&lt;/li&gt;
&lt;li&gt;添加水印&lt;/li&gt;
&lt;li&gt;表单填写&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 总结&lt;a href=&quot;#4-总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;技巧速查表&lt;a href=&quot;#技巧速查表&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;技巧&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;图片显示不全&lt;/td&gt;&lt;td&gt;段落 → 行间距改为单倍行距&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;添加题注&lt;/td&gt;&lt;td&gt;右键 → 插入题注 → 新建标签&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;自动编号&lt;/td&gt;&lt;td&gt;插入题注时勾选”包含章节号”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;交叉引用&lt;/td&gt;&lt;td&gt;引用 → 交叉引用 → 选择题注&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;批量更新&lt;/td&gt;&lt;td&gt;Ctrl+A → F9&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;三线表&lt;/td&gt;&lt;td&gt;新建表格样式 → 设置顶线/底线 1.5 磅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PDF 编辑&lt;/td&gt;&lt;td&gt;使用 Adobe Acrobat&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;注意事项&lt;a href=&quot;#注意事项&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;题注样式修改后，所有题注会自动更新&lt;/li&gt;
&lt;li&gt;交叉引用需要手动更新（F9）&lt;/li&gt;
&lt;li&gt;三线表的中间线通常比顶底线细&lt;/li&gt;
&lt;li&gt;建议在排版前先设置好样式，避免后期大量修改&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:工具</category><category>tag:Word</category></item><item><title>Cloudflare 边缘应用：一个 IP 承载数千网站</title><link>https://youki.fun/ko/post/cloudflare-ip-edge-apps</link><guid isPermaLink="false">ko:cloudflare-ip-edge-apps</guid><description>Cloudflare 一个 IP 承载数千网站的原理：SNI/Host 路由、Anycast、边缘计算</description><pubDate>Thu, 09 Apr 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;参考讨论：&lt;a href=&quot;https://github.com/XTLS/BBS/issues/23&quot;&gt;XTLS/BBS#23&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cloudflare 一个 IP（如 &lt;code&gt;104.16.x.x&lt;/code&gt;）能同时承载数千甚至上万个网站，背后是 CDN 的几项核心技术叠加：反向代理 + SNI/Host 路由、Anycast、边缘计算。&lt;/p&gt;
&lt;h2&gt;1. 反向代理（Reverse Proxy）+ SNI 路由&lt;a href=&quot;#1-反向代理reverse-proxy-sni-路由&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;用户访问 site1.com ──┐&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;用户访问 site2.com ──┼──→ Cloudflare IP (104.16.x.x) ──→ 根据信息分发到不同源站&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;用户访问 site3.com ──┘&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;关键技术：SNI + Host 头双重路由。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;第一步：TLS 握手（SNI 决定证书）&lt;a href=&quot;#第一步tls-握手sni-决定证书&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;你的浏览器 → Cloudflare:443&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Client Hello:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - SNI: &quot;site1.com&quot;  ← 明文传输&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Cloudflare 根据 SNI：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 找到 site1.com 的证书&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 返回 site1.com 的 TLS 证书&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;第二步：HTTP 请求（Host 头决定源站）&lt;a href=&quot;#第二步http-请求host-头决定源站&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;HTTP GET / HTTP/1.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Host: site1.com  ← 决定请求转发给哪个源服务器&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;结果：&lt;/strong&gt; 同一个 IP，根据 SNI 和 Host 头的不同，可以服务无限个域名。&lt;/p&gt;
&lt;h2&gt;2. Anycast 技术（全球共享 IP）&lt;a href=&quot;#2-anycast-技术全球共享-ip&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这是 Cloudflare 的独特之处：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;传统 DNS：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- site.com → 1.2.3.4（美国服务器）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 中国用户访问慢&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Cloudflare Anycast：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 104.16.249.249 同时在全球广播：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 北京用户访问 → 被路由到北京机房&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 纽约用户访问 → 被路由到纽约机房&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 伦敦用户访问 → 被路由到伦敦机房&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;同一个 IP 地址，物理上对应全球数百个数据中心。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;3. 边缘计算（Edge Computing）&lt;a href=&quot;#3-边缘计算edge-computing&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;对于 Workers/Pages：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;用户请求 example.worker.dev&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;           ↓&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;到达最近的 Cloudflare 边缘节点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;           ↓&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;不转发到源站，直接在边缘节点执行 JavaScript 代码&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;           ↓&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;返回结果&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;一个 IP 既是反向代理入口，又是代码执行环境。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;4. 边缘应用为什么几乎不占资源&lt;a href=&quot;#4-边缘应用为什么几乎不占资源&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;边缘应用（Worker）是 &lt;strong&gt;Serverless 无服务器架构&lt;/strong&gt;，特点是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不是一直开着一台服务器等请求&lt;/li&gt;
&lt;li&gt;来一个请求，临时启动一段代码&lt;/li&gt;
&lt;li&gt;执行完（通常几毫秒～几十毫秒）直接销毁&lt;/li&gt;
&lt;li&gt;不请求时，&lt;strong&gt;完全不占资源&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个节点上可以同时跑几万个用户的边缘函数，资源是&lt;strong&gt;极度共享、极度轻量化&lt;/strong&gt;的。对 Cloudflare 来说，这点算力几乎可以忽略不计。&lt;/p&gt;
&lt;h2&gt;5. 写 API、鉴权、查数据库会不会很耗资源？&lt;a href=&quot;#5-写-api鉴权查数据库会不会很耗资源&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;并不会，而且架构是这样的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;边缘节点：只做&lt;strong&gt;轻逻辑&lt;/strong&gt;（校验、路由、拼接、简单计算）&lt;/li&gt;
&lt;li&gt;数据库：依然放在&lt;strong&gt;中心服务器 / 云数据库&lt;/strong&gt;（比如阿里云、腾讯云、PlanetScale）&lt;/li&gt;
&lt;li&gt;边缘节点 → 发请求 → 中心数据库拿数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不是每个边缘节点都装数据库。只是&lt;strong&gt;用户到边缘近&lt;/strong&gt;，边缘到数据库那一步是厂商优化过的内网专线，整体依然很快。&lt;/p&gt;
&lt;h2&gt;6. 对比传统后端&lt;a href=&quot;#6-对比传统后端&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;传统后端&lt;/strong&gt;：买 1 台 / 3 台 / 10 台服务器，自己扛所有流量，崩了要自己重启扩容&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;边缘后端&lt;/strong&gt;：厂商全球几百个节点自动兜底，流量再大也自动分配，不用管服务器数量&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:网络与架构</category><category>tag:Cloudflare</category><category>tag:CDN</category></item><item><title>VMware Workstation 下载指南</title><link>https://youki.fun/ko/post/vmware-download</link><guid isPermaLink="false">ko:vmware-download</guid><description>VMware Workstation Pro 下载与中文界面配置指南，个人使用免费</description><pubDate>Tue, 07 Apr 2026 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;背景&lt;a href=&quot;#背景&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;VMware 于 2023 年 11 月被 Broadcom 收购。2024 年 5 月起，&lt;strong&gt;VMware Workstation Pro 个人使用免费&lt;/strong&gt;（商业用途仍需付费）。原 VMware Workstation Player 已合并进 Pro 版本。&lt;/p&gt;
&lt;h2&gt;下载步骤&lt;a href=&quot;#下载步骤&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;访问 &lt;a href=&quot;https://support.broadcom.com&quot;&gt;Broadcom 支持门户&lt;/a&gt;，注册并登录账号&lt;/li&gt;
&lt;li&gt;进入产品页面，找到 &lt;strong&gt;VMware Workstation Pro&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;选择版本（当前最新为 17.6.x）和操作系统（Windows / Linux）&lt;/li&gt;
&lt;li&gt;下载安装包&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://knowledge.broadcom.com/external/article/368734&quot;&gt;Broadcom 官方说明&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;安装&lt;a href=&quot;#安装&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;运行下载的安装程序，按向导完成安装即可。建议使用默认安装路径，安装过程中勾选”增强型键盘驱动”以获得更好的输入体验。&lt;/p&gt;
&lt;h2&gt;配置中文界面&lt;a href=&quot;#配置中文界面&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;安装完成后如需切换为中文界面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开 VMware Workstation&lt;/li&gt;
&lt;li&gt;菜单栏 → &lt;strong&gt;Edit&lt;/strong&gt; → &lt;strong&gt;Preferences&lt;/strong&gt; → &lt;strong&gt;Language&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;选择 &lt;strong&gt;简体中文&lt;/strong&gt;，重启软件生效&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;详细图文教程参考：&lt;a href=&quot;https://blog.timxs.com/archives/ebcamx05&quot;&gt;VMware 中文语言配置&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;注意事项&lt;a href=&quot;#注意事项&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;务必从 &lt;a href=&quot;https://support.broadcom.com&quot;&gt;Broadcom 官网&lt;/a&gt; 下载，避免第三方来源的恶意软件&lt;/li&gt;
&lt;li&gt;仅限个人非商业用途免费，商业环境需购买许可&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:工具</category><category>tag:VMware</category><category>tag:虚拟机</category></item><item><title>校园网 IP 切换原理</title><link>https://youki.fun/ko/post/campus-network-ip</link><guid isPermaLink="false">ko:campus-network-ip</guid><description>校园网 NAT 出口会频繁切换公网 IP，但日常上网几乎无感知——已建立的连接不受影响，只有新连接才使用新 IP</description><pubDate>Wed, 01 Apr 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;很多人会注意到一个现象：在校园网里隔一段时间查一下自己的公网 IP，会发现它在悄悄变化，但自己刷网页、登微信、打游戏却完全没有感知。这篇笔记拆解这背后的原理：NAT 出口为什么频繁换 IP，以及为什么用户感觉不到。&lt;/p&gt;
&lt;h2&gt;1. 校园网为什么用 NAT&lt;a href=&quot;#1-校园网为什么用-nat&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;校园网通常只有一个或少数几个&lt;strong&gt;公网 IP&lt;/strong&gt;，但网内可能有成千上万台设备（手机、电脑、平板）。要把这么多设备接上互联网，靠的是 &lt;strong&gt;NAT（网络地址转换）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每台设备拿到的是一个&lt;strong&gt;内网 IP&lt;/strong&gt;（如 &lt;code&gt;10.x.x.x&lt;/code&gt;、&lt;code&gt;172.16.x.x&lt;/code&gt;、&lt;code&gt;192.168.x.x&lt;/code&gt;），这些地址在公网上无法路由。&lt;/li&gt;
&lt;li&gt;设备访问外网时，出口的 NAT 设备（路由器/网关）把&lt;strong&gt;源地址从内网 IP 改写成公网 IP&lt;/strong&gt;，并记录一条”内网 IP+端口 ↔ 公网 IP+端口”的映射。&lt;/li&gt;
&lt;li&gt;对端回包时，NAT 再根据映射表把目的地址翻译回内网 IP，送到对应设备。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，所有设备的流量对外都”伪装”成从那几个公网 IP 发出，&lt;strong&gt;对外呈现的公网 IP 是 NAT 出口决定的&lt;/strong&gt;，而不是你的设备决定的。&lt;/p&gt;
&lt;h2&gt;2. 为什么校园网的公网 IP 会频繁切换&lt;a href=&quot;#2-为什么校园网的公网-ip-会频繁切换&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;公网 IP 的频繁变化，通常由这几个机制叠加造成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DHCP 短租约&lt;/strong&gt;：出口公网 IP 本身可能也是向运营商 DHCP 租来的，租约一过期就可能续到另一个 IP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多出口负载均衡&lt;/strong&gt;：大型校园网往往接了多条运营商线路（电信、联通、教育网等），网关根据负载把流量分到不同出口，于是你看到的公网 IP 在不同出口之间跳。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;故障/拥塞切换&lt;/strong&gt;：某条出口线路抖动或拥塞时，网关会自动把流量切到健康线路，公网 IP 跟着变。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以”公网 IP 变了”在校园网里是常态，而非异常。&lt;/p&gt;
&lt;h2&gt;3. 为什么已建立的连接不受影响&lt;a href=&quot;#3-为什么已建立的连接不受影响&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;关键在于：NAT 设备对&lt;strong&gt;已经打开的应用、已经建立的 TCP 连接&lt;/strong&gt;（比如正在刷的网页、登着的微信、打着的游戏），会保留这个会话的映射关系：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;哪怕公网 IP 变了，已经建立的连接不会断，应用完全不受影响，用着和平时一模一样。&lt;/li&gt;
&lt;li&gt;只有当发起&lt;strong&gt;全新的连接&lt;/strong&gt;（比如新开一个 SSH、重新登录某个服务、新开浏览器标签页），才会使用新的公网 IP。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 为什么应用本身不关心源 IP&lt;a href=&quot;#4-为什么应用本身不关心源-ip&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;现在的所有互联网应用（微信、浏览器、视频软件等），只需要「网络通」，完全不依赖固定的源公网 IP：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;源 IP 变了，应用不会报错、不会掉线，用户察觉不到任何异常。&lt;/li&gt;
&lt;li&gt;只有&lt;strong&gt;需要「源 IP 白名单」的服务&lt;/strong&gt;（比如服务器的安全组、企业 VPN、特定后台系统），才会因为源 IP 变了而被拦截——这就是在校园网里 SSH 突然连不上服务器的一个常见根因。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 链路切换是毫秒级的&lt;a href=&quot;#5-链路切换是毫秒级的&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;校园网的负载均衡、故障切换都是毫秒级完成的，几乎感觉不到断网，自然也不会意识到 IP 变了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;：校园网公网 IP 频繁变化是 NAT + 多出口的正常行为；已建立的连接靠 NAT 映射表维持不断；普通应用不依赖固定源 IP 所以无感；只有依赖源 IP 白名单的服务（典型如 SSH 白名单、VPN）才会被影响。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:网络与架构</category><category>tag:NAT</category><category>tag:计算机网络</category></item><item><title>虚拟机网络配置</title><link>https://youki.fun/ko/post/vm-network-config</link><guid isPermaLink="false">ko:vm-network-config</guid><description>从 IP 规划到故障修复，一文搞定 VMware 虚拟机固定 IP，覆盖 nmcli 与 network.service 两种方案</description><pubDate>Fri, 06 Mar 2026 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1. 为什么需要固定 IP？最稳妥的规划策略&lt;a href=&quot;#1-为什么需要固定-ip最稳妥的规划策略&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;VMware NAT 默认通过 DHCP 分配 IP，地址会浮动，多台虚拟机同时开机还可能撞 IP，导致 SSH 频繁断连。最彻底的解决办法是：&lt;strong&gt;设置静态 IP，并故意选在 DHCP 分配池之外&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;以常见的 NAT 网段 &lt;code&gt;192.168.17.0/24&lt;/code&gt; 为例，VMware 的 DHCP 通常分配 &lt;code&gt;128-254&lt;/code&gt; 这个区间。因此安全可用的静态 IP 范围为 &lt;strong&gt;&lt;code&gt;192.168.17.3&lt;/code&gt; ~ &lt;code&gt;192.168.17.127&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;规划示例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;宿主机：&lt;code&gt;192.168.17.1&lt;/code&gt;（系统固定）&lt;/li&gt;
&lt;li&gt;NAT 网关：&lt;code&gt;192.168.17.2&lt;/code&gt;（系统固定）&lt;/li&gt;
&lt;li&gt;虚拟机 A：&lt;code&gt;192.168.17.10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;虚拟机 B：&lt;code&gt;192.168.17.11&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;……&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样无论 DHCP 怎么分配，都不会跟你手动的地址冲突。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文示例均以目标 IP &lt;code&gt;192.168.17.10&lt;/code&gt;、网卡名 &lt;code&gt;ens160&lt;/code&gt; 为例。&lt;strong&gt;请务必将命令中的 IP 和网卡名替换为你实际规划的值。&lt;/strong&gt;
不确定网卡名？执行 &lt;code&gt;ip addr&lt;/code&gt; 或 &lt;code&gt;nmcli connection show&lt;/code&gt; 查看，常见名有 &lt;code&gt;ens33&lt;/code&gt;、&lt;code&gt;eth0&lt;/code&gt; 等。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 前置检查：确保虚拟链路与 VMware 服务正常&lt;a href=&quot;#2-前置检查确保虚拟链路与-vmware-服务正常&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;很多“配置完不通”的问题，根源不在 Linux 配置，而在虚拟机硬件或 Windows 服务。正式配 IP 之前，先过一遍下面几点。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 虚拟机网卡硬件设置&lt;/strong&gt;（所有方案通用）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;关闭虚拟机（不是暂停），打开「虚拟机设置」→「网络适配器」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;必须勾选&lt;/strong&gt; ✅「已连接」、✅「启动时连接」。&lt;/li&gt;
&lt;li&gt;确认网络连接方式为 &lt;strong&gt;「NAT 模式」&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 检查宿主机 VMware 核心服务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Windows 中 &lt;code&gt;Win+R&lt;/code&gt; 输入 &lt;code&gt;services.msc&lt;/code&gt;，找到下面两个服务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;VMware NAT Service&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VMware DHCP Service&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;确保它们都是「正在运行」状态。如有异常，右键重新启动。&lt;br /&gt;
如果之前修改过虚拟网络编辑器（如恢复了默认设置），可能需要重启这两个服务甚至宿主机。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 启动虚拟机，快速确认网卡链路&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt; addr&lt;/span&gt;&lt;span&gt; show&lt;/span&gt;&lt;span&gt; ens160&lt;/span&gt;&lt;span&gt;   # 记得替换网卡名&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果输出中包含 &lt;code&gt;state UP&lt;/code&gt;，说明链路已通，可以进入下一步。&lt;br /&gt;
如果始终是 &lt;code&gt;state DOWN&lt;/code&gt;，回第一步重新确认 VMware 设置与服务。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 方案 A：NetworkManager 正常时的推荐做法（nmcli 一键搞定）&lt;a href=&quot;#3-方案-anetworkmanager-正常时的推荐做法nmcli-一键搞定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;大多数现代 Linux 发行版（CentOS 7+、Fedora、Ubuntu 等）默认使用 NetworkManager 管理网络。这种情况下 &lt;code&gt;nmcli&lt;/code&gt; 是最安全、最不容易产生配置冲突的方式。&lt;/p&gt;
&lt;p&gt;直接把下面命令里的 &lt;code&gt;192.168.17.10&lt;/code&gt; 和 &lt;code&gt;ens160&lt;/code&gt; 替换成你的实际值，整个复制执行即可：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;CONN&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ens160&quot;&lt;/span&gt;&lt;span&gt;  # 先确认网卡名&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 1. 绑定静态 IP 和子网掩码&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; nmcli&lt;/span&gt;&lt;span&gt; connection&lt;/span&gt;&lt;span&gt; modify&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CONN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ipv4.addresses&lt;/span&gt;&lt;span&gt; 192.168.17.10/24&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 2. 设置网关&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; nmcli&lt;/span&gt;&lt;span&gt; connection&lt;/span&gt;&lt;span&gt; modify&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CONN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ipv4.gateway&lt;/span&gt;&lt;span&gt; 192.168.17.2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 3. 设置 DNS（推荐公共 DNS）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; nmcli&lt;/span&gt;&lt;span&gt; connection&lt;/span&gt;&lt;span&gt; modify&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CONN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ipv4.dns&lt;/span&gt;&lt;span&gt; &quot;8.8.8.8,114.114.114.114&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 4. 改获取方式为手动&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; nmcli&lt;/span&gt;&lt;span&gt; connection&lt;/span&gt;&lt;span&gt; modify&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CONN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ipv4.method&lt;/span&gt;&lt;span&gt; manual&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 5. 可选：禁用 IPv6，避免其干扰连通性测试&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; nmcli&lt;/span&gt;&lt;span&gt; connection&lt;/span&gt;&lt;span&gt; modify&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CONN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ipv6.method&lt;/span&gt;&lt;span&gt; disabled&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 6. 重启网卡&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; nmcli&lt;/span&gt;&lt;span&gt; connection&lt;/span&gt;&lt;span&gt; down&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CONN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; nmcli&lt;/span&gt;&lt;span&gt; connection&lt;/span&gt;&lt;span&gt; up&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CONN&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;执行后验证：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt; addr&lt;/span&gt;&lt;span&gt; show&lt;/span&gt;&lt;span&gt; ens160&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; grep&lt;/span&gt;&lt;span&gt; 192.168.17.10&lt;/span&gt;&lt;span&gt;   # 确认 IP 已绑定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ping&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt; baidu.com&lt;/span&gt;&lt;span&gt;                        # 确认外网域名解析正常&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此后即便重启虚拟机，IP 也不会变，MobaXterm 之类的工具直接用 &lt;code&gt;192.168.17.10&lt;/code&gt; 连接即可。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 方案 B：当 NetworkManager 罢工时（未托管、device not found）&lt;a href=&quot;#4-方案-b当-networkmanager-罢工时未托管device-not-found&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;有时候系统会出现以下症状：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nmcli device status&lt;/code&gt; 显示 &lt;code&gt;ens160&lt;/code&gt; 为 &lt;code&gt;unmanaged&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;尝试 &lt;code&gt;nmcli connection up&lt;/code&gt; 提示 &lt;code&gt;No suitable device found&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;修改 &lt;code&gt;/etc/NetworkManager/...&lt;/code&gt; 的托管配置无效；&lt;/li&gt;
&lt;li&gt;甚至 VMware 中「已连接」都勾不上，网卡始终 &lt;code&gt;DOWN&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是典型的 &lt;strong&gt;NetworkManager 未托管 + 虚拟链路异常&lt;/strong&gt;，二合一故障。此时最直接的办法是&lt;strong&gt;跳过 NetworkManager，用传统 &lt;code&gt;network.service&lt;/code&gt; 接管&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;兼容性警告&lt;/strong&gt;&lt;br /&gt;
方案 B 依赖传统 &lt;code&gt;network-scripts&lt;/code&gt; 包，在 &lt;strong&gt;CentOS 7.x&lt;/strong&gt; 上适用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果你的系统是&lt;strong&gt;最小化安装&lt;/strong&gt;，可能未包含此包，需先执行 &lt;code&gt;sudo yum install network-scripts&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果你使用的是 &lt;strong&gt;RHEL 9 / Rocky Linux 9 / CentOS Stream 9&lt;/strong&gt; 及更新版本，该包已被官方移除，方案 B 不可用。请返回方案 A 排查 NM 的配置问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4.1 确保链路恢复（若仍 DOWN）&lt;a href=&quot;#41-确保链路恢复若仍-down&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果第一节的前置检查已通过，网卡已是 &lt;code&gt;UP&lt;/code&gt; 状态可略过。否则先在虚拟机设置中确认「已连接」已勾，并重启 VMware NAT/DHCP 服务，直至 &lt;code&gt;ip addr show&lt;/code&gt; 看到 &lt;code&gt;state UP&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;4.2 停用 NetworkManager，手动固定 IP&lt;a href=&quot;#42-停用-networkmanager手动固定-ip&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 1. 彻底停用 NetworkManager&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; systemctl&lt;/span&gt;&lt;span&gt; stop&lt;/span&gt;&lt;span&gt; NetworkManager&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; systemctl&lt;/span&gt;&lt;span&gt; disable&lt;/span&gt;&lt;span&gt; NetworkManager&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 2. 临时配通 IP（立即生效）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; ip&lt;/span&gt;&lt;span&gt; addr&lt;/span&gt;&lt;span&gt; add&lt;/span&gt;&lt;span&gt; 192.168.17.10/24&lt;/span&gt;&lt;span&gt; dev&lt;/span&gt;&lt;span&gt; ens160&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; ip&lt;/span&gt;&lt;span&gt; link&lt;/span&gt;&lt;span&gt; set&lt;/span&gt;&lt;span&gt; ens160&lt;/span&gt;&lt;span&gt; up&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; ip&lt;/span&gt;&lt;span&gt; route&lt;/span&gt;&lt;span&gt; add&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; via&lt;/span&gt;&lt;span&gt; 192.168.17.2&lt;/span&gt;&lt;span&gt; dev&lt;/span&gt;&lt;span&gt; ens160&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; -e&lt;/span&gt;&lt;span&gt; &quot;nameserver 8.8.8.8\nnameserver 114.114.114.114&quot;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sudo&lt;/span&gt;&lt;span&gt; tee&lt;/span&gt;&lt;span&gt; /etc/resolv.conf&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 3. 排除 IPv6 干扰：若 ping 外网失败，先临时禁用 IPv6 再测&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; sysctl&lt;/span&gt;&lt;span&gt; -w&lt;/span&gt;&lt;span&gt; net.ipv6.conf.ens160.disable_ipv6=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;现在测试 &lt;code&gt;ping 192.168.17.2&lt;/code&gt; 和 &lt;code&gt;ping baidu.com&lt;/code&gt;，应该都能通。&lt;/p&gt;
&lt;h3&gt;4.3 让配置永久生效（重启后 IP 依旧）&lt;a href=&quot;#43-让配置永久生效重启后-ip-依旧&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;停止 NetworkManager 后，系统会转而使用传统 &lt;code&gt;network&lt;/code&gt; 服务读取 &lt;code&gt;/etc/sysconfig/network-scripts/ifcfg-*&lt;/code&gt; 文件。我们直接写入正确配置：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; tee&lt;/span&gt;&lt;span&gt; /etc/sysconfig/network-scripts/ifcfg-ens160&lt;/span&gt;&lt;span&gt; &amp;lt;&amp;lt;&lt;/span&gt;&lt;span&gt;&apos;EOF&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;TYPE=Ethernet&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;BOOTPROTO=static&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;NAME=ens160&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DEVICE=ens160&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ONBOOT=yes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;IPADDR=192.168.17.10&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PREFIX=24&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;GATEWAY=192.168.17.2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DNS1=8.8.8.8&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DNS2=114.114.114.114&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;IPV6INIT=no&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PEERDNS=no&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;EOF&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;关于 &lt;code&gt;PEERDNS=no&lt;/code&gt; 的说明&lt;/strong&gt;&lt;br /&gt;
加上此参数后，网络服务启动时不会自动修改 &lt;code&gt;/etc/resolv.conf&lt;/code&gt;，DNS 将严格使用此文件中定义的 &lt;code&gt;DNS1&lt;/code&gt; 和 &lt;code&gt;DNS2&lt;/code&gt;。这能确保你手动设定的 DNS 不被其他服务覆盖。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后启用并启动传统网络服务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; systemctl&lt;/span&gt;&lt;span&gt; enable&lt;/span&gt;&lt;span&gt; network&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; systemctl&lt;/span&gt;&lt;span&gt; restart&lt;/span&gt;&lt;span&gt; network&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后，如果之前禁用了 IPv6，可将其设为永久生效：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; sysctl&lt;/span&gt;&lt;span&gt; -w&lt;/span&gt;&lt;span&gt; net.ipv6.conf.ens160.disable_ipv6=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sudo&lt;/span&gt;&lt;span&gt; tee&lt;/span&gt;&lt;span&gt; -a&lt;/span&gt;&lt;span&gt; /etc/sysctl.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，无论是否重启，虚拟机都会固定使用 &lt;code&gt;192.168.17.10&lt;/code&gt;，且不会再被 NetworkManager 干扰。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 快速验证清单&lt;a href=&quot;#5-快速验证清单&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;无论用哪个方案，最后都跑一遍：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt; addr&lt;/span&gt;&lt;span&gt; show&lt;/span&gt;&lt;span&gt; ens160&lt;/span&gt;&lt;span&gt;          # 看到 192.168.17.10/24&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ping&lt;/span&gt;&lt;span&gt; 192.168.17.2&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;       # 网关通&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ping&lt;/span&gt;&lt;span&gt; www.baidu.com&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;      # 外网域名解析通&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;全通则成功。然后就可以打开 MobaXterm，新建 SSH Session，Host 填入 &lt;code&gt;192.168.17.10&lt;/code&gt;，享受稳定的连接。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 故障速查表&lt;a href=&quot;#6-故障速查表&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;现象&lt;/th&gt;&lt;th&gt;可能原因&lt;/th&gt;&lt;th&gt;快速处置&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;网卡始终 &lt;code&gt;DOWN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;虚拟机设置中未勾选「已连接」或 VMware 服务未运行&lt;/td&gt;&lt;td&gt;重启 VMware NAT/DHCP 服务，确认勾选已打&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;nmcli&lt;/code&gt; 报 &lt;code&gt;unmanaged&lt;/code&gt;&lt;/td&gt;&lt;td&gt;NetworkManager 配置禁止管理该网卡&lt;/td&gt;&lt;td&gt;若系统版本支持，直接用方案 B；否则排查 NM 配置文件&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ping&lt;/code&gt; 网关通但域名解析失败&lt;/td&gt;&lt;td&gt;DNS 配置错误或被覆盖&lt;/td&gt;&lt;td&gt;确认 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 内容；使用方案 B 时检查 &lt;code&gt;ifcfg&lt;/code&gt; 中 &lt;code&gt;PEERDNS&lt;/code&gt; 设置&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ping&lt;/code&gt; IP 通但域名不通&lt;/td&gt;&lt;td&gt;IPv6 路由异常，请求优先走了无响应的 IPv6&lt;/td&gt;&lt;td&gt;尝试 &lt;code&gt;ping -4 baidu.com&lt;/code&gt; 确认，然后禁用 IPv6&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;重启后 IP 丢失&lt;/td&gt;&lt;td&gt;配置未持久化或 NM 重新接管&lt;/td&gt;&lt;td&gt;检查 &lt;code&gt;/etc/sysconfig/network-scripts/ifcfg-ens160&lt;/code&gt; 是否存在且内容正确；若用方案 A，确保 &lt;code&gt;ipv4.method&lt;/code&gt; 为 &lt;code&gt;manual&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;多虚拟机互相抢 IP&lt;/td&gt;&lt;td&gt;静态 IP 落在了 DHCP 池内&lt;/td&gt;&lt;td&gt;将静态 IP 调整至 DHCP 池外（如 &lt;code&gt;3~127&lt;/code&gt;）；除非所有虚拟机均已手工配置静态 IP，否则不要关闭 VMware DHCP 服务&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;</content:encoded><category>category:笔记</category><category>category:网络与架构</category><category>tag:虚拟机</category><category>tag:VMware</category></item><item><title>让手机连上热点直接使用主机的 Clash</title><link>https://youki.fun/ko/post/vm-use-host-clash</link><guid isPermaLink="false">ko:vm-use-host-clash</guid><description>让手机/虚拟机连 Windows 热点直接走 Clash TUN</description><pubDate>Fri, 06 Mar 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Windows 上实现&lt;strong&gt;透明网关/全局路由&lt;/strong&gt;的方案，核心思路是：&lt;strong&gt;让 Windows 热点的上游不是物理网卡，而是 Clash 的 TUN 虚拟网卡&lt;/strong&gt;。这样手机连上热点后无需任何代理设置，所有流量强制经过 TUN。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;原理&lt;a href=&quot;#原理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Windows 自带热点的默认逻辑是：热点网卡 → 物理网卡（WiFi/以太网）→ 互联网。&lt;br /&gt;
我们要改成：热点网卡 → &lt;strong&gt;Clash TUN 虚拟网卡&lt;/strong&gt; → 互联网。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;已知限制&lt;/strong&gt;：Clash Verge Rev 的 TUN 与 Windows ICS（网络共享）存在兼容性冲突，部分版本下同时开启会导致本机和热点设备都无法上网。以下方案在 Clash Verge Rev 2.5.1 上可行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;操作步骤&lt;a href=&quot;#操作步骤&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. 先开启 Clash Verge 的 TUN 模式&lt;a href=&quot;#1-先开启-clash-verge-的-tun-模式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;打开 Clash Verge → 设置 → 开启 &lt;strong&gt;TUN 模式&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确保已安装服务模式（Service Mode），TUN 网卡正常创建（设备管理器中能看到如 &lt;code&gt;Meta Tunnel&lt;/code&gt; 或 &lt;code&gt;Clash&lt;/code&gt; 字样的虚拟网卡）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确认电脑本机可以正常翻墙&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 再开启 Windows 移动热点&lt;a href=&quot;#2-再开启-windows-移动热点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;设置 → 网络和 Internet → 移动热点 → 开启&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;记住热点名称和密码，&lt;strong&gt;此时先不要连接手机&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 关键：在 TUN 网卡上配置 ICS 共享&lt;a href=&quot;#3-关键在-tun-网卡上配置-ics-共享&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;按 &lt;code&gt;Win + R&lt;/code&gt;，输入 &lt;code&gt;ncpa.cpl&lt;/code&gt; 回车，打开网络连接面板：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;找到 &lt;strong&gt;Clash 的 TUN 虚拟网卡&lt;/strong&gt;（名称通常包含 &lt;code&gt;Meta Tunnel&lt;/code&gt;、&lt;code&gt;Clash&lt;/code&gt;、&lt;code&gt;mihomo&lt;/code&gt; 或 &lt;code&gt;Wintun&lt;/code&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;右键 → &lt;strong&gt;属性&lt;/strong&gt; → 切换到 &lt;strong&gt;共享&lt;/strong&gt; 选项卡&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;勾选 &lt;strong&gt;“允许其他网络用户通过此计算机的 Internet 连接来连接”&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;strong&gt;“家庭网络连接”&lt;/strong&gt; 下拉框中，选择&lt;strong&gt;刚才开启热点生成的网卡&lt;/strong&gt;（通常叫 &lt;code&gt;本地连接*&lt;/code&gt;、&lt;code&gt;Microsoft Wi-Fi Direct Virtual Adapter&lt;/code&gt; 或 &lt;code&gt;Local Area Connection&lt;/code&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确定保存&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;顺序很重要&lt;/strong&gt;：必须是 &lt;strong&gt;先开 TUN → 再开热点 → 最后设置共享&lt;/strong&gt;。如果反过来，Windows 的 ICS 会与 TUN 冲突。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4. 手机连接热点&lt;a href=&quot;#4-手机连接热点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;手机直接连接电脑热点，&lt;strong&gt;不需要设置任何代理&lt;/strong&gt;（不要填 IP 和端口）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开浏览器测试，所有流量应已被 TUN 透明接管&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:网络与架构</category><category>tag:虚拟机</category><category>tag:Clash</category><category>tag:代理</category></item><item><title>Trap、中断与系统调用详解</title><link>https://youki.fun/ko/post/trap-interrupt-syscall</link><guid isPermaLink="false">ko:trap-interrupt-syscall</guid><description>Trap 与中断的区别、系统调用完整流程（从库函数到内核态返回）</description><pubDate>Sun, 22 Feb 2026 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;陷入 (Trap)&lt;a href=&quot;#陷入-trap&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;陷入是由&lt;strong&gt;程序主动执行&lt;/strong&gt;特定指令（如系统调用指令 &lt;code&gt;int 0x80&lt;/code&gt; 或 &lt;code&gt;syscall&lt;/code&gt;）触发的&lt;strong&gt;同步&lt;/strong&gt;事件。它让 CPU 从用户态切换到内核态，执行内核代码。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;strong&gt;同步&lt;/strong&gt;（由程序主动发起）、&lt;strong&gt;软件驱动&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目的&lt;/strong&gt;：实现用户程序向操作系统请求服务（如读写文件、创建进程）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;特权指令 (Privileged Instruction)&lt;a href=&quot;#特权指令-privileged-instruction&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;特权指令是只能在&lt;strong&gt;内核态&lt;/strong&gt;（最高权限）下执行的指令，如修改页表基址寄存器、开关中断等。如果在用户态执行这些指令，CPU 会触发一个&lt;strong&gt;保护异常&lt;/strong&gt;（属于中断的一种），阻止操作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;strong&gt;权限限制&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目的&lt;/strong&gt;：保护系统资源不被用户程序随意修改，保证系统安全。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;区别总结&lt;a href=&quot;#区别总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;维度&lt;/th&gt;&lt;th&gt;&lt;strong&gt;中断&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;陷入&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;特权指令&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;本质&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;事件（Event）&lt;/td&gt;&lt;td&gt;事件（Event）&lt;/td&gt;&lt;td&gt;指令（Instruction）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;触发源&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;硬件/异常&lt;/td&gt;&lt;td&gt;软件（程序）&lt;/td&gt;&lt;td&gt;程序（试图执行）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;同步性&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;异步&lt;/td&gt;&lt;td&gt;同步&lt;/td&gt;&lt;td&gt;同步&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;作用&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;响应外部变化&lt;/td&gt;&lt;td&gt;请求系统服务&lt;/td&gt;&lt;td&gt;管理硬件资源&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;关系与交互&lt;a href=&quot;#关系与交互&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;当用户程序需要执行特权操作（如读写磁盘）时，它不能直接执行特权指令，而是通过&lt;strong&gt;陷入&lt;/strong&gt;（系统调用）进入内核，由内核代为执行。&lt;/li&gt;
&lt;li&gt;如果用户程序&lt;strong&gt;非法执行&lt;/strong&gt;特权指令，会触发一个&lt;strong&gt;中断&lt;/strong&gt;（保护异常），导致程序被终止。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;触发陷入（进入内核）只需要执行一条特定的指令（如 &lt;code&gt;int 0x80&lt;/code&gt;, &lt;code&gt;syscall&lt;/code&gt;）。&lt;strong&gt;但&lt;/strong&gt;，这条指令只是一个“&lt;strong&gt;门铃&lt;/strong&gt;”，按下门铃后，执行的是操作系统内核中&lt;strong&gt;预先写好的、功能完整且复杂&lt;/strong&gt;的服务例程&lt;a href=&quot;#触发陷入进入内核只需要执行一条特定的指令如-int-0x80-syscall但这条指令只是一个门铃按下门铃后执行的是操作系统内核中预先写好的功能完整且复杂的服务例程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这是一个经典的交互流程，以“写文件”这个系统调用为例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 用户程序&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    write&lt;/span&gt;&lt;span&gt;(fd, buffer, size);&lt;/span&gt;&lt;span&gt; // 1. 调用库函数&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;发生的步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;库函数包装&lt;/strong&gt;：&lt;code&gt;write&lt;/code&gt; 是 C 库函数，它的&lt;strong&gt;核心&lt;/strong&gt;工作是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将系统调用号（标识是&lt;code&gt;write&lt;/code&gt;）、参数（&lt;code&gt;fd&lt;/code&gt;, &lt;code&gt;buffer&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;）放到约定好的寄存器或栈中。&lt;/li&gt;
&lt;li&gt;执行一条 &lt;strong&gt;&lt;code&gt;trap&lt;/code&gt; 指令&lt;/strong&gt;（如 &lt;code&gt;syscall&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;这条指令是用户态最后一条指令&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;陷入与切换&lt;/strong&gt;：CPU 执行 &lt;code&gt;syscall&lt;/code&gt; 指令，硬件自动完成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;切换到内核态。&lt;/li&gt;
&lt;li&gt;保存用户程序现场（寄存器、程序计数器等）。&lt;/li&gt;
&lt;li&gt;跳转到&lt;strong&gt;操作系统内核预先设定好的统一入口&lt;/strong&gt;（系统调用处理程序）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;内核执行复杂逻辑&lt;/strong&gt;：现在完全在内核态运行，操作系统开始工作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分发&lt;/strong&gt;：根据放在寄存器里的系统调用号，查表找到对应的服务函数，比如 &lt;code&gt;sys_write&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行核心服务&lt;/strong&gt;：&lt;code&gt;sys_write&lt;/code&gt; 是操作系统内核里一个复杂的函数，它可能要做：
&lt;ul&gt;
&lt;li&gt;参数检查（缓冲区是否有效？文件描述符是否合法？）。&lt;/li&gt;
&lt;li&gt;从用户态缓冲区拷贝数据到内核（因为内核不能直接操作用户内存）。&lt;/li&gt;
&lt;li&gt;文件系统操作：找到文件的 inode，检查权限。&lt;/li&gt;
&lt;li&gt;驱动交互：调用硬盘驱动程序，将数据写入缓存或直接写入磁盘。&lt;/li&gt;
&lt;li&gt;进程调度：如果需要等待 IO，可能会挂起当前进程，切换到其他进程运行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;所有这些，都是在内核态执行的特权代码，功能可以非常复杂。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;返回结果&lt;/strong&gt;：内核函数执行完毕后：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将返回值（成功写入的字节数或错误码）放入约定寄存器。&lt;/li&gt;
&lt;li&gt;执行一条特殊的&lt;strong&gt;返回指令&lt;/strong&gt;（如 &lt;code&gt;sysret&lt;/code&gt; 或 &lt;code&gt;iret&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;CPU 硬件恢复用户态现场，跳回用户程序 &lt;code&gt;syscall&lt;/code&gt; 指令之后继续执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;所以，核心思想是：&lt;/strong&gt;
&lt;strong&gt;&lt;code&gt;trap&lt;/code&gt; 指令只是一把钥匙，打开了通往内核的大门。门后是一个由操作系统构建的、拥有完整功能和最高权限的“新世界”。用户程序通过“钥匙”请求这个世界里的“居民”（内核函数）来为自己完成复杂的特权工作。&lt;/strong&gt;&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:计算机基础</category><category>tag:操作系统</category><category>tag:Trap</category><category>tag:中断</category><category>tag:系统调用</category></item><item><title>计算机组成原理笔记</title><link>https://youki.fun/ko/post/computer-organization</link><guid isPermaLink="false">ko:computer-organization</guid><description>计算机组成原理核心知识点总结：冯诺依曼架构、CPU组成、指令系统、补码运算、存储系统</description><pubDate>Thu, 19 Feb 2026 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1. 计算机系统概述&lt;a href=&quot;#1-计算机系统概述&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1.1 冯·诺依曼架构特点&lt;a href=&quot;#11-冯诺依曼架构特点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;冯诺依曼架构&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：存储程序，即将程序和数据存储在存储器中，CPU 按地址依次取出并执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;主要特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;五大部件&lt;/strong&gt;：运算器、控制器、存储器、输入设备、输出设备&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;以存储器为中心&lt;/strong&gt;：早期冯·诺依曼机以运算器为中心，现代计算机以存储器为中心&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指令和数据均用二进制表示&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指令和数据存储在同一存储器中&lt;/strong&gt;（区别于哈佛架构）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;1.2 软件与硬件的逻辑等价性&lt;a href=&quot;#12-软件与硬件的逻辑等价性&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;计算机软件分类&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;三个级别的语言&lt;/strong&gt;：&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;语言类型&lt;/th&gt;&lt;th&gt;特点&lt;/th&gt;&lt;th&gt;翻译工具&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;高级语言&lt;/td&gt;&lt;td&gt;接近自然语言，如 C、Python&lt;/td&gt;&lt;td&gt;编译程序（编译器）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;汇编语言&lt;/td&gt;&lt;td&gt;用助记符表示指令&lt;/td&gt;&lt;td&gt;汇编程序（汇编器）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;机器语言&lt;/td&gt;&lt;td&gt;二进制代码，CPU 直接执行&lt;/td&gt;&lt;td&gt;无需翻译&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;编译程序&lt;/strong&gt;：将高级语言一次性全部翻译成汇编语言或机器语言&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解释程序&lt;/strong&gt;：将高级语言翻译成机器语言（翻译一句执行一句）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ISA（指令集体系结构）&lt;/strong&gt;：定义一台计算机可以支持的指令，以及每条指令的作用&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 数据表示与运算&lt;a href=&quot;#2-数据表示与运算&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;2.1 原码、反码、补码&lt;a href=&quot;#21-原码反码补码&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;定点整数的编码方式&lt;/strong&gt;：&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;编码方式&lt;/th&gt;&lt;th&gt;正数&lt;/th&gt;&lt;th&gt;负数&lt;/th&gt;&lt;th&gt;特点&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;原码&lt;/td&gt;&lt;td&gt;符号位 0 + 真值绝对值&lt;/td&gt;&lt;td&gt;符号位 1 + 真值绝对值&lt;/td&gt;&lt;td&gt;有+0 和-0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;反码&lt;/td&gt;&lt;td&gt;与原码相同&lt;/td&gt;&lt;td&gt;符号位不变，数值位取反&lt;/td&gt;&lt;td&gt;有+0 和-0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;补码&lt;/td&gt;&lt;td&gt;与原码相同&lt;/td&gt;&lt;td&gt;反码+1&lt;/td&gt;&lt;td&gt;只有一个 0，范围更大&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;补码的优势&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;消除正负零&lt;/strong&gt;：原码和反码中+0 和-0 表示不同，补码中只有一个 0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加减法统一&lt;/strong&gt;：减法可以转化为加法运算&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表示范围扩大&lt;/strong&gt;：8 位补码可表示 -128 ~ +127&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;符号扩展&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正数扩展：高位补 0&lt;/li&gt;
&lt;li&gt;负数扩展：高位补 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 溢出判断&lt;a href=&quot;#22-溢出判断&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;溢出检测方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;单符号位法&lt;/strong&gt;：操作数符号相同，结果符号相反则溢出&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;双符号位法&lt;/strong&gt;：结果的两个符号位不同则溢出&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;3. CPU 组成与功能&lt;a href=&quot;#3-cpu-组成与功能&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;各硬件部件&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;3.1 运算器&lt;a href=&quot;#31-运算器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;部件&lt;/th&gt;&lt;th&gt;全称&lt;/th&gt;&lt;th&gt;功能&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ACC&lt;/td&gt;&lt;td&gt;累加计数器&lt;/td&gt;&lt;td&gt;存放操作数、运算结果&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MQ&lt;/td&gt;&lt;td&gt;乘商寄存器&lt;/td&gt;&lt;td&gt;乘法运算时存放乘数，除法时存放商&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;通用寄存器&lt;/td&gt;&lt;td&gt;存放操作数&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ALU&lt;/td&gt;&lt;td&gt;算术逻辑单元&lt;/td&gt;&lt;td&gt;实现各种算术运算、逻辑运算&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;3.2 控制器&lt;a href=&quot;#32-控制器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;部件&lt;/th&gt;&lt;th&gt;全称&lt;/th&gt;&lt;th&gt;功能&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;PC&lt;/td&gt;&lt;td&gt;程序计数器&lt;/td&gt;&lt;td&gt;存放下一条指令的地址&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IR&lt;/td&gt;&lt;td&gt;指令寄存器&lt;/td&gt;&lt;td&gt;存放当前正在执行的指令&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CU&lt;/td&gt;&lt;td&gt;控制单元&lt;/td&gt;&lt;td&gt;分析指令，给出控制信号&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;重要说明&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PC&lt;/strong&gt; 自动指向下一条指令的地址（取指后 PC+1）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IR&lt;/strong&gt; 保存当前正在执行的指令&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CU&lt;/strong&gt; 是控制器的核心，负责分析指令并产生控制信号&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 主存储器&lt;a href=&quot;#4-主存储器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;主存储器基本组成&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;4.1 基本组成&lt;a href=&quot;#41-基本组成&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;存储体&lt;/strong&gt;：数据在存储体内按地址存储&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键寄存器&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MAR（主存地址寄存器）&lt;/strong&gt;：用于存放要访问的存储单元的地址，位数反映存储单元的个数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MDR（主存数据寄存器）&lt;/strong&gt;：用于暂存从存储体读出或写入的数据，位数 = 存储字长&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;基本概念&lt;/strong&gt;：&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;概念&lt;/th&gt;&lt;th&gt;定义&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;存储单元&lt;/td&gt;&lt;td&gt;每个存储单元存放一串二进制代码&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;存储字&lt;/td&gt;&lt;td&gt;存储单元中二进制代码的组合&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;存储字长&lt;/td&gt;&lt;td&gt;存储单元中二进制代码的位数&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;存储元&lt;/td&gt;&lt;td&gt;存储二进制的电子元件，每个存储元可存 1bit&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MAR = 4 位 → 共有 2^4 = 16 个存储单元&lt;/li&gt;
&lt;li&gt;MDR = 16 位 → 每个存储单元可存放 16bit&lt;/li&gt;
&lt;li&gt;1 个字（word）= 16bit&lt;/li&gt;
&lt;li&gt;1 字节（Byte）= 8bit，1B = 1Byte，1b = 1bit&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 指令系统&lt;a href=&quot;#5-指令系统&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;5.1 指令格式&lt;a href=&quot;#51-指令格式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;指令由&lt;strong&gt;操作码&lt;/strong&gt;和&lt;strong&gt;地址码&lt;/strong&gt;组成：&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;字段&lt;/th&gt;&lt;th&gt;内容&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;操作码（OP）&lt;/td&gt;&lt;td&gt;指明指令的操作类型（如加法、传送）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;地址码（Ad）&lt;/td&gt;&lt;td&gt;指明操作数的地址或下一条指令的地址&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;地址码的结构&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;| 操作码 | 地址码1 | 地址码2 | 地址码3 | 地址码4 |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 寻址方式&lt;a href=&quot;#52-寻址方式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;寻址方式&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;th&gt;优点&lt;/th&gt;&lt;th&gt;缺点&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;立即寻址&lt;/td&gt;&lt;td&gt;地址码即为操作数&lt;/td&gt;&lt;td&gt;速度快&lt;/td&gt;&lt;td&gt;地址码位数限制操作数范围&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;直接寻址&lt;/td&gt;&lt;td&gt;地址码给出操作数的内存地址&lt;/td&gt;&lt;td&gt;简单&lt;/td&gt;&lt;td&gt;地址码位数限制寻址范围&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;间接寻址&lt;/td&gt;&lt;td&gt;地址码给出的内存地址中存放的是操作数的地址&lt;/td&gt;&lt;td&gt;寻址范围大&lt;/td&gt;&lt;td&gt;访存次数多，速度慢&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;寄存器寻址&lt;/td&gt;&lt;td&gt;地址码给出寄存器编号&lt;/td&gt;&lt;td&gt;速度快&lt;/td&gt;&lt;td&gt;寄存器数量有限&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;寄存器间接寻址&lt;/td&gt;&lt;td&gt;地址码给出的寄存器中存放的是操作数的地址&lt;/td&gt;&lt;td&gt;寻址范围大&lt;/td&gt;&lt;td&gt;需要访存&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;6. CPU 工作过程&lt;a href=&quot;#6-cpu-工作过程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;6.1 指令执行流程&lt;a href=&quot;#61-指令执行流程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;CPU工作过程&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;指令执行的基本步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;取指周期&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(PC) → MAR：将程序计数器的内容送到主存地址寄存器&lt;/li&gt;
&lt;li&gt;M(MAR) → MDR：从存储体读取指令到主存数据寄存器&lt;/li&gt;
&lt;li&gt;(MDR) → IR：将指令送到指令寄存器&lt;/li&gt;
&lt;li&gt;OP(IR) → CU：将操作码送到控制单元分析&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;执行周期&lt;/strong&gt;（以取数指令为例）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ad(IR) → MAR：将地址码送到主存地址寄存器&lt;/li&gt;
&lt;li&gt;M(MAR) → MDR：从存储体读取数据&lt;/li&gt;
&lt;li&gt;(MDR) → ACC：将数据送到累加器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;6.2 取数指令详解&lt;a href=&quot;#62-取数指令详解&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;取数指令执行&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;“取数”指令的执行&lt;/strong&gt;（从主存指定地址处取数）：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;取指周期（必经步骤）：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (PC) → MAR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    M(MAR) → MDR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (MDR) → IR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (PC) + 1 → PC&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    OP(IR) → CU&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;执行周期（不同指令具体步骤不同）：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Ad(IR) → MAR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    M(MAR) → MDR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (MDR) → ACC&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;取指周期&lt;/strong&gt;：所有指令都必须经历的阶段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行周期&lt;/strong&gt;：根据指令类型不同，具体操作不同&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PC+1&lt;/strong&gt;：在取指周期结束时自动执行，指向下一条指令&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.3 乘法指令执行&lt;a href=&quot;#63-乘法指令执行&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;乘法指令执行&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;乘法指令的执行过程&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;取指周期：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (PC) → MAR → MDR → IR → CU&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;执行周期：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Ad(IR) → MAR → MDR → MQ    # 取乘数到MQ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (ACC) × (MQ) → ACC          # 乘法运算&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    结果存入主存单元&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：乘法运算结果可能超出 ACC 的位数，需要 MQ 辅助存储。&lt;/p&gt;
&lt;h3&gt;6.4 指令周期&lt;a href=&quot;#64-指令周期&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CPU 区分指令和数据的依据&lt;/strong&gt;：指令周期的不同阶段&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;阶段&lt;/th&gt;&lt;th&gt;取出的内容&lt;/th&gt;&lt;th&gt;用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;取指周期&lt;/td&gt;&lt;td&gt;指令&lt;/td&gt;&lt;td&gt;送到 IR 分析&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;执行周期&lt;/td&gt;&lt;td&gt;数据&lt;/td&gt;&lt;td&gt;送到 ACC 或其他寄存器&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;指令周期的组成&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;取指周期&lt;/strong&gt;：从存储器取出指令&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;间址周期&lt;/strong&gt;（可选）：如果指令使用间接寻址&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行周期&lt;/strong&gt;：执行指令的操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中断周期&lt;/strong&gt;（可选）：处理中断请求&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 总结&lt;a href=&quot;#7-总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;7.1 核心知识点&lt;a href=&quot;#71-核心知识点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;冯·诺依曼架构&lt;/strong&gt;：存储程序，五大部件，以存储器为中心&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU 组成&lt;/strong&gt;：运算器（ACC、MQ、X、ALU）+ 控制器（PC、IR、CU）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储系统&lt;/strong&gt;：MAR（地址）、MDR（数据）、存储体&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指令系统&lt;/strong&gt;：操作码 + 地址码，多种寻址方式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指令周期&lt;/strong&gt;：取指周期 → 执行周期，PC 自动+1&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7.2 易错点&lt;a href=&quot;#72-易错点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PC&lt;/strong&gt; 存放下一条指令的地址，不是当前指令的地址&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IR&lt;/strong&gt; 保存当前正在执行的指令&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MAR&lt;/strong&gt; 的位数决定存储单元的个数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MDR&lt;/strong&gt; 的位数等于存储字长&lt;/li&gt;
&lt;li&gt;取指周期取出的是&lt;strong&gt;指令&lt;/strong&gt;，执行周期取出的是&lt;strong&gt;数据&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:计算机基础</category><category>tag:计算机组成</category><category>tag:考研408</category></item><item><title>李宏毅机器学习笔记：线性模型与梯度下降</title><link>https://youki.fun/ko/post/linear-model-basics</link><guid isPermaLink="false">ko:linear-model-basics</guid><description>李宏毅 ML/DL 课程第一节课笔记：线性模型、Loss、梯度下降、Hard Sigmoid、激活函数到深度学习</description><pubDate>Sat, 27 Dec 2025 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文是&lt;a href=&quot;https://www.bilibili.com/video/BV1TAtwzTE1S?p=2&quot;&gt;李宏毅机器学习深度学习系列课程&lt;/a&gt;第一节课的笔记，从最简单的线性模型一路串到深度学习。&lt;/p&gt;
&lt;h2&gt;第一节课 基本概念&lt;a href=&quot;#第一节课-基本概念&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. 最简单的模型：线性模型&lt;a href=&quot;#1-最简单的模型线性模型&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我们要预测的值记为 &lt;code&gt;y&lt;/code&gt;，最简单的预测方式是假设它和某个特征 &lt;code&gt;x&lt;/code&gt; 成线性关系：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;y = b + w * x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;b&lt;/code&gt;：偏置（bias）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;w&lt;/code&gt;：权重（weight）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x&lt;/code&gt;：输入特征（下标 &lt;code&gt;x_1&lt;/code&gt; 表示第一个特征）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Loss：衡量一组参数好不好&lt;a href=&quot;#2-loss衡量一组参数好不好&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;有了模型，还需要一个标准来评判当前的 &lt;code&gt;w&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt; 好不好，这个标准就是 &lt;strong&gt;Loss（损失函数）&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;label&lt;/strong&gt;：正确的值（真实标签）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;计算误差的方式&lt;/strong&gt;：把模型预测值和 label 做差，累加起来（例如 MAE：&lt;code&gt;e = |ŷ - y|&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;计算误差的方式&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Loss 越小，说明这组 &lt;code&gt;w&lt;/code&gt;、&lt;code&gt;b&lt;/code&gt; 越好。&lt;strong&gt;要找到最小的 Loss，就是找到最优的 &lt;code&gt;w&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt;，方法是梯度下降（Gradient Descent）。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;3. 梯度下降：怎么更新参数&lt;a href=&quot;#3-梯度下降怎么更新参数&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;梯度下降示意&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;参数更新过程&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;梯度下降的核心思路：算出 Loss 对每个参数的梯度（偏导），然后让参数&lt;strong&gt;沿着梯度的反方向&lt;/strong&gt;走一小步，Loss 就会下降一点。反复迭代，直到 Loss 不再明显下降。上图展示的就是这个”更新参数”的过程。&lt;/p&gt;
&lt;h3&gt;4. 用更复杂的模型拟合真实数据&lt;a href=&quot;#4-用更复杂的模型拟合真实数据&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;线性模型（一条直线）往往不足以描述真实数据。结合真实情况，需要改进模型，让它具备表达非线性关系的能力。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;结合真实情况改进模型&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;这就引出了下面要讲的 &lt;strong&gt;linear model 的扩展&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;5. Linear Model 的扩展：用蓝色折线拼出红色曲线&lt;a href=&quot;#5-linear-model-的扩展用蓝色折线拼出红色曲线&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;模型名仍叫 &lt;strong&gt;linear model&lt;/strong&gt;，但思路变了：&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;线性模型扩展&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;蓝色折线拼红色曲线&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;核心想法是：&lt;strong&gt;用多段蓝色的折线（分段线性函数）组合起来，逼近任意形状的红色曲线&lt;/strong&gt;。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;蓝色折线逼近&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;这种蓝色折线段的数学名字叫 &lt;strong&gt;Hard Sigmoid&lt;/strong&gt;——形状像一个拉长的 S。&lt;/p&gt;
&lt;h3&gt;6. Hard Sigmoid 与激活函数&lt;a href=&quot;#6-hard-sigmoid-与激活函数&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;Hard Sigmoid&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Hard Sigmoid 可以用一个连续函数 &lt;strong&gt;Sigmoid&lt;/strong&gt; 来近似：&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;Sigmoid 近似&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;于是每一小段蓝色曲线可以写成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;y = c / (1 + exp(-(b + w * x)))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    = c * sigmoid(b + w * x)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;Sigmoid 公式&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h4&gt;问：为什么这里 &lt;code&gt;c&lt;/code&gt; 要转置？&lt;a href=&quot;#问为什么这里-c-要转置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;公式里的 &lt;code&gt;c&lt;/code&gt;（以及 &lt;code&gt;b&lt;/code&gt;、&lt;code&gt;w&lt;/code&gt;）在多个特征/多段曲线堆叠时会变成向量/矩阵，写成 &lt;code&gt;c^T&lt;/code&gt; 是为了&lt;strong&gt;把行向量转置成列向量&lt;/strong&gt;，方便和后面的未知向量做矩阵乘法、合并起来。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;转置与合并&lt;/figcaption&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;这一步是在&lt;strong&gt;合并未知向量&lt;/strong&gt;：把多段曲线的参数统一写成一个矩阵运算 &lt;code&gt;y = b + c^T * sigmoid(b + w * x)&lt;/code&gt;，形式更紧凑，也便于后续推广到多层网络。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;7. 多段曲线堆叠：还是用梯度下降更新参数&lt;a href=&quot;#7-多段曲线堆叠还是用梯度下降更新参数&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;把多个 Hard Sigmoid 加起来后，未知参数变多了（每段都有自己的 &lt;code&gt;c&lt;/code&gt;、&lt;code&gt;b&lt;/code&gt;、&lt;code&gt;w&lt;/code&gt;），但&lt;strong&gt;更新方法不变&lt;/strong&gt;——还是梯度下降，只是参数更多。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;多段曲线更新参数&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;8. Batch 与 Epoch：分段处理数据&lt;a href=&quot;#8-batch-与-epoch分段处理数据&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;把一整份训练数据&lt;strong&gt;分成多段（batch）逐段计算并更新参数&lt;/strong&gt;。相关的两个概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;batch&lt;/strong&gt;：一次取一小批样本算梯度、更新一次参数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;epoch&lt;/strong&gt;：把所有训练样本都过一遍叫一个 epoch。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这个概念在 YOLO 等模型的训练里也会用到，是深度学习训练的通用约定。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;Batch 与 Epoch&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;9. 两种激活函数&lt;a href=&quot;#9-两种激活函数&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;除了 Sigmoid，还有另一种常用的激活函数。课程里对比了两种：&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;两种激活函数&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;（Sigmoid 和 ReLU，后者在现代网络里更常用，因为计算简单、收敛快。）&lt;/p&gt;
&lt;h3&gt;10. 把结构叠多层：Neural Network → Deep Learning&lt;a href=&quot;#10-把结构叠多层neural-network--deep-learning&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;上面这种”输入 → 一组激活函数 → 输出”的结构可以&lt;strong&gt;多次重复堆叠（layers）&lt;/strong&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;多层堆叠&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;单个这种计算单元叫 &lt;strong&gt;neuron（神经元）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;把很多 neuron 组织起来就叫 &lt;strong&gt;neural network（神经网络）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;当层数很多时，换了个名字：&lt;strong&gt;Many layers means Deep → Deep Learning（深度学习）&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这节课从 &lt;code&gt;y = b + w*x&lt;/code&gt; 出发，一步步展示了：怎么用 Loss 衡量模型好坏、怎么用梯度下降找最优参数、怎么用 Hard Sigmoid/Sigmoid 把线性模型扩展成能拟合复杂曲线的模型，最后通过堆叠 layers 自然引出了神经网络和深度学习。后续课程会在这个基础上讲网络结构、反向传播和具体任务。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:AI</category><category>tag:机器学习</category><category>tag:深度学习</category><category>tag:梯度下降</category></item><item><title>二叉树基础笔记</title><link>https://youki.fun/ko/post/binary-tree</link><guid isPermaLink="false">ko:binary-tree</guid><description>二叉树的顺序存储、链式存储、线索二叉树详解</description><pubDate>Mon, 15 Dec 2025 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文整理二叉树的存储方式和线索二叉树的核心知识点。&lt;/p&gt;

&lt;h2&gt;1. 二叉树的存储&lt;a href=&quot;#1-二叉树的存储&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1.1 顺序存储&lt;a href=&quot;#11-顺序存储&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;顺序存储使用&lt;strong&gt;数组&lt;/strong&gt;存放完全二叉树，通过下标表示结点关系。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;顺序存储&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;编号规则&lt;/strong&gt;（从 1 开始）：&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;结点&lt;/th&gt;&lt;th&gt;公式&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;左孩子&lt;/td&gt;&lt;td&gt;&lt;code&gt;2i&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;右孩子&lt;/td&gt;&lt;td&gt;&lt;code&gt;2i + 1&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;父亲&lt;/td&gt;&lt;td&gt;&lt;code&gt;⌊i/2⌋&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：适合&lt;strong&gt;完全二叉树&lt;/strong&gt;，空间利用率高；非完全二叉树会浪费空间。&lt;/p&gt;
&lt;h3&gt;1.2 链式存储（二叉链表）&lt;a href=&quot;#12-链式存储二叉链表&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;链式存储使用&lt;strong&gt;指针&lt;/strong&gt;连接结点，每个结点包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt;：数据域&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lchild&lt;/code&gt;：左孩子指针&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rchild&lt;/code&gt;：右孩子指针&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;链式存储&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;重要规律&lt;/strong&gt;：空指针个数 = 结点数 + 1&lt;/p&gt;
&lt;h3&gt;1.3 三叉链表&lt;a href=&quot;#13-三叉链表&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在二叉链表基础上增加 &lt;code&gt;parent&lt;/code&gt; 指针，方便向上查找。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;三叉链表&lt;/figcaption&gt;&lt;/figure&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;类型&lt;/th&gt;&lt;th&gt;指针&lt;/th&gt;&lt;th&gt;特点&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;二叉链表&lt;/td&gt;&lt;td&gt;lchild, rchild&lt;/td&gt;&lt;td&gt;只能向下查找&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;三叉链表&lt;/td&gt;&lt;td&gt;lchild, rchild, parent&lt;/td&gt;&lt;td&gt;可双向查找&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;2. 链式存储代码实现&lt;a href=&quot;#2-链式存储代码实现&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;2.1 C++ 写法&lt;a href=&quot;#21-c-写法&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;struct&lt;/span&gt;&lt;span&gt; BTNode&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    char&lt;/span&gt;&lt;span&gt; data;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    BTNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;lchild;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    BTNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;rchild;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;C++写法&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;2.2 C 语言写法（typedef）&lt;a href=&quot;#22-c-语言写法typedef&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;使用 &lt;code&gt;typedef&lt;/code&gt; 给结构体起别名，简化声明：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;typedef&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; BTNode {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    char&lt;/span&gt;&lt;span&gt; data;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    struct&lt;/span&gt;&lt;span&gt; BTNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;lchild, &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;rchild;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} BTNode;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 使用时不需要加 struct 前缀&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;BTNode a, &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;d;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;BTNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;b &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (BTNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;malloc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sizeof&lt;/span&gt;&lt;span&gt;(BTNode));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;C写法&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;typedef详解&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;3. 线索二叉树&lt;a href=&quot;#3-线索二叉树&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;3.1 为什么需要线索化&lt;a href=&quot;#31-为什么需要线索化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;二叉链表中存在大量空指针（结点数+1 个），造成浪费。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;线索化&lt;/strong&gt;：利用空指针存储&lt;strong&gt;前驱/后继&lt;/strong&gt;信息，提高遍历效率。&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;线索化原理&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;3.2 线索化规则&lt;a href=&quot;#32-线索化规则&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;空指针&lt;/th&gt;&lt;th&gt;指向&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;左空指针&lt;/td&gt;&lt;td&gt;前驱结点&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;右空指针&lt;/td&gt;&lt;td&gt;后继结点&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;3.3 线索标志位&lt;a href=&quot;#33-线索标志位&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如何区分指针指向的是&lt;strong&gt;孩子&lt;/strong&gt;还是&lt;strong&gt;线索&lt;/strong&gt;？增加 &lt;code&gt;ltag&lt;/code&gt; 和 &lt;code&gt;rtag&lt;/code&gt; 标志位：&lt;/p&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;线索标志&lt;/figcaption&gt;&lt;/figure&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;标志位&lt;/th&gt;&lt;th&gt;值&lt;/th&gt;&lt;th&gt;含义&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ltag/rtag&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;指向左/右孩子&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ltag/rtag&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;指向前驱/后继（线索）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;结点结构&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;typedef&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; BTNode {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    char&lt;/span&gt;&lt;span&gt; data;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    struct&lt;/span&gt;&lt;span&gt; BTNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;lchild, &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;rchild;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    int&lt;/span&gt;&lt;span&gt; ltag, rtag;&lt;/span&gt;&lt;span&gt;  // 线索标志&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} BTNode;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 中序线索二叉树示例&lt;a href=&quot;#34-中序线索二叉树示例&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img /&gt;&lt;figcaption&gt;中序线索二叉树&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;对二叉树进行中序线索化后：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中序序列：B → D → A → C&lt;/li&gt;
&lt;li&gt;空指针被利用为前驱/后继线索&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 总结&lt;a href=&quot;#4-总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;存储方式&lt;/th&gt;&lt;th&gt;优点&lt;/th&gt;&lt;th&gt;缺点&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;顺序存储&lt;/td&gt;&lt;td&gt;访问简单，适合完全二叉树&lt;/td&gt;&lt;td&gt;非完全树浪费空间&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;二叉链表&lt;/td&gt;&lt;td&gt;灵活，节省空间&lt;/td&gt;&lt;td&gt;空指针浪费&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;三叉链表&lt;/td&gt;&lt;td&gt;可双向查找&lt;/td&gt;&lt;td&gt;多一个指针开销&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;线索二叉树&lt;/td&gt;&lt;td&gt;利用空指针，遍历高效&lt;/td&gt;&lt;td&gt;结构复杂&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;空指针个数 = 结点数 + 1&lt;/li&gt;
&lt;li&gt;线索化利用空指针存储前驱/后继&lt;/li&gt;
&lt;li&gt;ltag/rtag 用于区分孩子和线索&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:计算机基础</category><category>tag:数据结构</category><category>tag:二叉树</category><category>tag:考研408</category></item><item><title>链队列实现</title><link>https://youki.fun/ko/post/queue-and-array</link><guid isPermaLink="false">ko:queue-and-array</guid><description>带头结点的链队列 C 语言实现：初始化、入队、出队、判空与释放，附二级指针用法详解</description><pubDate>Thu, 13 Nov 2025 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;队列是一种**先进先出（FIFO）**的线性数据结构，只允许在一端（队尾）插入、另一端（队头）删除。链队列用带头结点的单链表实现，相比顺序队列（循环数组）不会存在假溢出问题，空间按需分配。&lt;/p&gt;
&lt;h2&gt;数据结构定义&lt;a href=&quot;#数据结构定义&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;#include&lt;/span&gt;&lt;span&gt; &amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#include&lt;/span&gt;&lt;span&gt; &amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#include&lt;/span&gt;&lt;span&gt; &amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;typedef&lt;/span&gt;&lt;span&gt; char&lt;/span&gt;&lt;span&gt; DataType;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;typedef&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; LQNode {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    DataType data;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    struct&lt;/span&gt;&lt;span&gt; LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;next;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} LQNode, &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;LinkedQNode;&lt;/span&gt;&lt;span&gt; // 链队列结点的类型&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;typedef&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    struct&lt;/span&gt;&lt;span&gt; LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;front, &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;rear;&lt;/span&gt;&lt;span&gt; // 头指针和尾指针&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} LQueue, &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;LinkedQueue;&lt;/span&gt;&lt;span&gt; // 将头、尾指针封装在一起的链队列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// [算法 3.21]    创建一个带头结点的空队列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; QueueInit&lt;/span&gt;&lt;span&gt;(LinkedQueue &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;span&gt;  //构造一个空队列 Q&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    *&lt;/span&gt;&lt;span&gt;Q &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (LinkedQueue)&lt;/span&gt;&lt;span&gt;malloc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sizeof&lt;/span&gt;&lt;span&gt;(LQueue));&lt;/span&gt;&lt;span&gt; // 分配队列结构体空间&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; // 分配失败&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;front &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (LQNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;malloc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sizeof&lt;/span&gt;&lt;span&gt;(LQNode));&lt;/span&gt;&lt;span&gt; // 分配头结点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;front)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        free&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        *&lt;/span&gt;&lt;span&gt;Q &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;front-&amp;gt;next &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; // 头结点next置空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;rear &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;front;&lt;/span&gt;&lt;span&gt; // front和rear都指向头结点，表示队列为空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// [算法 3.22]    入队列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;bool&lt;/span&gt;&lt;span&gt; QueueIn&lt;/span&gt;&lt;span&gt;(LinkedQueue &lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;, DataType &lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;span&gt;  //插入元素 x 为 Q 的队尾元素&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;Q) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;node &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (LQNode&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;malloc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sizeof&lt;/span&gt;&lt;span&gt;(LQNode));&lt;/span&gt;&lt;span&gt; // 新建结点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;node) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    node-&amp;gt;data &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; x;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    node-&amp;gt;next &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Q-&amp;gt;rear-&amp;gt;next &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; node;&lt;/span&gt;&lt;span&gt; // 原队尾结点next指向新结点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Q-&amp;gt;rear &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; node;&lt;/span&gt;&lt;span&gt; // rear指向新结点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// [算法 3.23]    出队列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;bool&lt;/span&gt;&lt;span&gt; QueueOut&lt;/span&gt;&lt;span&gt; (LinkedQueue &lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;, DataType &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;){&lt;/span&gt;&lt;span&gt;  //若队列不空 ,则删除 Q 的队头元素&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;Q &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; Q-&amp;gt;front &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; Q-&amp;gt;rear) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; // 队空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;p &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Q-&amp;gt;front-&amp;gt;next;&lt;/span&gt;&lt;span&gt; // 队头元素&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    *&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p-&amp;gt;data;&lt;/span&gt;&lt;span&gt; // 取数据&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Q-&amp;gt;front-&amp;gt;next &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p-&amp;gt;next;&lt;/span&gt;&lt;span&gt; // 头结点next指向下一个结点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (Q-&amp;gt;rear &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; p) Q-&amp;gt;rear &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Q-&amp;gt;front;&lt;/span&gt;&lt;span&gt; // 若出队后队空，rear回到头结点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    free&lt;/span&gt;&lt;span&gt;(p);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// [算法 3.24]    判断队列是否为空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;bool&lt;/span&gt;&lt;span&gt; QueueEmpty&lt;/span&gt;&lt;span&gt;(LinkedQueue &lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;span&gt;  //判断队列 Q 是否为空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;Q) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;            // 队列指针为空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; Q-&amp;gt;front &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; Q-&amp;gt;rear;&lt;/span&gt;&lt;span&gt;     // 头尾指针相遇即为空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//清空队列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; Empty&lt;/span&gt;&lt;span&gt;(LinkedQueue &lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    while&lt;/span&gt;&lt;span&gt; (Q-&amp;gt;front &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; Q-&amp;gt;rear)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; p &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Q-&amp;gt;front-&amp;gt;next;&lt;/span&gt;&lt;span&gt;    //取得队列第一个元素指针&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        Q-&amp;gt;front-&amp;gt;next &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p-&amp;gt;next;&lt;/span&gt;&lt;span&gt;   //修改队头指针指向&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        free&lt;/span&gt;&lt;span&gt;(p);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt;(Q-&amp;gt;front-&amp;gt;next &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; //只有一个元素时 ,出队后队空 ,此时还要修改队尾指针&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Q-&amp;gt;rear &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Q-&amp;gt;front;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 释放队列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; FreeQueue&lt;/span&gt;&lt;span&gt;(LinkedQueue &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; // 如果队列为空，直接返回&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; current &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;front;&lt;/span&gt;&lt;span&gt; // 从头节点开始&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; temp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    while&lt;/span&gt;&lt;span&gt; (current &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        temp &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; current;&lt;/span&gt;&lt;span&gt; // 保存当前节点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        current &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; current-&amp;gt;next;&lt;/span&gt;&lt;span&gt; // 移动到下一个节点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        free&lt;/span&gt;&lt;span&gt;(temp);&lt;/span&gt;&lt;span&gt; // 释放当前节点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    free&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q);&lt;/span&gt;&lt;span&gt; // 最后释放队列结构体本身&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    *&lt;/span&gt;&lt;span&gt;Q &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; // 将队列指针设为NULL，防止悬挂指针&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//获取队首元素&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; GetQueue&lt;/span&gt;&lt;span&gt;(LinkedQueue &lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (Q-&amp;gt;front &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;None&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    else&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;%c\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, Q-&amp;gt;front-&amp;gt;next-&amp;gt;data);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    DataType c,x;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    LinkedQueue Q &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    QueueInit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;Q);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        scanf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot; &lt;/span&gt;&lt;span&gt;%c&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;c);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (c &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &apos;E&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            scanf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot; &lt;/span&gt;&lt;span&gt;%c&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;x);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            QueueIn&lt;/span&gt;&lt;span&gt;(Q, x);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (c &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &apos;C&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Empty&lt;/span&gt;&lt;span&gt;(Q);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (c &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &apos;G&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            GetQueue&lt;/span&gt;&lt;span&gt;(Q);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (c &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &apos;D&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            //dequeue(m);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            if&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;QueueOut&lt;/span&gt;&lt;span&gt;(Q, &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;x))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;             printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;%c\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, x);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          else&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;             printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;None&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (c &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &apos;Q&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            break&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    FreeQueue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;Q);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里详细讲一下 &lt;code&gt;(*Q)-&amp;gt;&lt;/code&gt; 的含义和作用：&lt;/p&gt;
&lt;h2&gt;二级指针解引用 &lt;code&gt;(*Q)-&amp;gt;&lt;/code&gt;&lt;a href=&quot;#二级指针解引用-q-&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;参数类型&lt;/strong&gt;&lt;br /&gt;
&lt;code&gt;QueueInit(LinkedQueue *Q)&lt;/code&gt; 里的 &lt;code&gt;Q&lt;/code&gt; 是“指向队列指针的指针”。&lt;br /&gt;
也就是：&lt;code&gt;LinkedQueue&lt;/code&gt; 是 &lt;code&gt;LQueue *&lt;/code&gt;，&lt;code&gt;LinkedQueue *Q&lt;/code&gt; 就是 &lt;code&gt;LQueue **Q&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;解引用&lt;/strong&gt;&lt;br /&gt;
&lt;code&gt;*Q&lt;/code&gt; 表示“取出 Q 指向的内容”，也就是实际的队列指针（&lt;code&gt;LQueue *&lt;/code&gt;）。&lt;br /&gt;
例如：&lt;code&gt;*Q = malloc(...)&lt;/code&gt;，就是让外部的队列指针指向新分配的队列结构体。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;访问结构体成员&lt;/strong&gt;&lt;br /&gt;
&lt;code&gt;(*Q)-&amp;gt;front&lt;/code&gt; 表示“队列的头指针”。&lt;br /&gt;
因为 &lt;code&gt;*Q&lt;/code&gt; 是 &lt;code&gt;LQueue *&lt;/code&gt;，所以用 &lt;code&gt;-&amp;gt;&lt;/code&gt; 访问结构体成员。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;为什么不用 Q-&amp;gt;front&lt;/strong&gt;&lt;br /&gt;
如果直接用 &lt;code&gt;Q-&amp;gt;front&lt;/code&gt;，那 Q 必须是 &lt;code&gt;LQueue *&lt;/code&gt;，而不是 &lt;code&gt;LQueue **&lt;/code&gt;。&lt;br /&gt;
但这里 Q 是指向指针的指针，所以要先解引用，再访问成员。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;举例说明：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; QueueInit&lt;/span&gt;&lt;span&gt;(LinkedQueue &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    *&lt;/span&gt;&lt;span&gt;Q &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (LinkedQueue)&lt;/span&gt;&lt;span&gt;malloc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sizeof&lt;/span&gt;&lt;span&gt;(LQueue));&lt;/span&gt;&lt;span&gt; // 分配队列结构体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;front &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;rear &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (LQNode &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;malloc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sizeof&lt;/span&gt;&lt;span&gt;(LQNode));&lt;/span&gt;&lt;span&gt; // 分配头结点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;Q)-&amp;gt;front-&amp;gt;next &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NULL&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; // 头结点next置空&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*Q&lt;/code&gt;：拿到实际的队列指针&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(*Q)-&amp;gt;front&lt;/code&gt;：访问队列的头指针&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(*Q)-&amp;gt;rear&lt;/code&gt;：访问队列的尾指针&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;：&lt;code&gt;(*Q)-&amp;gt;&lt;/code&gt; 是先解引用拿到队列指针，再用 &lt;code&gt;-&amp;gt;&lt;/code&gt; 访问结构体成员。这样才能正确初始化和操作队列。&lt;/p&gt;
&lt;h2&gt;用指针返回出队值 &lt;code&gt;*x = p-&amp;gt;data&lt;/code&gt;&lt;a href=&quot;#用指针返回出队值-x--p-data&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;出队函数签名是 &lt;code&gt;QueueOut(LinkedQueue Q, DataType *x)&lt;/code&gt;，其中 &lt;code&gt;*x = p-&amp;gt;data;&lt;/code&gt; 这一行&lt;strong&gt;不带 &lt;code&gt;*&lt;/code&gt; 不行&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;原因是：要把出队的元素值“带出来”给主程序用，必须用&lt;strong&gt;指针参数&lt;/strong&gt;（&lt;code&gt;DataType *x&lt;/code&gt;），在函数里通过 &lt;code&gt;*x = p-&amp;gt;data;&lt;/code&gt; 赋值。如果只用值参数（如 &lt;code&gt;DataType x&lt;/code&gt;），函数内部的赋值不会影响主程序变量，主程序拿不到出队的值。这就是 C 语言“用指针返回结果”的常规做法——和 &lt;code&gt;scanf(&quot;%d&quot;, &amp;amp;n)&lt;/code&gt; 传 &lt;code&gt;&amp;amp;n&lt;/code&gt; 一个道理。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:计算机基础</category><category>tag:数据结构</category><category>tag:C语言</category><category>tag:队列</category></item><item><title>Git 使用指南</title><link>https://youki.fun/ko/post/git-usage-guide</link><guid isPermaLink="false">ko:git-usage-guide</guid><description>Git 安装配置、基础工作流、分支管理、常用技巧速查</description><pubDate>Thu, 13 Nov 2025 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;安装与配置&lt;a href=&quot;#安装与配置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;安装&lt;a href=&quot;#安装&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Windows：&lt;a href=&quot;https://git-scm.com/download/win&quot;&gt;git-scm.com&lt;/a&gt; 下载安装包，按向导完成&lt;/li&gt;
&lt;li&gt;macOS：&lt;code&gt;brew install git&lt;/code&gt; 或安装 Xcode Command Line Tools&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;用户信息配置&lt;a href=&quot;#用户信息配置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span&gt; --global&lt;/span&gt;&lt;span&gt; user.name&lt;/span&gt;&lt;span&gt; &quot;你的名字&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span&gt; --global&lt;/span&gt;&lt;span&gt; user.email&lt;/span&gt;&lt;span&gt; &quot;你的邮箱&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SSH Key 配置&lt;a href=&quot;#ssh-key-配置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 生成密钥对&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ssh-keygen&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; ed25519&lt;/span&gt;&lt;span&gt; -C&lt;/span&gt;&lt;span&gt; &quot;你的邮箱&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 查看公钥，复制到 GitHub/GitLab 的 SSH Keys 设置中&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; ~/.ssh/id_ed25519.pub&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 测试连接&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; -T&lt;/span&gt;&lt;span&gt; git@github.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;基础工作流&lt;a href=&quot;#基础工作流&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 初始化仓库&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; init&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 克隆远程仓库&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; clone&lt;/span&gt;&lt;span&gt; git@github.com:user/repo.git&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 查看状态&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; status&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 添加到暂存区&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; add&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;                # 所有文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; add&lt;/span&gt;&lt;span&gt; file.txt&lt;/span&gt;&lt;span&gt;         # 指定文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 提交&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; commit&lt;/span&gt;&lt;span&gt; -m&lt;/span&gt;&lt;span&gt; &quot;提交说明&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 推送到远程&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; push&lt;/span&gt;&lt;span&gt; origin&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 拉取远程更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; pull&lt;/span&gt;&lt;span&gt; origin&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 查看提交历史&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; log&lt;/span&gt;&lt;span&gt; --oneline&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;分支管理&lt;a href=&quot;#分支管理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 查看分支&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; branch&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 创建并切换分支&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; checkout&lt;/span&gt;&lt;span&gt; -b&lt;/span&gt;&lt;span&gt; feature/xxx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 或用新版命令&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; switch&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;span&gt; feature/xxx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 切换分支&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; checkout&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 合并分支（在 main 上合并 feature）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; merge&lt;/span&gt;&lt;span&gt; feature/xxx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 删除分支&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; branch&lt;/span&gt;&lt;span&gt; -d&lt;/span&gt;&lt;span&gt; feature/xxx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;解决合并冲突&lt;a href=&quot;#解决合并冲突&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;合并时如果出现冲突，手动编辑冲突文件，保留需要的内容，然后：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; add&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; commit&lt;/span&gt;&lt;span&gt; -m&lt;/span&gt;&lt;span&gt; &quot;解决合并冲突&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常用技巧&lt;a href=&quot;#常用技巧&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;.gitignore&lt;a href=&quot;#gitignore&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在仓库根目录创建 &lt;code&gt;.gitignore&lt;/code&gt; 文件，列出不需要跟踪的文件：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 依赖目录&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;node_modules/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;__pycache__/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 编辑器配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.vscode/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.idea/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 系统文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.DS_Store&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Thumbs.db&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 构建产物&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;dist/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;build/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;移除跟踪但保留本地文件&lt;a href=&quot;#移除跟踪但保留本地文件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 从 Git 跟踪中移除（不会删除本地文件）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; rm&lt;/span&gt;&lt;span&gt; --cached&lt;/span&gt;&lt;span&gt; .obsidian/app.json&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 或移除整个目录&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; rm&lt;/span&gt;&lt;span&gt; --cached&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; .obsidian/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 加入 .gitignore 防止再次跟踪&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &quot;.obsidian/&quot;&lt;/span&gt;&lt;span&gt; &amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; .gitignore&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; add&lt;/span&gt;&lt;span&gt; .gitignore&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; commit&lt;/span&gt;&lt;span&gt; -m&lt;/span&gt;&lt;span&gt; &quot;移除配置文件跟踪&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;stash 暂存工作区&lt;a href=&quot;#stash-暂存工作区&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 暂存当前修改&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; stash&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 恢复暂存&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; stash&lt;/span&gt;&lt;span&gt; pop&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 查看暂存列表&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; stash&lt;/span&gt;&lt;span&gt; list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;撤销操作&lt;a href=&quot;#撤销操作&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 撤销工作区修改（未 add）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; checkout&lt;/span&gt;&lt;span&gt; --&lt;/span&gt;&lt;span&gt; file.txt&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 撤销暂存（已 add 未 commit）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; reset&lt;/span&gt;&lt;span&gt; HEAD&lt;/span&gt;&lt;span&gt; file.txt&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 撤销最近一次提交（保留修改）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; reset&lt;/span&gt;&lt;span&gt; --soft&lt;/span&gt;&lt;span&gt; HEAD~1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;图形化工具&lt;a href=&quot;#图形化工具&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PyCharm 集成&lt;/strong&gt;：内置 Git 支持，VCS 菜单直接操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VSCode 集成&lt;/strong&gt;：左侧源代码管理面板，支持 diff、提交、推送&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:笔记</category><category>category:工具</category><category>tag:Git</category><category>tag:SSH</category></item></channel></rss>