<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://sjxu-base.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sjxu-base.github.io/" rel="alternate" type="text/html" /><updated>2026-01-05T20:41:08+08:00</updated><id>https://sjxu-base.github.io/feed.xml</id><title type="html">Shujia’s Blog</title><subtitle>Shujia&apos;s Knowledge Base</subtitle><author><name>Shujia Xu</name></author><entry><title type="html">Pod 中的探针问题</title><link href="https://sjxu-base.github.io/cloudnative/Pod-Probe/" rel="alternate" type="text/html" title="Pod 中的探针问题" /><published>2025-11-30T00:00:00+08:00</published><updated>2025-11-30T00:00:00+08:00</updated><id>https://sjxu-base.github.io/cloudnative/Pod-Probe</id><content type="html" xml:base="https://sjxu-base.github.io/cloudnative/Pod-Probe/"><![CDATA[<p>之前写过pod的优雅关闭问题，其实同一篇文档里还提到了探针的定，Pod资源探针包括三种类型是</p>

<ul>
  <li><strong>存活探针</strong> Liveness Probe</li>
  <li><strong>就绪探针</strong> Readness Probe</li>
  <li><strong>启动探针</strong> Startup Probe</li>
</ul>

<h2 id="存活探针-livenessprobe">存活探针 LivenessProbe</h2>

<p>用于判断 Pod 是否“活着”。如果探测失败，kubelet 会认为 Pod 已经挂掉，然后根据 <code class="language-plaintext highlighter-rouge">restartPolicy</code> 重启容器。
作用：<strong>挽救卡死或异常的应用</strong>。它是应用的最后一道防线，通过重启来尝试恢复服务。
场景：你的应用在运行过程中可能因为<strong>死锁、内存泄漏、外部依赖丢失</strong>等原因而停止响应，但它本身的进程还在。这时存活探针会失败，从而触发重启。</p>

<h2 id="就绪探针-readinessprobe">就绪探针 ReadinessProbe</h2>

<ul>
  <li><strong>目的</strong>：<strong>判断 Pod 是否“准备好”接收外部流量</strong>。如果探测失败，该 Pod 会从与其关联的 Service 的负载均衡器中<strong>被移除</strong>，直到探测成功才会重新加入。</li>
  <li><strong>核心作用</strong>：<strong>保证流量的质量</strong>。确保不会将请求发送给还没有完全启动、正在执行繁重任务或暂时无法处理请求的 Pod。</li>
  <li><strong>使用场景</strong>：
    <ul>
      <li><strong>启动阶段</strong>：应用可能需要加载大量数据或配置才能提供服务。</li>
      <li><strong>运行阶段</strong>：应用可能依赖一个临时不可用的数据库，此时它不应该处理请求。</li>
      <li><strong>高负载阶段</strong>：应用暂时处理不了更多请求。</li>
    </ul>
  </li>
</ul>

<h2 id="启动探针-startupprobe">启动探针 StartupProbe</h2>

<ul>
  <li>目的：容器 App 是否已经<strong>启动完成</strong>。在启动探针成功之前，所有其他探针（存活和就绪）都会被禁用。</li>
  <li>作用：<strong>保护慢启动容器</strong>。对于启动速度很慢的应用（如大型 Java 应用），如果使用存活探针，可能会因为它在启动超时时间内没有响应而不断被重启，陷入“启动-&gt;探测失败-&gt;重启-&gt;启动…”的死循环。启动探针解决了这个问题。</li>
  <li>场景：专门用于那些需要较长时间（例如超过 <code class="language-plaintext highlighter-rouge">initialDelaySeconds + failureThreshold * periodSeconds</code>）才能启动完毕的应用。</li>
</ul>

<h3 id="三者的区别与联系对比表格">三者的区别与联系（对比表格）</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: left">特性</th>
      <th style="text-align: left">存活探针</th>
      <th style="text-align: left">就绪探针</th>
      <th style="text-align: left">启动探针</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><strong>探测失败的结果</strong></td>
      <td style="text-align: left">根据Restart Policy 重启 Pod</td>
      <td style="text-align: left">将 Pod 从 Service Endpoints 中移除</td>
      <td style="text-align: left">杀死容器并根据策略重启</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>对流量的影响</strong></td>
      <td style="text-align: left">间接影响。重启期间 Pod 不可用</td>
      <td style="text-align: left"><strong>直接影响</strong>，流量不会被发送到该 Pod。</td>
      <td style="text-align: left">在启动成功前，Pod 不会接收任何流量</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>主要目标</strong></td>
      <td style="text-align: left">恢复故障应用</td>
      <td style="text-align: left">确保流量只发给健康的 Pod</td>
      <td style="text-align: left">保护慢启动容器</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>禁用其他探针</strong></td>
      <td style="text-align: left">No</td>
      <td style="text-align: left">No</td>
      <td style="text-align: left"><strong>Yes</strong></td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>配置建议</strong></td>
      <td style="text-align: left">相对敏感，快速发现无响应</td>
      <td style="text-align: left">相对保守，避免因临时波动被踢出</td>
      <td style="text-align: left">设置足够长的超时时间以覆盖启动过程</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>适用阶段</strong></td>
      <td style="text-align: left">整个 Pod 生命周期</td>
      <td style="text-align: left">整个 Pod 生命周期</td>
      <td style="text-align: left"><strong>仅在容器启动阶段</strong></td>
    </tr>
  </tbody>
</table>

<h3 id="探针作用速记餐厅的后厨">探针作用速记：餐厅的后厨</h3>

<p>如果把一个 Service 想象成一家<strong>餐厅的前台</strong>，把 Pod 想象成<strong>后厨的厨师</strong>。</p>

<ul>
  <li><strong>启动探针</strong>：厨师的<strong>上班打卡</strong>。在他打卡成功之前，餐厅经理（kubelet）不会给他分配任何任务，也不会检查他是否在工作（存活探针）或是否准备好做菜（就绪探针）。他可能还在换衣服、预热烤箱。</li>
  <li><strong>就绪探针</strong>：厨师的<strong>菜品就绪</strong>指示灯。如果厨师正在处理一个非常复杂的菜品，或者灶台暂时满了，他可以把灯按灭。前台（Service）看到灯灭了，就不会把新的点菜单（网络请求）分配给他。等他忙完了，再把灯按亮，重新接收订单。</li>
  <li><strong>存活探针</strong>：餐厅经理检查<strong>厨师是否还活着</strong>（看他有没有心跳）。如果经理发现厨师突然晕倒了（应用崩溃），经理（kubelet）会立刻把他抬出去，并换一个新的厨师进来（重启容器），以确保后厨整体能继续运作。</li>
</ul>

<h2 id="配置示例">配置示例</h2>

<p>在 Pod 的 YAML 文件中，同时配置这三种探针。每种探针都有三种定义方式：<code class="language-plaintext highlighter-rouge">httpGet</code>、<code class="language-plaintext highlighter-rouge">tcpSocket</code> 和 <code class="language-plaintext highlighter-rouge">exec</code>。</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">my-app-pod</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">my-app</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">my-app:latest</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8080</span>
    <span class="c1"># 启动探针 - 使用 HTTP GET 请求</span>
    <span class="na">startupProbe</span><span class="pi">:</span>
      <span class="na">httpGet</span><span class="pi">:</span>
        <span class="na">path</span><span class="pi">:</span> <span class="s">/health-startup</span>
        <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">failureThreshold</span><span class="pi">:</span> <span class="m">30</span> <span class="c1"># 尝试 30 次</span>
      <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">10</span>    <span class="c1"># 每 10 秒一次 -&gt; 总共允许 300 秒启动时间</span>
    <span class="c1"># 存活探针 - 使用 TCP 端口检查</span>
    <span class="na">livenessProbe</span><span class="pi">:</span>
      <span class="na">tcpSocket</span><span class="pi">:</span>
        <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">5</span> <span class="c1"># 容器启动后 5 秒开始探测</span>
      <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">5</span>       <span class="c1"># 每 5 秒探测一次</span>
    <span class="c1"># 就绪探针 - 执行一个 shell 命令</span>
    <span class="na">readinessProbe</span><span class="pi">:</span>
      <span class="na">exec</span><span class="pi">:</span>
        <span class="na">command</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">cat</span>
        <span class="pi">-</span> <span class="s">/tmp/healthy</span>
      <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">5</span>
      <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">5</span>
</code></pre></div></div>

<h2 id="探针的最佳实践">探针的最佳实践</h2>

<ol>
  <li>所有 Prod 级别的应用都应该定义 <strong>Readiness Probe</strong> 和 <strong>Liveness Probe</strong>。这是保证应用高可用的基础。</li>
  <li><strong>Startup Probe不是必须的</strong>，但如果有启动很慢的旧应用，它才会显得很有用。</li>
  <li>探针检查的逻辑应该是<strong>轻量的、无副作用的</strong>，并且不依赖数据库一类的外部服务。如果就绪探针因为数据库挂掉而失败，会导致所有 Pod 同时被踢出服务，引发雪崩效应。</li>
  <li>探针的正常运转依赖于<strong>合理的超时和阈值设置</strong>，否则很可能导致因<strong>网络抖动</strong>或<strong>临时高负载</strong>引发不必要的重启或流量切换。</li>
</ol>

<h2 id="读取探针">读取探针</h2>

<p>读取 Kubernetes 探针的结果并不像直接查看日志那样直观，因为探针的执行和决策是由 kubelet 在节点上完成的。</p>

<p>但是，Kubernetes 中有很多基础命令其实都展示了探针的结果，下面自顶向下聊一下工作中的常用方法。</p>

<h3 id="外部读取使用-kubectl-describe-命令">外部读取：使用 <code class="language-plaintext highlighter-rouge">kubectl describe</code> 命令</h3>

<p>这是最简单、常用的方法，查看 Pod 当前状态和最近的事件，其中包含了探针失败的重要信息。</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe pod <span class="k">${</span><span class="nv">pod_name</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">namespace</span><span class="k">}</span>
</code></pre></div></div>

<p>在命令输出中，重点关注 <code class="language-plaintext highlighter-rouge">Containers</code> 和 <code class="language-plaintext highlighter-rouge">Events</code> 两个部分：</p>

<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Containers</code> 部分
这里会显示每个容器的<strong>当前探针状态</strong>。</p>

    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">Containers</span><span class="pi">:</span>
  <span class="na">my-app-container</span><span class="pi">:</span>
    <span class="na">Container ID</span><span class="pi">:</span>   <span class="s">docker://abc123...</span>
    <span class="na">Image</span><span class="pi">:</span>          <span class="s">my-app:latest</span>
    <span class="na">Image ID</span><span class="pi">:</span>       <span class="s">docker-pullable://my-app@sha256...</span>
    <span class="na">Port</span><span class="pi">:</span>           <span class="s">8080/TCP</span>
    <span class="na">Host Port</span><span class="pi">:</span>      <span class="s">0/TCP</span>
    <span class="na">State</span><span class="pi">:</span>          <span class="s">Running</span>
      <span class="s">Started</span><span class="err">:</span>      <span class="s">Wed, 01 Jan 2023 12:00:00 +0800</span>
    <span class="na">Ready</span><span class="pi">:</span>          <span class="s">True</span>   <span class="c1"># 就绪探针的结果。True 表示通过，False 表示失败。</span>
    <span class="na">Restart Count</span><span class="pi">:</span>  <span class="m">2</span>      <span class="c1"># 存活探针的失败次数，每次重启都是由存活探针失败引起</span>
    <span class="na">Liveness</span><span class="pi">:</span>       <span class="s">http-get http://:8080/healthz delay=0s timeout=1s period=10s</span> <span class="c1">#success=1 #failure=3</span>
    <span class="na">Readiness</span><span class="pi">:</span>      <span class="s">http-get http://:8080/ready delay=0s timeout=1s period=5s</span> <span class="c1">#success=1 #failure=3</span>
    <span class="na">Startup</span><span class="pi">:</span>        <span class="s">http-get http://:8080/startup delay=0s timeout=1s period=10s</span> <span class="c1">#success=1 #failure=30</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Events</code> 部分
这里记录了 Pod 生命周期中的重要事件，<strong>探针失败和容器重启都会在这里显示</strong>。
 Events:
   Type     Reason     Age                   From               Message
   —-     ——     —-                  —-               ——-
   Normal   Scheduled  5m                    default-scheduler  Successfully assigned default/my-app-pod to node-01
   Normal   Pulled     5m                    kubelet            Successfully pulled image “my-app:latest”
   Normal   Created    5m                    kubelet            Created container my-app-container
   Normal   Started    5m                    kubelet            Started container my-app-container
   Warning  Unhealthy  2m (x6 over 4m)       kubelet            Liveness probe failed: HTTP probe failed with statuscode: 500
   Normal   Killing    2m                    kubelet            Container my-app-container failed liveness probe, will be restarted
   Warning  Unhealthy  90s (x10 over 3m30s)  kubelet            Readiness probe failed: Get “http://10.1.0.25:8080/ready”: context deadline exceeded (Client.Timeout exceeded while awaiting headers)</p>
  </li>
</ol>

<p>如果在上述两个部分中，发现下面的问题，就可以进行初步诊断：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Liveness probe failed</code>：存活探针失败的具体原因（如 HTTP 500、连接超时等）。</li>
  <li><code class="language-plaintext highlighter-rouge">Killing ... will be restarted</code>：kubelet 正在杀死并重启容器，这是存活探针失败的<strong>直接后果</strong>。</li>
  <li><code class="language-plaintext highlighter-rouge">Readiness probe failed</code>：就绪探针失败，Pod 会被从 Service 端点中移除。</li>
</ul>

<h3 id="快速浏览查看-pod-状态">快速浏览：查看 Pod 状态</h3>

<p>使用 <code class="language-plaintext highlighter-rouge">kubectl get pods</code> 可以快速查看 Pod 的总体状态，其中 <code class="language-plaintext highlighter-rouge">READY</code> 列直接反映了就绪探针的结果。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pod <span class="k">${</span><span class="nv">pod_name</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">namespace</span><span class="k">}</span>
<span class="c"># NAME          READY   STATUS    RESTARTS   AGE</span>
<span class="c"># my-app-pod    0/1     Running   5          10m</span>
</code></pre></div></div>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">READY</code></strong>： <code class="language-plaintext highlighter-rouge">0/1</code> 表示 Pod 内有 1 个容器，但有 0 个是就绪状态。这通常意味着<strong>就绪探针</strong> FAIL；</li>
  <li><strong><code class="language-plaintext highlighter-rouge">RESTARTS</code></strong>： 如果这个数字在不断增长，几乎可以肯定是<strong>存活探针</strong> FAIL 导致容器在不断重启；</li>
  <li><strong><code class="language-plaintext highlighter-rouge">STATUS</code></strong>： 如果是 <code class="language-plaintext highlighter-rouge">CrashLoopBackOff</code>，通常也与存活探针失败后的频繁重启有关；</li>
</ul>

<h3 id="详细分析查看-kubelet-日志">详细分析：查看 Kubelet 日志</h3>

<p>当 <code class="language-plaintext highlighter-rouge">describe</code> 的事件信息不足以诊断问题时，可以到节点中查看 kubelet 的详细日志。kubelet 会记录每次探针检查的详细过程。</p>

<ol>
  <li>
    <p>大多数现代 Linux 发行版系统使用 <code class="language-plaintext highlighter-rouge">journald</code></p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c"># 首先找到 Pod 所在的节点</span>
 kubectl get pod <span class="k">${</span><span class="nv">pod_name</span><span class="k">}</span> <span class="nt">-o</span> wide
 <span class="c"># 然后登录到该节点，查看 kubelet 日志</span>
 journalctl <span class="nt">-u</span> kubelet <span class="nt">-f</span> <span class="nt">--since</span> <span class="s2">"1 hour ago"</span> | <span class="nb">grep</span> <span class="k">${</span><span class="nv">pod_name</span><span class="k">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>如果日志写在文件中，如 <code class="language-plaintext highlighter-rouge">/var/log/kubelet.log</code>：</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">tail</span> <span class="nt">-f</span> /var/log/kubelet.log | <span class="nb">grep</span> <span class="k">${</span><span class="nv">pod_name</span><span class="k">}</span>
 <span class="c"># Probe for pod-my-namespace_my-app-pod(abc123...) container "my-app-container" succeeded</span>
 <span class="c"># Probe for pod-my-namespace_my-app-pod(abc123...) container "my-app-container" failed: HTTP probe failed with statuscode: 503</span>
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="探针分析检查探针端点本身">探针分析：检查探针端点本身</h3>

<p>很多时候，探针失败不是因为 Pod 有问题，而是因为探针配置或端点本身有问题。</p>

<ol>
  <li>
    <p><strong>进入 Pod 内部手动执行命令</strong>（针对 <code class="language-plaintext highlighter-rouge">exec</code> 类型探针）</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">pod_name</span><span class="k">}</span> <span class="nt">--</span> <span class="k">${</span><span class="nv">command_line</span><span class="k">}</span>
<span class="c"># 例如，探针配置是 `cat /tmp/healthy`</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">pod_name</span><span class="k">}</span> <span class="nt">--</span> <span class="nb">cat</span> /tmp/healthy
<span class="c"># 查看命令的退出码</span>
<span class="nb">echo</span> <span class="nv">$?</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>从集群内部访问 HTTP/TCP 端点</strong>（针对 <code class="language-plaintext highlighter-rouge">httpGet</code> 和 <code class="language-plaintext highlighter-rouge">tcpSocket</code> 类型探针）</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 启动一个临时的调试 Pod，例如使用 busybox</span>
kubectl run <span class="nt">-it</span> <span class="nt">--rm</span> debug <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--restart</span><span class="o">=</span>Never <span class="nt">--</span> sh

<span class="c"># 在 debug Pod 内部，尝试访问目标 Pod 的探针端点，以 HTTP 探针为例：</span>
wget <span class="nt">-O-</span> http://<span class="k">${</span><span class="nv">pod_ip</span><span class="k">}</span>:<span class="k">${</span><span class="nv">port</span><span class="k">}</span>/<span class="k">${</span><span class="nv">path</span><span class="k">}</span>
telnet <span class="k">${</span><span class="nv">pod_ip</span><span class="k">}</span> <span class="k">${</span><span class="nv">port</span><span class="k">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<p><em>注意：Pod IP 可以通过 <code class="language-plaintext highlighter-rouge">kubectl get pod -o wide</code> 获取</em></p>

<h3 id="pod-诊断流程">Pod 诊断流程</h3>

<p>当怀疑探针出现问题时，建议按照以下流程进行排查：</p>

<ol>
  <li><strong>快速定位</strong>
使用 <code class="language-plaintext highlighter-rouge">kubectl get pods</code> 和 <code class="language-plaintext highlighter-rouge">kubectl describe pod</code> 查看 <code class="language-plaintext highlighter-rouge">READY</code> 状态、<code class="language-plaintext highlighter-rouge">RESTARTS</code> 次数和 <code class="language-plaintext highlighter-rouge">Events</code> 事件，确认是否是探针问题以及是哪种探针。</li>
  <li><strong>初步诊断</strong>：从 <code class="language-plaintext highlighter-rouge">Events</code> 中获取失败原因（如 HTTP 500、超时等）。</li>
  <li><strong>深入排查</strong>：
    <ul>
      <li>如果是 <strong>就绪探针</strong> 失败，重点检查应用是否真的已经准备好（如依赖服务、初始化逻辑）。</li>
      <li>如果是 <strong>存活探针</strong> 失败导致重启，重点检查应用是否卡死、内存溢出或出现内部错误。</li>
      <li>如果是 <strong>启动探针</strong> 失败导致无法启动，检查应用启动是否太慢，或者启动脚本是否有问题。</li>
    </ul>
  </li>
  <li><strong>验证端点</strong>：通过 <code class="language-plaintext highlighter-rouge">kubectl exec</code> 或启动临时 Pod 的方式，手动验证探针配置的端点是否按预期工作。</li>
  <li><strong>查看详请</strong>：如果以上步骤无法解决问题，根据排除法，去节点上查看 kubelet 的详细日志。</li>
</ol>]]></content><author><name>Shujia Xu</name></author><category term="CloudNative" /><category term="Kubernetes" /><category term="Pod" /><summary type="html"><![CDATA[之前聊到了 Kubernetes 中 Pod 的优雅关闭问题，其实在 Pod 的官方文档里还非常详细地提到了探针的定义。而三类 Probe 给出的信号，才是真正 定义了 Pod Lifecycle 的最常用参数。]]></summary></entry><entry><title type="html">算法练习：动态规划 DP 合集</title><link href="https://sjxu-base.github.io/algorithm/DP-Collection/" rel="alternate" type="text/html" title="算法练习：动态规划 DP 合集" /><published>2025-11-27T00:00:00+08:00</published><updated>2025-11-27T00:00:00+08:00</updated><id>https://sjxu-base.github.io/algorithm/DP-Collection</id><content type="html" xml:base="https://sjxu-base.github.io/algorithm/DP-Collection/"><![CDATA[<h2 id="lc53最大子数组和"><a href="https://leetcode.cn/problems/house-robber/">LC53</a>｜最大子数组和</h2>

<h3 id="题目">题目</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>给你一个整数数组 nums ，请你找出一个具有最大和的连续子数组（子数组最少包含一个元素），返回其最大和。

子数组是数组中的一个连续部分。

示例 1：
输入：nums = [-2,1,-3,4,-1,2,1,-5,4]
输出：6
解释：连续子数组 [4,-1,2,1] 的和最大，为 6 。

示例 2：
输入：nums = [1]
输出：1

示例 3：
输入：nums = [5,4,-1,7,8]
输出：23


提示：
1 &lt;= nums.length &lt;= 105
-104 &lt;= nums[i] &lt;= 104
</code></pre></div></div>

<h3 id="解析">解析</h3>

<p>两种方法，递推、分治。难点在于如果分治思路，很难理解，（需要剪枝），但递推相对容易，递推难点在于想明 $f(n)$ 记录的是必抢第 n 家的最大值，而不是从 0 到 n 的最大值；</p>

<p>n=0 必抢的情况，$f(0)=num[0]$
n=1 必抢的情况，$f(1)=max(f(0)+num[1], num[1])$
n=2 必抢的情况，$f(2)=max(f(1)+num[2], num[2])$
…
n=n 必抢的情况，$f(n)=max(f(n-1)+num[n], num[n])$</p>

<p>此外，关于分治思路，每次求解f(i,j)，代表从i到j的最大子段和。
线段和问题基本思路，将f(i,j)拆解四个子问题</p>

<ol>
  <li>必定包含左端点的最大值 \(lSum(l,r)=max(num[l],num[l]+lSum(l+1,r))\)</li>
  <li>必定包含右端点的最大值 \(rSum(l,r)=max(num[r], num[r]+rSum(l,r-1))\)</li>
  <li>最大子段和（最终问题）\(mSum(l,r) = max(lSum(l,r),rSum(l,r),mSum(l+1,r-1),iSum(l+1,r-1))\)</li>
  <li>区间和 \(iSum(l,r)=num[l]+num[r]+iSum(l,r)\)</li>
</ol>

<p>只计算4，就会退化为暴力，导致 $O(N^2)$ 的时间复杂度。
此外 3，4 两个问题比较困扰我，当时没想清楚如何拆解出 1 和 2 两个子状态。但这应该是线段树的基础思路。</p>

<h3 id="题解">题解</h3>

<p>递推方案：</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">():</span>
    <span class="k">def</span> <span class="nf">maxSubArray</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nums</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">==</span><span class="mi">0</span><span class="p">:</span>
            <span class="k">return</span> <span class="mi">0</span>
        <span class="n">dp</span><span class="o">=</span><span class="p">[[</span><span class="mi">0</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">))]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">))]</span>
        <span class="n">maxSum</span><span class="o">=</span><span class="nb">max</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)):</span>
            <span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">i</span><span class="p">]</span><span class="o">=</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)):</span>
            <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">),</span><span class="mi">1</span><span class="p">):</span>
                <span class="k">if</span> <span class="n">i</span><span class="o">==</span><span class="n">j</span><span class="p">:</span>
                    <span class="k">continue</span>
                <span class="k">elif</span> <span class="n">j</span><span class="o">==</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">:</span>
                    <span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span><span class="o">=</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span><span class="o">=</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">+</span><span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
                    <span class="n">maxSum</span><span class="o">=</span><span class="nb">max</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">],</span><span class="n">maxSum</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)):</span>
            <span class="k">print</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
        <span class="k">return</span> <span class="n">maxSum</span>
</code></pre></div></div>

<p>分治版本</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
</code></pre></div></div>

<h2 id="lc198打家劫舍"><a href="https://leetcode.cn/problems/house-robber/">LC198</a>｜打家劫舍</h2>

<h3 id="题目-1">题目</h3>

<h3 id="解析-1">解析</h3>

<h3 id="题解-1">题解</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">rob</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nums</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">nums</span><span class="p">:</span> <span class="k">return</span> <span class="mi">0</span>
        <span class="n">size</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span>
        <span class="c1"># 有一家的情况，直接抢唯一一家
</span>        <span class="k">if</span> <span class="n">size</span><span class="o">==</span><span class="mi">1</span><span class="p">:</span> <span class="k">return</span> <span class="n">nums</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="c1"># 有两家的情况，去抢最有钱的一家
</span>        <span class="n">first</span><span class="p">,</span> <span class="n">second</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="nb">max</span><span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">nums</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="n">size</span><span class="p">):</span>
            <span class="c1"># 通过前两项递推第三项
</span>            <span class="n">first</span><span class="p">,</span><span class="n">second</span><span class="o">=</span><span class="n">second</span><span class="p">,</span><span class="nb">max</span><span class="p">(</span><span class="n">first</span><span class="o">+</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="n">second</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">second</span>
</code></pre></div></div>

<h2 id="lc740删除并获得点数"><a href="https://leetcode.cn/problems/delete-and-earn/description/">LC740</a>｜删除并获得点数</h2>

<h3 id="题目-2">题目</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>给你一个整数数组 nums ，你可以对它进行一些操作。
每次操作中，选择任意一个 nums[i] ，删除它并获得 nums[i] 的点数。之后，你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

示例 1：
输入：nums = [3,4,2]
输出：6
解释：
删除 4 获得 4 个点数，因此 3 也被删除。
之后，删除 2 获得 2 个点数。总共获得 6 个点数。

示例 2：
输入：nums = [2,2,3,3,3,4]
输出：9
解释：
删除 3 获得 3 个点数，接着要删除两个 2 和 4 。
之后，再次删除 3 获得 3 个点数，再次删除 3 获得 3 个点数。
总共获得 9 个点数。

提示：
1 &lt;= nums.length &lt;= 2 * 104
1 &lt;= nums[i] &lt;= 104
</code></pre></div></div>

<h3 id="解析-2">解析</h3>

<h3 id="题解-2">题解</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">deleteAndEarn</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nums</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">nums</span><span class="p">:</span> <span class="k">return</span> <span class="mi">0</span>
        <span class="n">numSet</span><span class="o">=</span><span class="nb">set</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span>
        <span class="n">enum</span><span class="o">=</span><span class="nb">list</span><span class="p">(</span><span class="n">numSet</span><span class="p">)</span>
        <span class="n">enum</span><span class="p">.</span><span class="n">sort</span><span class="p">()</span>
        <span class="n">record</span><span class="o">=</span><span class="p">{}</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">enum</span><span class="p">:</span>
            <span class="n">record</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)]</span><span class="o">=</span><span class="n">i</span><span class="o">*</span><span class="n">nums</span><span class="p">.</span><span class="n">count</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
        <span class="n">dp</span><span class="o">=</span><span class="p">[]</span>
        <span class="n">dp</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">record</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">enum</span><span class="p">[</span><span class="mi">0</span><span class="p">])])</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">enum</span><span class="p">)</span><span class="o">==</span><span class="mi">1</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">record</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">enum</span><span class="p">[</span><span class="mi">0</span><span class="p">])]</span>
        <span class="k">if</span> <span class="n">enum</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">!=</span><span class="n">enum</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">+</span><span class="mi">1</span><span class="p">:</span>
            <span class="n">dp</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">+</span><span class="n">record</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">enum</span><span class="p">[</span><span class="mi">1</span><span class="p">])])</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">dp</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">record</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">enum</span><span class="p">[</span><span class="mi">1</span><span class="p">])],</span><span class="n">dp</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">enum</span><span class="p">)</span><span class="o">==</span><span class="mi">2</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">dp</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">enum</span><span class="p">)):</span>
            <span class="k">if</span> <span class="n">enum</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">!=</span><span class="n">enum</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="mi">1</span><span class="p">:</span>
                <span class="n">dp</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="n">record</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">enum</span><span class="p">[</span><span class="n">i</span><span class="p">])])</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">dp</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span><span class="o">+</span><span class="n">record</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">enum</span><span class="p">[</span><span class="n">i</span><span class="p">])]))</span>
        <span class="k">return</span> <span class="n">dp</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>     
</code></pre></div></div>
<h2 id="lc1143最长公共子序列"><a href="https://leetcode.cn/problems/longest-common-subsequence">LC1143</a>｜最长公共子序列</h2>

<h3 id="题目-3">题目</h3>

<h3 id="解析-3">解析</h3>

<h3 id="题解-3">题解</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">longestCommonSubsequence</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text1</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">text2</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">text1</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">text2</span><span class="p">:</span>
            <span class="k">return</span> <span class="mi">0</span>
        <span class="n">dp</span><span class="o">=</span><span class="p">[[</span><span class="mi">0</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">text2</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">)]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">text1</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">)]</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">text1</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">text2</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
                <span class="k">if</span> <span class="n">text1</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">==</span><span class="n">text2</span><span class="p">[</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
                    <span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span><span class="o">=</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="mi">1</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span><span class="o">=</span><span class="nb">max</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">],</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
        <span class="k">return</span> <span class="n">dp</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</code></pre></div></div>

<h2 id="动态规划dp小结">动态规划（DP）小结</h2>

<h3 id="问题特点最优子结构-和-无后效性">问题特点：最优子结构 和 无后效性</h3>

<p>DP 类问题最主要的特点就是 <strong>无后效性</strong>，即一个大问题可以拆成多个小问题，每个小问题有两个特性</p>

<ul>
  <li>最优子结构（无后效性）：只需要记录最优解，而非最优解对后续求解不会产生影响</li>
  <li>重叠子问题：每个小问题的解会被反复使用，所以需要 <code class="language-plaintext highlighter-rouge">dp</code> 数组来记录小问题的解</li>
</ul>

<blockquote>
  <p>首先，虽然动态规划的核心思想就是穷举求最值，但是问题可以千变万化，穷举所有可行解其实并不是一件容易的事，需要你熟练掌握递归思维，只有列出正确的「状态转移方程」，才能正确地穷举。</p>

  <p>而且，你需要判断算法问题是否具备「最优子结构」，是否能够通过子问题的最值得到原问题的最值。</p>

  <p>另外，动态规划问题存在「重叠子问题」，如果暴力穷举的话效率会很低，所以需要你使用「备忘录」或者「DP table」来优化穷举过程，避免不必要的计算。</p>
</blockquote>

<p>而拆解这种小问题，就包括两种方式，一个是自底向上，一个是自顶向下。</p>

<h3 id="状态转移方程自底向上-和-自顶向下">状态转移方程：自底向上 和 自顶向下</h3>

<p>自底向上拆解方法最典型的就是斐波那契数列，需要首先定义 <code class="language-plaintext highlighter-rouge">dp[0]</code>，<code class="language-plaintext highlighter-rouge">dp[1]</code> 等初始的状态，然后通过状态转移方程，自动推理到 <code class="language-plaintext highlighter-rouge">dp[len(nums)]</code> 一类的状态。代码上，通常通过循环实现。</p>

<p>自顶向下拆解方法则是采用分治办法，首先将大问题拆解为小问题，例如 求解 <code class="language-plaintext highlighter-rouge">dp[20]</code>，需要求解小问题 <code class="language-plaintext highlighter-rouge">dp[19]</code> 和 <code class="language-plaintext highlighter-rouge">dp[18]</code>，求解过程中，对于未知的子问题进行迭代求解，并将已经解决的子问题归入已解决问题的范畴，对于已知的子问题，直接调用已经记录的子状态。代码上，通常通过函数递归实现。</p>

<p>这两者的本质是一样的，可以互相转化。迭代解法中的那个 dp 数组，就是递归解法中的 memo 数组。自顶向下、自底向上两种解法本质其实是差不多的，大部分情况下，效率也基本相同。</p>

<h3 id="模版思路">模版思路</h3>

<p>本质：暴力枚举，原问题不断拆分为子问题。
思考思路：选或不选（大部分是），枚举选哪个
解决方法：记忆化搜索（较容易）或者 dp 数组递推（较难）
关于计算 dp 时间复杂度的理解：1. 子问题个数 × 得到子问题解的复杂度。 2. 状态个数 × 得到每个状态的复杂度</p>

<p>思路基本确定，回归到具体题目中，</p>

<p>解决动态规划类问题，通常分为四步</p>

<p>第一步，构建正确的问题状态表征，尽量去除无用的参数。如果这部搞得不清楚，就会做出很多重复性计算，无后效性就会变差，甚至退化为暴力求解。</p>

<p>第二步，构建状态转移方程，需要搞清楚<strong>大问题</strong>和<strong>小问题</strong>之间的推进分析管理，即各个小问题的解如何合并成大问题的解，这一步至关重要。</p>

<p>第三步，开始编码，去关注一些边际问题，例如<code class="language-plaintext highlighter-rouge">dp[0]</code>等初始值的设立，无效子问题的合理解设定等。</p>

<hr />

<h2 id="reference">Reference</h2>

<ul>
  <li><a href="https://houbb.github.io/2020/01/23/data-struct-learn-07-base-dp#%E7%AE%97%E6%B3%95%E5%AE%9E%E6%88%98-%E8%B7%AF%E5%BE%84">五大基本算法之动态规划算法 DP dynamic programming</a></li>
  <li><a href="https://labuladong.online/algo/essential-technique/dynamic-programming-framework-2/">动态规划解题套路框架</a></li>
  <li><a href="https://leetcode.cn/discuss/post/obCjFP/">动态规划常见题型、模版总结</a></li>
</ul>]]></content><author><name>Shujia Xu</name></author><category term="Algorithm" /><category term="Leetcode" /><category term="DP" /><summary type="html"><![CDATA[关于动态规划的五道题，核心在处理无]]></summary></entry><entry><title type="html">Markdown的一些操作小技巧</title><link href="https://sjxu-base.github.io/language/Markdown-Tricks/" rel="alternate" type="text/html" title="Markdown的一些操作小技巧" /><published>2025-11-03T00:00:00+08:00</published><updated>2025-11-03T00:00:00+08:00</updated><id>https://sjxu-base.github.io/language/Markdown-Tricks</id><content type="html" xml:base="https://sjxu-base.github.io/language/Markdown-Tricks/"><![CDATA[<h2 id="0x01-章节定位符">0x01 章节定位符</h2>

<p>许多 Markdown 处理器支持标题的自定义ID，部分 Markdown 渲染器会自动添加它们。</p>

<p>添加自定义 ID 可以允许直接链接到标题并使用 CSS 对其进行修改。</p>

<p>要添加自定义标题ID，与标题相同的行上用大括号括起该自定义ID <code class="language-plaintext highlighter-rouge">{#user_defined_id}</code>。</p>

<h2 id="0x02-图片样式规范">0x02 图片样式规范</h2>

<p>使用 Markdown 插入图片一般有两种方式</p>

<ul>
  <li>Markdown 原生语法 <code class="language-plaintext highlighter-rouge">![]()</code></li>
  <li>HTML 的 <code class="language-plaintext highlighter-rouge">&lt;img&gt;</code> 和 <code class="language-plaintext highlighter-rouge">&lt;figure&gt;</code>标签</li>
</ul>

<p>但是前者无法定义图片大小和图片解释词，所以最好使用 HTML 进行标定。</p>

<ul>
  <li>
    <p>修改图片大小</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"../images/rocks.jpg"</span> <span class="na">width=</span><span class="s">"200"</span> <span class="na">height=</span><span class="s">"100"</span><span class="nt">&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>为图片添加注释</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="nt">&lt;figure</span> <span class="na">style=</span><span class="s">"text-align: center;"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"https://sjxu-base.github.io/assets/images/posts/20210802/netfilter.png"</span> <span class="na">alt=</span><span class="s">"20210802"</span> <span class="na">width=</span><span class="s">"500"</span> <span class="na">height=</span><span class="s">"150"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;figcaption&gt;</span>A Netfilter Demo Pic<span class="nt">&lt;/figcaption&gt;</span>
      <span class="nt">&lt;/figure&gt;</span>
</code></pre></div>    </div>

    <p>实际效果如下</p>

    <figure style="text-align: center;">
      <img src="https://sjxu-base.github.io/assets/images/posts/20210802/netfilter.png" alt="20210802" width="500" height="150" />
      <figcaption>A Netfilter Demo Pic</figcaption>
  </figure>
  </li>
</ul>

<h3 id="在-markdown-中使用-html-的最佳实践">在 Markdown 中使用 HTML 的最佳实践</h3>

<ol>
  <li>使用语义化的 HTML 标签</li>
  <li>添加样式增强</li>
  <li>确保内容的可访问性</li>
  <li>保持代码整洁和可维护</li>
</ol>

<h2 id="0x03-使用-frontmatter">0x03 使用 FrontMatter</h2>

<p>Front Matter 是位于文件顶部、用分隔符包围的元数据区块，用于存储文档的元信息。</p>

<p>很多静态网站生成器都支持 Front Matter：</p>

<ul>
  <li>Jekyll (Ruby)</li>
  <li>Hugo (Go) - 支持 YAML、TOML、JSON 格式</li>
  <li>Next.js (React/JavaScript)</li>
  <li>Hexo (Node.js)</li>
  <li>GitBook</li>
</ul>

<p>使用过程中，最佳实践是在分隔符后保持一行空行。</p>

<ul>
  <li>分行可以从而保持更好的可读性，清晰区分元数据和内容。</li>
  <li>避免解析问题，例如在 Jekyll 中紧接 <code class="language-plaintext highlighter-rouge">---</code> 的内容会被认为是 FrontMatter 的一部分，并引发渲染错误。</li>
  <li>符合 CommonMark 标准：空行有助于 Markdown 解析器正确识别段落。</li>
</ul>

<h2 id="0x04-使用-mermaid-绘图">0x04 使用 Mermaid 绘图</h2>

<p>可以使用 Mermaid 来支持</p>

<h2 id="0x06-本地测试">0x06 本地测试</h2>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="c"># Install gem denpendency</span>
    bundle <span class="nb">install</span>
    <span class="c"># Exec the Jekyll Server</span>
    bundle <span class="nb">exec </span>jekyll server
</code></pre></div></div>

<hr />

<h2 id="reference">Reference</h2>

<ul>
  <li><a href="https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll">Creating a GitHub Pages site with Jekyll</a></li>
</ul>]]></content><author><name>Shujia Xu</name></author><category term="Language" /><category term="Markdown" /><summary type="html"><![CDATA[Markdown 小技巧以及一些编写博客的最佳实践]]></summary></entry><entry><title type="html">LeetCode 解法列表</title><link href="https://sjxu-base.github.io/algorithm/LeetCode-List/" rel="alternate" type="text/html" title="LeetCode 解法列表" /><published>2025-10-31T00:00:00+08:00</published><updated>2025-10-31T00:00:00+08:00</updated><id>https://sjxu-base.github.io/algorithm/LeetCode-List</id><content type="html" xml:base="https://sjxu-base.github.io/algorithm/LeetCode-List/"><![CDATA[<h2 id="0x01-字符串">0x01 字符串</h2>

<ul>
  <li>23.合并 K 个升序链表：<a href="https://leetcode-cn.com/problems/merge-k-sorted-lists/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/23/">解法</a></li>
  <li>179.最大数：<a href="https://leetcode-cn.com/problems/largest-number/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/179/">解法</a></li>
  <li>300.最长递增子序列：<a href="https://leetcode-cn.com/problems/longest-increasing-subsequence/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/300/">解法</a></li>
  <li>1458.两个子序列的最大点积：<a href="https://leetcode.cn/problems/max-dot-product-of-two-subsequences/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/1458/">解法</a></li>
</ul>

<h2 id="0x02-二叉树">0x02 二叉树</h2>

<ul>
  <li>671.二叉树中第二小的节点：<a href="https://leetcode-cn.com/problems/second-minimum-node-in-a-binary-tree">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/671/">解法</a></li>
  <li>863.二叉树中所有距离为K的节点：<a href="https://leetcode.cn/problems/all-nodes-distance-k-in-binary-tree/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/863/">解法</a></li>
  <li>1104.二叉树寻路：<a href="https://leetcode.cn/problems/path-in-zigzag-labelled-binary-tree/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/1104/">解法</a></li>
</ul>

<h2 id="0x03-动态规划">0x03 动态规划</h2>

<ul>
  <li>1105.填充书架：<a href="https://leetcode.cn/problems/filling-bookcase-shelves/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/1105/">解法</a></li>
</ul>

<h2 id="0x04-dfs--bfs">0x04 DFS &amp; BFS</h2>

<h2 id="dfs-专集">DFS 专集</h2>

<ul>
  <li>37.解数独：<a href="https://leetcode-cn.com/problems/sudoku-solver/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/37/">解法</a></li>
  <li>67.二进制求和：<a href="https://leetcode-cn.com/problems/add-binary/">题目</a>｜<a href="https://s j xu/leetcode/67/">解法</a></li>
  <li>94.二叉树的中序遍历：<a href="https://leetcode-cn.com/problems/binary-tree-inorder-traversal/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/94/">解法</a></li>
  <li>100.相同的树：<a href="https://leetcode-cn.com/problems/same-tree/">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/100/">解法</a></li>
  <li>2044.统计按位或能得到最大值的子集数目：<a href="https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets">题目</a>｜<a href="https://sjxu-base.github.io/leetcode/2044/">解法</a></li>
</ul>]]></content><author><name>Shujia Xu</name></author><category term="Algorithm" /><category term="Leetcode" /><category term="Summary" /><summary type="html"><![CDATA[LeetCode 解法目录]]></summary></entry><entry><title type="html">基础架构技能树</title><link href="https://sjxu-base.github.io/cloudnative/DevOps-Skill-Tree/" rel="alternate" type="text/html" title="基础架构技能树" /><published>2025-10-30T00:00:00+08:00</published><updated>2025-10-30T00:00:00+08:00</updated><id>https://sjxu-base.github.io/cloudnative/DevOps-Skill-Tree</id><content type="html" xml:base="https://sjxu-base.github.io/cloudnative/DevOps-Skill-Tree/"><![CDATA[<p>进入基础架构行业工作四年了，每次面试难免都要回忆基础知识。</p>

<p>但 SRE、DevOps、IT 各个层级的内容相互覆盖，为了方便整理，也可以给新入行的同学查缺补漏，构建了下面的知识树。</p>

<h2 id="part-a基础知识">Part A.基础知识</h2>

<h3 id="a01计算机网络">A01.计算机网络</h3>

<h4 id="基础概念">基础概念</h4>

<ol>
  <li>五层与七层网络模型</li>
</ol>

<h4 id="网络协议">网络协议</h4>

<ol>
  <li>TCP 与 UDP</li>
  <li>TCP 中的握手、离手概念、滑动窗口</li>
  <li>HTTP 与 HTTPS</li>
</ol>

<h4 id="网络诊断">网络诊断</h4>

<ol>
  <li>Linux 网络诊断命令</li>
  <li>Wireshark 抓包分析</li>
  <li>Firewall 设置</li>
  <li><a href="https://www.redhat.com/zh-cn/topics/linux/what-is-selinux">设置和关闭 SELinux</a></li>
</ol>

<h4 id="网络服务">网络服务</h4>

<ol>
  <li>DNS 查询原理</li>
  <li>SELinux</li>
  <li>四层、七层负载均衡</li>
</ol>

<h3 id="a02操作系统--linux">A02.操作系统 / Linux</h3>

<h4 id="进程管理">进程管理</h4>

<ol>
  <li>进程的7个状态、进程调度、进程间通信模型</li>
  <li>僵尸进程与孤儿进程的区别</li>
  <li>查看与关闭 daemon 守护进程</li>
</ol>

<h4 id="内存管理">内存管理</h4>

<ol>
  <li>什么是虚拟内存 SWAP</li>
  <li>Linux 中的内存分页与交换技术</li>
  <li>内存映射技术 mmap、缓冲与缓存</li>
</ol>

<h4 id="文件管理">文件管理</h4>

<ol>
  <li><a href="https://manpages.ubuntu.com/manpages/resolute/en/man7/hier.7.html">FHS｜类 Unix 系统标准文件系统结构</a></li>
  <li>什么是 inode 和 block</li>
  <li>文件权限与文件类型</li>
  <li>挂载机制</li>
</ol>

<h4 id="容器原理">容器原理</h4>

<ol>
  <li>容器的基本原理
    <ul>
      <li>namespace 隔离机制</li>
      <li>cgroups 资源限制</li>
    </ul>
  </li>
  <li>容器运行时原理</li>
</ol>

<h4 id="性能调优">性能调优</h4>

<ul>
  <li>系统调优参数（sysctl）</li>
  <li>内存回收机制</li>
  <li>I/O调度算法</li>
</ul>

<h4 id="安全功能">安全功能</h4>

<ol>
  <li>什么是 SELinux 和 AppArmor</li>
  <li>如何调取审计日志（auditd）</li>
  <li>系统加固原则</li>
</ol>

<h3 id="a03计算机组成原理">A03.计算机组成原理</h3>

<h4 id="系统引导">系统引导</h4>

<ol>
  <li>什么是 BIOS/UEFI、Bootloader、Kernel、Init、Runlevel、systemd初始化系统</li>
  <li>内核机制：系统调用（syscall）、中断处理、模块加载与卸载</li>
</ol>

<h4 id="cpu--gpu">CPU &amp; GPU</h4>

<ol>
  <li>如何计算 CPU 时钟频率</li>
  <li>CPU 和 GPU区别</li>
</ol>

<h4 id="memory">Memory</h4>

<h4 id="disk">Disk</h4>

<ol>
  <li>RAID 有哪几类？</li>
  <li>如何选择最合适的系统 RAID？</li>
</ol>

<h2 id="part-b硬件管理与系统管理it1-部分">Part B.硬件管理与系统管理（IT1 部分）</h2>

<h3 id="b01系统交付">B01.系统交付</h3>

<h4 id="带外管理服务">带外管理服务</h4>

<ol>
  <li>ipmi 和 redfish 是什么?</li>
  <li>使用 ipmitool 远程获取系统信息</li>
</ol>

<h4 id="系统引导服务">系统引导服务</h4>

<ol>
  <li>系统应答文件 AutoInstall</li>
  <li>使用 netboot 根据 SN 号引导进入不同镜像</li>
  <li>对 dhcpd 服务进行 IaC 改造</li>
</ol>

<h4 id="系统镜像管理">系统镜像管理</h4>

<ol>
  <li>如何更改 Linux 内核参数</li>
  <li>常见 Linux 内核调优参数</li>
  <li>使用 Packer 构建自动镜像打包服务</li>
</ol>

<h3 id="b02cmdb-支持">B02.CMDB 支持</h3>

<h4 id="netbox">NetBox</h4>

<h2 id="part-c基础架构管理devops-部分">Part C.基础架构管理（DevOps 部分）</h2>

<h3 id="c01容器服务">C01.容器服务</h3>

<h4 id="docker">Docker</h4>

<ol>
  <li>基础概念：cgroup、不可变交付、隔离依赖</li>
  <li>基础知识：
    <ul>
      <li>什么是 DOCKER-CE</li>
      <li>基本 Docker 容器运行命令 <code class="language-plaintext highlighter-rouge">docker run</code></li>
      <li>编写 Dockerfile</li>
      <li>Image 管理：构建、推送、版本控制</li>
      <li>Docker 网络管理：bridge、host、overlay、各级别通信模型</li>
      <li>存储管理：储存外挂、文件权限</li>
    </ul>
  </li>
  <li>进阶知识：
    <ul>
      <li>为服务编写 docker-composer</li>
      <li>docker 镜像构建阶段</li>
    </ul>
  </li>
</ol>

<h4 id="kubernetes">Kubernetes</h4>

<ol>
  <li>基础资源定义 Pod/Deployment/Service/Ingress资源管理</li>
  <li>Ingress服务发现与负载均衡</li>
  <li>配置管理（ConfigMap/Secret）
存储管理（PV/PVC/StorageClass）
    <ul>
      <li>HPA/VPA自动扩缩容</li>
      <li>网络策略（NetworkPolicy）</li>
      <li>集群运维（备份恢复、升级）</li>
      <li>本地实践：
        <ul>
          <li>minikube</li>
          <li>microk8s</li>
          <li>k3s</li>
        </ul>
      </li>
      <li>Helm/Kuberlizer</li>
    </ul>
  </li>
</ol>

<h3 id="c02虚拟机服务">C02.虚拟机服务</h3>

<h4 id="proxmox">Proxmox</h4>

<ul>
  <li>虚拟机生命周期管理</li>
  <li>集群管理与高可用</li>
  <li>存储配置（LVM、Ceph）</li>
  <li>备份与恢复策略</li>
  <li>安全隔离设计</li>
</ul>

<h3 id="c03云原生服务">C03.云原生服务</h3>

<h4 id="openstack">Openstack</h4>

<ol>
  <li>
    <table>
      <tbody>
        <tr>
          <td>Openstack 基本概念与核心组件：<code class="language-plaintext highlighter-rouge">Nova</code></td>
          <td><code class="language-plaintext highlighter-rouge">Neutron</code></td>
          <td><code class="language-plaintext highlighter-rouge">Cinder</code></td>
        </tr>
      </tbody>
    </table>
  </li>
  <li>如何实现云资源管理</li>
  <li>如何实现多租户隔离</li>
  <li>如何实现计量与计费</li>
</ol>

<h3 id="c04ci--cd--iac">C04.CI &amp; CD &amp; IaC</h3>

<h4 id="ci">CI</h4>

<ul>
  <li>Jenkins</li>
  <li>ArgoWorkflow</li>
  <li>Gitea</li>
  <li>GithubAction</li>
</ul>

<h4 id="cd">CD</h4>

<ul>
  <li>ArgoCD</li>
  <li>FluxCD</li>
</ul>

<h4 id="cicd-完整技术方案">CICD 完整技术方案</h4>

<ul>
  <li>Jenkins =&gt; Docker =&gt; Shell</li>
  <li>Gitea =&gt; Python =&gt; Shell</li>
  <li>Gitlab CI/CD =&gt; Docker &amp; K8S =&gt; Shell</li>
</ul>

<h4 id="terraform">Terraform</h4>

<h4 id="ansible">Ansible</h4>

<ol>
  <li>Ansible 基础概念：幂等性、模块、Playbook</li>
  <li>Ansible 常用模块</li>
  <li>Ansible 调试与输出</li>
  <li>Ansible Tower</li>
</ol>

<h4 id="jsonnet-开发">jsonnet 开发</h4>

<h3 id="c05监控与告警系统">C05.监控与告警系统</h3>

<h4 id="基础平台">基础平台</h4>

<ul>
  <li>Prometheus</li>
  <li>Grafana</li>
  <li>Alertmanager</li>
</ul>

<h4 id="技术方案">技术方案</h4>

<ul>
  <li>APM+Agent探针</li>
</ul>

<h4 id="可观测性日志指标追踪">可观测性：日志、指标、追踪</h4>

<ul>
  <li>Elasticsearch
    <ul>
      <li>集群规划与容量管理</li>
      <li>索引生命周期管理</li>
      <li>性能调优</li>
      <li>安全配置</li>
    </ul>
  </li>
  <li>日志收集
    <ul>
      <li>Fluentd/Fluent Bit：日志采集与转发</li>
      <li>Logstash：日志处理管道</li>
      <li>Loki：轻量级日志聚合</li>
    </ul>
  </li>
  <li>链路追踪
    <ul>
      <li>Jaeger：分布式追踪</li>
      <li>Zipkin：调用链分析</li>
      <li>OpenTelemetry：可观测性标准</li>
    </ul>
  </li>
  <li>指标监控
    <ul>
      <li>VictoriaMetrics：高性能时序数据库</li>
      <li>InfluxDB：时间序列数据存储</li>
    </ul>
  </li>
</ul>

<h4 id="时序数据库">时序数据库</h4>

<ol>
  <li>使用 ELK 构建日志分析平台</li>
</ol>

<h4 id="服务网格-istio">服务网格 istio</h4>

<ul>
  <li>什么是 Service Mesh 服务网格</li>
  <li><a href="https://istio.io/latest/zh/docs/overview/what-is-istio/">istio 是什么？</a></li>
  <li>istio 功能：服务发现、负载均衡、故障恢复、度量和监控等</li>
  <li>使用 istio 支持更复杂的运维需求：AB 测试、金丝雀发布、速率限制、访问控制和端到端认证</li>
  <li>API 流量管控</li>
  <li>多版本API分流</li>
</ul>

<h3 id="c06数据库">C06.数据库</h3>

<h4 id="数据库基础概念">数据库基础概念</h4>

<ul>
  <li>ACID特性</li>
  <li>事务隔离级别</li>
  <li>索引原理与优化</li>
  <li>查询执行计划</li>
  <li>备份恢复策略</li>
  <li>高可用方案（主从、集群）</li>
</ul>

<h4 id="mysql">MySQL</h4>

<h4 id="mongodb">MongoDB</h4>

<h3 id="c07kv-存储">C07.KV 存储</h3>

<h4 id="一致性基础知识">一致性基础知识</h4>

<ol>
  <li>什么是 Raft 算法</li>
  <li>脑裂问题与处理方案</li>
</ol>

<h4 id="redis">Redis</h4>

<h4 id="etcd">etcd</h4>

<h3 id="c08消息队列">C08.消息队列</h3>

<h4 id="kafka">Kafka</h4>

<h4 id="rabbitmq">RabbitMQ</h4>

<h3 id="c09对象存储及文件存储">C09.对象存储及文件存储</h3>

<h4 id="ceph">Ceph</h4>

<h4 id="minio">MinIO</h4>

<h4 id="nfs">NFS</h4>

<h4 id="cephfs">CephFS</h4>

<h2 id="part-d公有云服务devops-与-sre-重合部分">Part D.公有云服务（DevOps 与 SRE 重合部分）</h2>

<h3 id="d01aws云服务">D01.AWS云服务</h3>

<h2 id="part-e稳定性工程sre-部分">Part E.稳定性工程（SRE 部分）</h2>

<h3 id="e01系统指标">E01.系统指标</h3>

<ol>
  <li><a href="https://sre.google/sre-book/service-level-objectives/">什么是 SLO/SLA/SLI？</a></li>
  <li>系统占用率评估</li>
</ol>

<h3 id="e02服务发布-sop">E02.服务发布 SOP</h3>

<ol>
  <li>使用 Apach Benchmark 进行系统容量测算</li>
  <li>使用 metric-server 构建系统监控指标</li>
  <li>使用 ClusterAutoscaler + HPA 设置自动扩容方案</li>
  <li>使用 Route53 + ALB + NLB 设置蓝绿发布与灰度发布</li>
  <li>服务监控环节：服务、k8s、基础架构</li>
</ol>

<h3 id="e03高可用系统构建">E03.高可用系统构建</h3>

<ol>
  <li>自动扩容设计</li>
</ol>

<h3 id="e04容灾演练与方案预设">E04.容灾演练与方案预设</h3>

<h3 id="e05系统成本审计">E05.系统成本审计</h3>

<h3 id="e06团队最佳实践">E06.团队最佳实践</h3>

<ol>
  <li><a href="https://sre.google/sre-book/postmortem-culture/">构建 Post Mortem 环节</a></li>
  <li>各项服务的 Capacity Review</li>
</ol>

<h2 id="part-f编程语言">Part F.编程语言</h2>

<h3 id="f01shell-脚本开发">F01.Shell 脚本开发</h3>

<h4 id="文本处理工具">文本处理工具</h4>

<ul>
  <li>gawk</li>
  <li>sed</li>
  <li>grep</li>
</ul>

<h4 id="文件处理">文件处理</h4>

<ul>
  <li>目录处理：ls\cat\find\rm\mv\touch\mkdir</li>
  <li>文件比较：diff\cmp</li>
  <li>压缩解压：tar\gzip\zip</li>
</ul>

<h4 id="权限管理">权限管理</h4>

<ul>
  <li>chmod\chown\chgrp</li>
  <li>文件权限（rwx）、特殊权限（suid\sgid\sticky）</li>
  <li>useradd\groupadd\passwd</li>
</ul>

<h4 id="io-处理">IO 处理</h4>

<ul>
  <li>管道、重定向、stdin</li>
  <li>参数传递：xargs</li>
  <li>脚本调试：set &amp; trap</li>
  <li>脚本传参：<code class="language-plaintext highlighter-rouge">$0</code> &amp; <code class="language-plaintext highlighter-rouge">$1</code> &amp; <code class="language-plaintext highlighter-rouge">$*</code> &amp; <code class="language-plaintext highlighter-rouge">$@</code></li>
</ul>

<h4 id="网络连接">网络连接</h4>

<ul>
  <li>联通诊断：telnet/ssh/ping/traceroute</li>
  <li>内网穿透：frp/ngrok</li>
  <li>防火墙：iptables/firewalld/ufw</li>
  <li>网络配置：ifconfig/ip/route</li>
  <li>文件传输：scp/rsync/wget/curl</li>
</ul>

<h4 id="系统监控">系统监控</h4>

<ul>
  <li>进程监控：top/htop/ps/pstree/perf
    <ul>
      <li><a href="https://zhuanlan.zhihu.com/p/54276509">Linux Perf 性能分析工具及火焰图浅析</a></li>
    </ul>
  </li>
  <li>性能分析：vmstat/iostat/mpstat</li>
  <li>内存监控：free/smaps</li>
  <li>磁盘监控：df/du/lsblk/lsof</li>
  <li>网络监控：netstat/ss/iftop</li>
</ul>

<h4 id="系统服务">系统服务</h4>

<ul>
  <li>systemctl：服务启停、状态查看、开机自启</li>
  <li>journalctl：日志查看与分析</li>
  <li>cron：定时任务管理</li>
</ul>

<h4 id="安装包管理">安装包管理</h4>

<ul>
  <li>apt 与 dpkg：Debian/Ubuntu</li>
  <li>yum 与 rpm： CentOS/RHEL</li>
</ul>

<h3 id="f02python-开发">F02.Python 开发</h3>

<h4 id="基础数据结构">基础数据结构</h4>

<ol>
  <li>Python 数据类型中的<strong>可变类型</strong>与<strong>不可变类型</strong></li>
  <li><strong>深拷贝</strong>与<strong>浅拷贝</strong>问题</li>
  <li>Python 中列表与字典的操作（四种元素删除操作）</li>
</ol>

<h4 id="文件处理-1">文件处理</h4>

<h4 id="异常处理">异常处理</h4>

<h4 id="多线程">多线程</h4>

<ol>
  <li>Python 中的 GIL 锁</li>
  <li>使用<strong>协程</strong>进行开发</li>
</ol>

<h4 id="python-语法糖">Python 语法糖</h4>

<ol>
  <li>Python 中的装饰器与解释器</li>
  <li>Python 中的生成器与迭代器</li>
</ol>

<h3 id="f03golang-开发">F03.Golang 开发</h3>

<h2 id="part-g算法">Part G.算法</h2>

<h3 id="g01数据结构基础">G01.数据结构基础</h3>

<h4 id="链表">链表</h4>

<ol>
  <li>使用 Python 快速构建链表</li>
</ol>

<h4 id="树">树</h4>

<ol>
  <li>使用平衡二叉树、二叉搜索树</li>
  <li>构建 AVL 与 红黑树</li>
</ol>

<h4 id="队列">队列</h4>

<ol>
  <li>LRU 算法实现（LFU、FIFO 算法）</li>
</ol>

<h3 id="g02算法类型">G02.算法类型</h3>

<h4 id="浮点数">浮点数</h4>

<h4 id="字符串">字符串</h4>

<h4 id="双指针">双指针</h4>

<ol>
  <li>典型双指针：接雨水</li>
</ol>

<h4 id="动态规划">动态规划</h4>

<ol>
  <li>动态规划基础思路</li>
  <li>背包问题：背包九讲 与 打家劫舍</li>
</ol>

<h4 id="深搜-与-广搜">深搜 与 广搜</h4>

<h4 id="贪心算法">贪心算法</h4>

<ol>
  <li>贪心算法与动态搜索的区别</li>
</ol>

<h4 id="线段树">线段树</h4>

<h4 id="启发式搜索">启发式搜索</h4>]]></content><author><name>Shujia Xu</name></author><category term="CloudNative" /><category term="DevOps" /><category term="SRE" /><summary type="html"><![CDATA[根据工作经验总结的 DevOps、SRE、IT 工程师需要的基本技能树，以供参考。]]></summary></entry><entry><title type="html">Linux 网络面试题 Part02：高性能网络优化</title><link href="https://sjxu-base.github.io/linux/Network-Interview-02/" rel="alternate" type="text/html" title="Linux 网络面试题 Part02：高性能网络优化" /><published>2025-06-18T00:00:00+08:00</published><updated>2025-06-18T00:00:00+08:00</updated><id>https://sjxu-base.github.io/linux/Network-Interview-02</id><content type="html" xml:base="https://sjxu-base.github.io/linux/Network-Interview-02/"><![CDATA[<p>在高频量化公司的面试中，听到的一些网络问题。从最基础的 TCP 三次握手开始，顺势聊到了高频交易中实用的一些网络优化方案。结合后续网上补课看到的一些技术，总结的一下这些题目相关的网络入门知识。</p>

<h2 id="0x03-高性能网络优化">0x03 高性能网络优化</h2>

<h3 id="10-什么是-busy-polling-和-dpdk">10. 什么是 busy-polling 和 DPDK？</h3>

<h4 id="传统网络模型及限制">传统网络模型及限制</h4>

<p>传统网络模型<em>(Linux&lt; 2.5.44)</em>中，一个数据包的发送了流程可以参考下图：</p>

<pre><code class="language-mermaid">graph TD
    subgraph UserSpace["用户态 (User Space)"]
        A["应用程序 (Application)"]
        B["Socket API"]
    end;

    subgraph KernelSpace["内核态 (Kernel Space)"]
        C["TCP/UDP 协议栈"]
        D["IP Layer"]
        E["MAC/以太网 Layer"]
        F["NIC Driver"]
    end

    subgraph HW["NIC Hardware"]
        G["硬件队列 (TX/RX Queues)"]
        H["物理介质 (Wire)"]
    end

    A --&gt; B --&gt; C --&gt; D --&gt; E --&gt; F --&gt; G --&gt; H
    H --&gt; G --&gt; F --&gt; E --&gt; D --&gt; C --&gt; B --&gt; A
</code></pre>

<p>可以看到，一个数据包从用户空间到物理网卡至少要经过<strong>两次</strong>拷贝：</p>

<ul>
  <li>第一次拷贝：从用户空间缓冲区 → 内核空间缓冲区
    <ul>
      <li>通过 系统调用触发，如<code class="language-plaintext highlighter-rouge">send()</code></li>
      <li>涉及用户态到内核态的上下文切换</li>
    </ul>
  </li>
  <li>第二次拷贝：从内核空间缓冲区 → 网卡DMA缓冲区
    <ul>
      <li>内核协议栈处理完成后</li>
      <li>通过 DMA 引擎传输，但仍需 CPU 中断</li>
    </ul>
  </li>
</ul>

<p>这中间，当来自 User Space 的新数据包到达时，产生一次中断，网卡完成数据发送，产生第二次时中断
此外，每次 Kernel Space 将数据拷贝到 硬件缓冲 都会引发硬件中断，当大量数据包拥堵时，反复中断引发的上下文拷贝，产生大量的冗余消耗，也就是<strong>中断风暴</strong>。</p>

<h4 id="内核旁路技术以-intel-dpdk-为例">内核旁路技术：以 Intel DPDK 为例</h4>

<pre><code class="language-mermaid">graph LR
    %% 从左到右的数据流
    subgraph Hardware["网卡硬件"]
        G["物理介质"]
        F["硬件队列"]
    end
    
    subgraph Kernel["内核态"]
        E["UIO/VFIO"]
    end
    
    subgraph UserSpace["用户态"]
        C["PMD驱动"]
        D["内存管理"]
        B["DPDK库"]
        A["应用程序"]
    end

    %% 数据流向
    G --&gt; F --&gt; C --&gt; D --&gt; B --&gt; A
    E --&gt;|映射| C
    E --&gt;|DMA| F
    C --&gt; B --&gt; A
</code></pre>

<p>DPDK（Data Plane Development Kit）是一个开源工具包，提供了一组数据平面库和网络接口控制器<strong>轮询模式</strong>驱动程序。</p>

<p>可以将 TCP 数据包的处理，从<strong>内核空间</strong>中转移到<strong>用户空间</strong>中的进程里完成。</p>

<p>DPDK 特点就是：</p>

<ul>
  <li>用户态绕过内核协议栈直接访问 NIC Buffer</li>
  <li>零拷贝 + 多核并发 + 高吞吐</li>
  <li>适用于高频交易、SDN</li>
</ul>

<h4 id="优化内核中断机制">优化内核中断机制</h4>

<p><strong>epoll</strong> 机制：在内核空间中，构建epoll对象，每次中断，将只需要保存指定的上下文描述</p>

<p><strong>busy-polling</strong> 机制：开启忙轮询设置后，CPU 将会在空闲状态下，主动每隔一段时间主动对缓存进行查看。</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 启用socket级别的忙轮询</span>
<span class="kt">int</span> <span class="nf">enable_busy_poll</span><span class="p">(</span><span class="kt">int</span> <span class="n">sockfd</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">val</span><span class="p">;</span>
    
    <span class="c1">// SO_BUSY_POLL: 当没有数据时，内核轮询驱动的时间(微秒)</span>
    <span class="n">val</span> <span class="o">=</span> <span class="mi">50</span><span class="p">;</span>  <span class="c1">// 50微秒</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">SO_BUSY_POLL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">val</span><span class="p">))</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"setsockopt SO_BUSY_POLL"</span><span class="p">);</span>
    <span class="p">}</span>
    
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>上面socket级别的忙轮询依然是从用户态发起的，内核级别的忙轮询可以参考下面的配置。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## 启用内核级别的忙轮询</span>
<span class="nb">echo </span>0 <span class="o">&gt;</span> /proc/sys/net/core/netdev_budget      <span class="c">## 减少NAPI处理量</span>
<span class="nb">echo </span>1 <span class="o">&gt;</span> /proc/sys/net/core/busy_poll          <span class="c">## 启用繁忙轮询</span>
<span class="nb">echo </span>50 <span class="o">&gt;</span> /proc/sys/net/core/busy_read         <span class="c">## 繁忙读取超时(微秒)</span>
</code></pre></div></div>

<h3 id="11-数据包的延迟会出现在哪些环节">11. 数据包的延迟会出现在哪些环节？</h3>

<pre><code class="language-mermaid">sequenceDiagram
    autonumber
    participant UA as 用户应用（User Space）
    participant K as 系统内核（Kernel）
    participant NIC as 驱动 / 网卡（Driver &amp; NIC）
    participant L as 物理链路（光纤/铜缆）
    participant SR as 交换/路由设备（中间节点，若多跳可理解为多台）
    participant RNIC as 远端网卡
    participant RK as 远端内核
    participant RA as 远端应用

    UA-&gt;&gt;K: send()/write() 等系统调用
    Note over UA,K: [A] 系统层级延迟

    K-&gt;&gt;K: TCP/IP 协议栈处理
    Note right of K: [B] 内核层级延迟

    K-&gt;&gt;NIC: 报文下发到驱动/NIC
    Note over K,NIC: [C] Drive/NIC 延迟

    NIC-&gt;&gt;L: Byte上线
    L-&gt;&gt;SR: 入交换/路由设备
    Note over NIC,SR: [D1] 物理传播延迟

    SR-&gt;&gt;SR: 设备处理/转发
    Note right of SR: [D2] 交换/路由转发

    SR--&gt;&gt;SR: 排队等待（突发/拥塞时）
    Note right of SR: [D3] 排队延迟（Bufferbloat）

    SR-&gt;&gt;L: 出口端口发出
    Note right of L: [D1] 继续传播（若多跳则重复 D1/D2/D3）

    L-&gt;&gt;RNIC: 到达远端网卡
    RNIC-&gt;&gt;RK: DMA 上送/中断
    Note over RNIC,RK: [C] 对端 Drive/NIC 延迟

    RK-&gt;&gt;RK: 远端 TCP/IP 协议栈处理
    Note right of RK: [B] 对端内核协议栈延迟

    RK-&gt;&gt;RA: 递交到远端应用
    Note over RK,RA: [A] 对端系统层级延迟
</code></pre>

<ol>
  <li><strong>[A]</strong> 系统层级延迟：从 User Space 到 Kernal Space 调用延迟。</li>
  <li><strong>[B]</strong> 内核层级延迟：内核协议栈，如 TCP/IP，协议的处理延迟。</li>
  <li><strong>[C]</strong> Drive/NIC 层级延迟：硬件驱动层 到 网卡（NIC）造成的延迟。</li>
  <li><strong>[D]</strong> 物理层级传输延迟：
    <ol>
      <li><strong>[D1]</strong> 光纤延迟:约 5 μs/km。</li>
      <li><strong>[D2]</strong> 交换/路由器处理延迟。</li>
      <li><strong>[D3]</strong> 数据包排队延迟Bufferbloat。</li>
    </ol>
  </li>
</ol>

<h3 id="12-可以采用哪些方案优化延迟">12. 可以采用哪些方案优化延迟？</h3>

<p>上面提到的延迟来源，每个阶段都有优化方案。</p>

<h4 id="a-系统层级延迟优化">[A] 系统层级延迟优化</h4>

<ul>
  <li>使用内核旁路 Kernel Bypass 方案绕过 Kernal Space，使用 DPDK 编写发包模块。</li>
  <li>
    <p>优化线程的 CPU Affinity ，将关键的进程、线程，物理中断（IRQ），绑定到特定的 CPU Core 上，避免操作系统调度器将系统任务在核心间迁移。从而保证缓存热度、减少上下文切换。<code class="language-plaintext highlighter-rouge">sched_setaffinity</code> 系统调用来设置线程的CPU亲和性绑定 CPU core。</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## 在 GRUB 配置中将指定 CPU 隔离：isolcpus=1,2,3</span>
<span class="c">## 将 my_program 启动，并将其绑定到核心 2 和 3 上</span>
taskset <span class="nt">-c</span> 2,3 ./my_program

<span class="c">## 对于一个已经运行的进程 PID=12345，将其绑定到核心 1</span>
taskset <span class="nt">-pc</span> 1 12345
  
<span class="c">## 将中断号90绑定到CPU核心2</span>
<span class="nb">echo </span>2 <span class="o">&gt;</span> /proc/irq/90/smp_affinity
</code></pre></div>    </div>
  </li>
</ul>

<h4 id="b-内核协议层级延迟优化">[B] 内核/协议层级延迟优化</h4>

<ul>
  <li>关闭 TCP 协议中 <a href="http://lunxunmedium.com/@elouadinouhaila566/the-nagle-algorithm-a-simple-solution-to-a-complex-problem-0c66715663dc">Nagle</a> 配置</li>
  <li>
    <p>使用 <code class="language-plaintext highlighter-rouge">SO_RCVBUF</code> / <code class="language-plaintext highlighter-rouge">SO_SNDBUF</code> 调整 socket 缓冲区</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 小缓冲区策略 - 减少数据在内核中的停留时间</span>
<span class="n">set_socket_buffers</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="mi">64</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">32</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">);</span> <span class="c1">// 64KB接收，32KB发送</span>
<span class="c1">// 大缓冲区策略 - 提高批量传输效率</span>
<span class="n">set_socket_buffers</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">);</span> <span class="c1">// 4MB接收，2MB发送</span>
</code></pre></div>    </div>
  </li>
</ul>

<h4 id="c-drivenic-层级延迟优化">[C] Drive/NIC 层级延迟优化</h4>

<ul>
  <li>DPDK 内核旁路模式，User Space 直接从 NIC Buffer 的映射中读取数据。</li>
  <li>轮询策略优化：
    <ul>
      <li><code class="language-plaintext highlighter-rouge">epoll</code>：使用 epoll 取代 select 和poll 模式，提升内核态处理中断的效率。</li>
      <li><code class="language-plaintext highlighter-rouge">busypoll</code>：使用 <code class="language-plaintext highlighter-rouge">busy_poll</code> 套接字接口读取数据，牺牲 CPU 性能，换取最快读取时间。</li>
    </ul>
  </li>
  <li>缓存优化：
    <ul>
      <li>使用巨页（Huge Pages）提高内存访问效率，降低快表（TLB）缺失的概率。</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="reference">Reference</h2>

<ul>
  <li><a href="https://medium.com/@avocadi/what-is-epoll-9bbc74272f7c">Medium｜What’s epoll?</a></li>
  <li><a href="https://netdevconf.org/2.1/papers/BusyPollingNextGen.pdf">Google文章｜Busy Polling: Past, Present, Future</a></li>
  <li><a href="https://github.com/DPDK/dpdk">DPDK Repo</a></li>
  <li><a href="https://www.bilibili.com/video/BV1dtNtzvEPS">25-网络协议栈与系统实现：Linux系统</a></li>
  <li><a href="https://www.bilibili.com/video/BV1GQN4zcEXR">26-网络协议栈与系统实现：Intel DPDK</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Nagle%27s_algorithm#Algorithm">Nagle’s algorithm</a></li>
  <li><a href="http://lunxunmedium.com/@elouadinouhaila566/the-nagle-algorithm-a-simple-solution-to-a-complex-problem-0c66715663dc">The Nagle Algorithm: A Simple Solution to a Complex Problem</a></li>
</ul>]]></content><author><name>Shujia Xu</name></author><category term="Linux" /><category term="Network" /><category term="DPDK" /><summary type="html"><![CDATA[在高频量化公司的面试中，听到的一些网络问题。从最基础的 TCP 三次握手开始，顺势聊到了高频交易中实用的一些网络优化方案。结合后续网上补课看到的一些技术，总结的一下这些题目相关的网络入门知识。]]></summary></entry><entry><title type="html">Linux 网络面试题 Part03：滑动窗口 Scaling Windows</title><link href="https://sjxu-base.github.io/linux/Network-Interview-03/" rel="alternate" type="text/html" title="Linux 网络面试题 Part03：滑动窗口 Scaling Windows" /><published>2025-06-18T00:00:00+08:00</published><updated>2025-06-18T00:00:00+08:00</updated><id>https://sjxu-base.github.io/linux/Network-Interview-03</id><content type="html" xml:base="https://sjxu-base.github.io/linux/Network-Interview-03/"><![CDATA[<p>在高频量化公司的面试中，听到的一些网络问题。顺势聊到了高频交易中实用的一些网络优化方案。结合后续网上补课看到的一些技术，总结的一下这些题目相关的网络入门知识。</p>

<h2 id="0x03-tcp-中的窗口概念">0x03 TCP 中的窗口概念</h2>

<h3 id="10-tcp-中有哪些窗口">10. TCP 中有哪些窗口</h3>

<h4 id="传统网络模型及限制">传统网络模型及限制</h4>

<p>传统网络模型<em>(Linux&lt; 2.5.44)</em>中，一个数据包的发送了流程可以参考下图：</p>

<pre><code class="language-mermaid">graph TD
    subgraph UserSpace["用户态 (User Space)"]
        A["应用程序 (Application)"]
        B["Socket API"]
    end;

    subgraph KernelSpace["内核态 (Kernel Space)"]
        C["TCP/UDP 协议栈"]
        D["IP Layer"]
        E["MAC/以太网 Layer"]
        F["NIC Driver"]
    end

    subgraph HW["NIC Hardware"]
        G["硬件队列 (TX/RX Queues)"]
        H["物理介质 (Wire)"]
    end

    A --&gt; B --&gt; C --&gt; D --&gt; E --&gt; F --&gt; G --&gt; H
    H --&gt; G --&gt; F --&gt; E --&gt; D --&gt; C --&gt; B --&gt; A
</code></pre>

<p>可以看到，一个数据包从用户空间到物理网卡至少要经过<strong>两次</strong>拷贝：</p>

<ul>
  <li>第一次拷贝：从用户空间缓冲区 → 内核空间缓冲区
    <ul>
      <li>通过 系统调用触发，如<code class="language-plaintext highlighter-rouge">send()</code></li>
      <li>涉及用户态到内核态的上下文切换</li>
    </ul>
  </li>
  <li>第二次拷贝：从内核空间缓冲区 → 网卡DMA缓冲区
    <ul>
      <li>内核协议栈处理完成后</li>
      <li>通过 DMA 引擎传输，但仍需 CPU 中断</li>
    </ul>
  </li>
</ul>

<p>这中间，当来自 User Space 的新数据包到达时，产生一次中断，网卡完成数据发送，产生第二次时中断
此外，每次 Kernel Space 将数据拷贝到 硬件缓冲 都会引发硬件中断，当大量数据包拥堵时，反复中断引发的上下文拷贝，产生大量的冗余消耗，也就是<strong>中断风暴</strong>。</p>

<h4 id="内核旁路技术以-intel-dpdk-为例">内核旁路技术：以 Intel DPDK 为例</h4>

<pre><code class="language-mermaid">graph LR
    %% 从左到右的数据流
    subgraph Hardware["网卡硬件"]
        G["物理介质"]
        F["硬件队列"]
    end
    
    subgraph Kernel["内核态"]
        E["UIO/VFIO"]
    end
    
    subgraph UserSpace["用户态"]
        C["PMD驱动"]
        D["内存管理"]
        B["DPDK库"]
        A["应用程序"]
    end

    %% 数据流向
    G --&gt; F --&gt; C --&gt; D --&gt; B --&gt; A
    E --&gt;|映射| C
    E --&gt;|DMA| F
    C --&gt; B --&gt; A
</code></pre>

<p>DPDK（Data Plane Development Kit）是一个开源工具包，提供了一组数据平面库和网络接口控制器<strong>轮询模式</strong>驱动程序。</p>

<p>可以将 TCP 数据包的处理，从<strong>内核空间</strong>中转移到<strong>用户空间</strong>中的进程里完成。</p>

<p>DPDK 特点就是：</p>

<ul>
  <li>用户态绕过内核协议栈直接访问 NIC Buffer</li>
  <li>零拷贝 + 多核并发 + 高吞吐</li>
  <li>适用于高频交易、SDN</li>
</ul>

<h4 id="优化内核中断机制">优化内核中断机制</h4>

<p><strong>epoll</strong> 机制：在内核空间中，构建epoll对象，每次中断，将只需要保存指定的上下文描述</p>

<p><strong>busy-polling</strong> 机制：开启忙轮询设置后，CPU 将会在空闲状态下，主动每隔一段时间主动对缓存进行查看。</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 启用socket级别的忙轮询</span>
<span class="kt">int</span> <span class="nf">enable_busy_poll</span><span class="p">(</span><span class="kt">int</span> <span class="n">sockfd</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">val</span><span class="p">;</span>
    
    <span class="c1">// SO_BUSY_POLL: 当没有数据时，内核轮询驱动的时间(微秒)</span>
    <span class="n">val</span> <span class="o">=</span> <span class="mi">50</span><span class="p">;</span>  <span class="c1">// 50微秒</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">SO_BUSY_POLL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">val</span><span class="p">))</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">perror</span><span class="p">(</span><span class="s">"setsockopt SO_BUSY_POLL"</span><span class="p">);</span>
    <span class="p">}</span>
    
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>上面socket级别的忙轮询依然是从用户态发起的，内核级别的忙轮询可以参考下面的配置。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## 启用内核级别的忙轮询</span>
<span class="nb">echo </span>0 <span class="o">&gt;</span> /proc/sys/net/core/netdev_budget      <span class="c">## 减少NAPI处理量</span>
<span class="nb">echo </span>1 <span class="o">&gt;</span> /proc/sys/net/core/busy_poll          <span class="c">## 启用繁忙轮询</span>
<span class="nb">echo </span>50 <span class="o">&gt;</span> /proc/sys/net/core/busy_read         <span class="c">## 繁忙读取超时(微秒)</span>
</code></pre></div></div>

<h3 id="11-数据包的延迟会出现在哪些环节">11. 数据包的延迟会出现在哪些环节？</h3>

<pre><code class="language-mermaid">sequenceDiagram
    autonumber
    participant UA as 用户应用（User Space）
    participant K as 系统内核（Kernel）
    participant NIC as 驱动 / 网卡（Driver &amp; NIC）
    participant L as 物理链路（光纤/铜缆）
    participant SR as 交换/路由设备（中间节点，若多跳可理解为多台）
    participant RNIC as 远端网卡
    participant RK as 远端内核
    participant RA as 远端应用

    UA-&gt;&gt;K: send()/write() 等系统调用
    Note over UA,K: [A] 系统层级延迟

    K-&gt;&gt;K: TCP/IP 协议栈处理
    Note right of K: [B] 内核层级延迟

    K-&gt;&gt;NIC: 报文下发到驱动/NIC
    Note over K,NIC: [C] Drive/NIC 延迟

    NIC-&gt;&gt;L: Byte上线
    L-&gt;&gt;SR: 入交换/路由设备
    Note over NIC,SR: [D1] 物理传播延迟

    SR-&gt;&gt;SR: 设备处理/转发
    Note right of SR: [D2] 交换/路由转发

    SR--&gt;&gt;SR: 排队等待（突发/拥塞时）
    Note right of SR: [D3] 排队延迟（Bufferbloat）

    SR-&gt;&gt;L: 出口端口发出
    Note right of L: [D1] 继续传播（若多跳则重复 D1/D2/D3）

    L-&gt;&gt;RNIC: 到达远端网卡
    RNIC-&gt;&gt;RK: DMA 上送/中断
    Note over RNIC,RK: [C] 对端 Drive/NIC 延迟

    RK-&gt;&gt;RK: 远端 TCP/IP 协议栈处理
    Note right of RK: [B] 对端内核协议栈延迟

    RK-&gt;&gt;RA: 递交到远端应用
    Note over RK,RA: [A] 对端系统层级延迟
</code></pre>

<ol>
  <li><strong>[A]</strong> 系统层级延迟：从 User Space 到 Kernal Space 调用延迟。</li>
  <li><strong>[B]</strong> 内核层级延迟：内核协议栈，如 TCP/IP，协议的处理延迟。</li>
  <li><strong>[C]</strong> Drive/NIC 层级延迟：硬件驱动层 到 网卡（NIC）造成的延迟。</li>
  <li><strong>[D]</strong> 物理层级传输延迟：
    <ol>
      <li><strong>[D1]</strong> 光纤延迟:约 5 μs/km。</li>
      <li><strong>[D2]</strong> 交换/路由器处理延迟。</li>
      <li><strong>[D3]</strong> 数据包排队延迟Bufferbloat。</li>
    </ol>
  </li>
</ol>

<h3 id="12-可以采用哪些方案优化延迟">12. 可以采用哪些方案优化延迟？</h3>

<p>上面提到的延迟来源，每个阶段都有优化方案。</p>

<h4 id="a-系统层级延迟优化">[A] 系统层级延迟优化</h4>

<ul>
  <li>使用内核旁路 Kernel Bypass 方案绕过 Kernal Space，使用 DPDK 编写发包模块。</li>
  <li>
    <p>优化线程的 CPU Affinity ，将关键的进程、线程，物理中断（IRQ），绑定到特定的 CPU Core 上，避免操作系统调度器将系统任务在核心间迁移。从而保证缓存热度、减少上下文切换。<code class="language-plaintext highlighter-rouge">sched_setaffinity</code> 系统调用来设置线程的CPU亲和性绑定 CPU core。</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## 在 GRUB 配置中将指定 CPU 隔离：isolcpus=1,2,3</span>
<span class="c">## 将 my_program 启动，并将其绑定到核心 2 和 3 上</span>
taskset <span class="nt">-c</span> 2,3 ./my_program

<span class="c">## 对于一个已经运行的进程 PID=12345，将其绑定到核心 1</span>
taskset <span class="nt">-pc</span> 1 12345
  
<span class="c">## 将中断号90绑定到CPU核心2</span>
<span class="nb">echo </span>2 <span class="o">&gt;</span> /proc/irq/90/smp_affinity
</code></pre></div>    </div>
  </li>
</ul>

<h4 id="b-内核协议层级延迟优化">[B] 内核/协议层级延迟优化</h4>

<ul>
  <li>关闭 TCP 协议中 <a href="http://lunxunmedium.com/@elouadinouhaila566/the-nagle-algorithm-a-simple-solution-to-a-complex-problem-0c66715663dc">Nagle</a> 配置</li>
  <li>
    <p>使用 <code class="language-plaintext highlighter-rouge">SO_RCVBUF</code> / <code class="language-plaintext highlighter-rouge">SO_SNDBUF</code> 调整 socket 缓冲区</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 小缓冲区策略 - 减少数据在内核中的停留时间</span>
<span class="n">set_socket_buffers</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="mi">64</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">32</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">);</span> <span class="c1">// 64KB接收，32KB发送</span>
<span class="c1">// 大缓冲区策略 - 提高批量传输效率</span>
<span class="n">set_socket_buffers</span><span class="p">(</span><span class="n">sockfd</span><span class="p">,</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">);</span> <span class="c1">// 4MB接收，2MB发送</span>
</code></pre></div>    </div>
  </li>
</ul>

<h4 id="c-drivenic-层级延迟优化">[C] Drive/NIC 层级延迟优化</h4>

<ul>
  <li>DPDK 内核旁路模式，User Space 直接从 NIC Buffer 的映射中读取数据。</li>
  <li>轮询策略优化：
    <ul>
      <li><code class="language-plaintext highlighter-rouge">epoll</code>：使用 epoll 取代 select 和poll 模式，提升内核态处理中断的效率。</li>
      <li><code class="language-plaintext highlighter-rouge">busypoll</code>：使用 <code class="language-plaintext highlighter-rouge">busy_poll</code> 套接字接口读取数据，牺牲 CPU 性能，换取最快读取时间。</li>
    </ul>
  </li>
  <li>缓存优化：
    <ul>
      <li>使用巨页（Huge Pages）提高内存访问效率，降低快表（TLB）缺失的概率。</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="reference">Reference</h2>

<ul>
  <li><a href="https://medium.com/@avocadi/what-is-epoll-9bbc74272f7c">Medium｜What’s epoll?</a></li>
  <li><a href="https://netdevconf.org/2.1/papers/BusyPollingNextGen.pdf">Google文章｜Busy Polling: Past, Present, Future</a></li>
  <li><a href="https://github.com/DPDK/dpdk">DPDK Repo</a></li>
  <li><a href="https://www.bilibili.com/video/BV1dtNtzvEPS">25-网络协议栈与系统实现：Linux系统</a></li>
  <li><a href="https://www.bilibili.com/video/BV1GQN4zcEXR">26-网络协议栈与系统实现：Intel DPDK</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Nagle%27s_algorithm#Algorithm">Nagle’s algorithm</a></li>
  <li><a href="http://lunxunmedium.com/@elouadinouhaila566/the-nagle-algorithm-a-simple-solution-to-a-complex-problem-0c66715663dc">The Nagle Algorithm: A Simple Solution to a Complex Problem</a></li>
</ul>]]></content><author><name>Shujia Xu</name></author><category term="Linux" /><category term="Network" /><category term="TCP" /><summary type="html"><![CDATA[在高频量化公司的面试中，听到的一些网络问题。顺势聊到了高频交易中实用的一些网络优化方案。结合后续网上补课看到的一些技术，总结的一下这些题目相关的网络入门知识。]]></summary></entry><entry><title type="html">Linux 网络面试题 Part01：协议和子网</title><link href="https://sjxu-base.github.io/linux/Network-Interview-01/" rel="alternate" type="text/html" title="Linux 网络面试题 Part01：协议和子网" /><published>2025-06-17T00:00:00+08:00</published><updated>2025-06-17T00:00:00+08:00</updated><id>https://sjxu-base.github.io/linux/Network-Interview-01</id><content type="html" xml:base="https://sjxu-base.github.io/linux/Network-Interview-01/"><![CDATA[<p>在高频量化公司的面试中，听到的一些网络问题。从最基础的 TCP 三次握手开始，顺势聊到了高频交易中实用的一些网络优化方案。结合后续网上补课看到的一些技术，总结的一下这些题目相关的网络入门知识。</p>

<h2 id="0x01-计算机网络基础">0x01 计算机网络基础</h2>

<h3 id="1-tcp-三次握手与四次挥手">1. TCP 三次握手与四次挥手</h3>

<p>TCP 数据包头部共 20 字节，结构参考下图：</p>

<figure style="text-align: center;">
    <img src="/assets/images/posts/20250617/tcp_header.png" alt="tcp_header" width="750" height="210" />
    <figcaption>TCP Header</figcaption>
</figure>

<ul>
  <li>0-4   字节：SrcIP / DstIP</li>
  <li>5-8   字节：<strong>Sequence Number</strong></li>
  <li>9-12  字节：<strong>Acknowledge Number</strong></li>
  <li>13-16 字节：
    <ul>
      <li>13-14 字节：4 offset + 6 Reserved Data + 6 <strong>Signal</strong></li>
      <li>15-16 字节：Window 大小</li>
    </ul>
  </li>
  <li>17-20 字节：
    <ul>
      <li>17-18 字节：校验和</li>
      <li>19-20 字节：紧急指针</li>
    </ul>
  </li>
</ul>

<p>13-14 byte 的最后 6 bit 为信号标志位，其中就包括 SYN、ACK、FIN 标志位。</p>

<h4 id="三次握手流程">三次握手流程</h4>

<p>构建 TCP 连接时，三次握手顺序如下，Client 和 Server 都会出一个随机的 <strong>Sequence Number</strong>，ACK 中返回 seq+1 作为 ACK 编码。</p>

<pre><code class="language-mermaid">sequenceDiagram
  Client-)Server: SYN
  Server-)Client: SYN+ACK
  Client-)Server: ACK
</code></pre>

<h4 id="why-not为什么两次无法结束连接">Why Not：为什么两次无法结束连接</h4>

<blockquote>
  <p>假如将 TCP 三次握手流程改为两次。
Client 端不需要确认 Server 端第二次握手发送的 SYN 包，直接传输数据。</p>
</blockquote>

<p><strong>BAD CASE</strong></p>

<p>假设某个 Client 发送的第一个连接请求 SYN-1 因网络延迟而滞留，Client 因未接到 SYN-1-ACK，选择重发，发送了第二个请求 SYN-2 并成功建立了连接，并在数据传输完毕后释放 FIN-2 构造的连接。</p>

<p>此时，延迟的 SYN-1 才到达 Server 端，Server 端将会正常构造连接，而 Client 无法拒绝错误回应，将会被动产生新连接。</p>

<h4 id="四次挥手流程">四次挥手流程</h4>

<p>因为 TCP 是<strong>全双工模式连接</strong>，所以双方结束连接时，需各自释放连接。</p>

<p>四次挥手过程如下：</p>

<pre><code class="language-mermaid">  sequenceDiagram
    Client-&gt;&gt;Server: FIN
    activate Client
    participant Client
    Note left of Client: FIN-WAIT-1
    Server-&gt;&gt;Client: ACK
    deactivate Client
    participant Client
    Note left of Client: FIN-WAIT-2
    Server-&gt;&gt;Client: FIN
    activate Server
    participant Server
    Note right of Server: LAST-ACK
    Client-&gt;&gt;Server: ACK
    deactivate Server
    Note left of Client: TIME-WAIT(2MSL)
    Note right of Server: CLOSE
    Note left of Client: CLOSE
</code></pre>

<h4 id="why-not为什么三次无法结束连接">Why Not：为什么三次无法结束连接</h4>

<blockquote>
  <p>假如将 TCP 四次挥手流程改为三次。
Server 端不等待 Client 端回应第三次挥手请求，直接关闭套接字。</p>
</blockquote>

<p><strong>BAD CASE</strong></p>

<p>第四次挥手时，客户端发送给服务端的 ACK 有可能丢失，如果服务端因为某些原因而没有收到 ACK 的话，服务端就会重发 FIN。</p>

<p>如果客户端在 2x MSL 内收到了 FIN，就会重新发送 ACK 并再次等待 2x MSL，防止 Server 没有收到 ACK 而不断重发 FIN…</p>

<h3 id="2-tcp-和-udp-特点和适用场景">2. TCP 和 UDP 特点和适用场景</h3>

<table>
  <thead>
    <tr>
      <th>协议</th>
      <th>是否连接</th>
      <th>是否可靠</th>
      <th>适用场景</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>TCP</td>
      <td>有连接（三次握手）</td>
      <td>可靠、有序、支持重传、拥塞控制</td>
      <td>Web、文件传输</td>
    </tr>
    <tr>
      <td>UDP</td>
      <td>无连接</td>
      <td>不可靠、不支持重传</td>
      <td>视频、语音、DNS、DHCP</td>
    </tr>
  </tbody>
</table>

<h3 id="3-什么是-mtu-mss-window-scaling-nagle算法">3. 什么是 MTU, MSS, Window Scaling, Nagle算法</h3>

<ul>
  <li>MTU（最大传输单元）：物理网中传输的最大单元，包含协议头部及数据，以太网一般为 1500 bytes。</li>
  <li>MSS（最大报文段）：TCP 数据报大小，约为 MTU - 40，即给协议头部留 40 bytes。</li>
  <li>Window Scaling：TCP 窗口调整，支持高带宽延迟网络。</li>
  <li><a href="(https://en.wikipedia.org/wiki/Nagle%27s_algorithm#Algorithm)">Nagle 算法</a>：合并小包发送，降低发包频率，减少对系统内核造成的载荷花，但会造成更高的平均延迟。</li>
</ul>

<h3 id="4-简述-dns-原理递归-vs-迭代查询">4. 简述 DNS 原理，递归 vs 迭代查询</h3>

<ul>
  <li>递归查询
    <ul>
      <li>客户端 → 本地 DNS</li>
      <li>请求流程：客户端只发 <strong>一次请求</strong>，要求对方给出最终结果。由 <strong>本地域名服务器 (Local DNS)</strong> 一口气查到最终 IP 并返回。</li>
      <li>返回结果：查询成功或查询失败。</li>
    </ul>
  </li>
  <li>迭代查询：
    <ul>
      <li>客户端 → 本地DNS/向根、客户端 → TLD、客户端 → 权威DNS</li>
      <li>请求流程：客户端发出 <strong>多次请求</strong>，对方如果没有授权回答，它就会返回一个能解答这个查询的其它名称<strong>服务器列表</strong>。客户端会再向返回的列表中发出请求，直到找到最终负责所查域名的名称服务器，从它得到最终结果。</li>
      <li>返回结果：最佳的查询点 或 主机地址。</li>
    </ul>
  </li>
</ul>

<pre><code class="language-mermaid">sequenceDiagram
    autonumber
    participant Client as 客户端
    participant LDNS as 本地DNS解析器
    participant Root as 根DNS服务器
    participant TLD as 顶级域DNS（.com / .cn）
    participant Auth as 权威DNS服务器

    %% 递归查询路径
    rect rgb(240,240,255)
    Note over Client,LDNS: 🌀 递归查询（本地DNS替客户端完成所有查询）
    Client-&gt;&gt;LDNS: 请求 www.example.com 的 IP（递归查询）
    LDNS-&gt;&gt;Root: 询问根服务器（.com 在哪？）
    Root--&gt;&gt;LDNS: 返回 .com 顶级域的地址
    LDNS-&gt;&gt;TLD: 询问 .com 域（example.com 在哪？）
    TLD--&gt;&gt;LDNS: 返回 example.com 的权威DNS地址
    LDNS-&gt;&gt;Auth: 询问 example.com 权威DNS
    Auth--&gt;&gt;LDNS: 返回 www.example.com 的 IP
    LDNS--&gt;&gt;Client: 返回最终 IP 地址
    end

    %% 迭代查询路径
    rect rgb(255,250,230)
    Note over Client,Auth: 🔁 迭代查询（DNS服务器仅返回下一跳）
    Client-&gt;&gt;Root: 直接询问根DNS（www.example.com）
    Root--&gt;&gt;Client: 告诉你 .com 顶级域DNS在哪
    Client-&gt;&gt;TLD: 继续询问 .com DNS
    TLD--&gt;&gt;Client: 告诉你 example.com 权威DNS在哪
    Client-&gt;&gt;Auth: 最后询问权威DNS
    Auth--&gt;&gt;Client: 返回 www.example.com 的 IP
    end
</code></pre>

<h3 id="5-底层协议什么是-arpdhcp-协议">5. 底层协议：什么是 ARP、DHCP 协议？</h3>

<ul>
  <li>ARP：IP → MAC 映射，二层通信。</li>
  <li>DHCP：动态分配IP地址。</li>
</ul>

<h3 id="6-子网管理什么是-nat-vlan-子网划分功能">6. 子网管理：什么是 NAT, VLAN, 子网划分功能？</h3>

<ul>
  <li>NAT：负责私网 → 公网地址转换。</li>
  <li>VLAN：逻辑隔离二层广播域。</li>
  <li>子网划分：CIDR 记法（如 192.168.1.0/24）控制子网主机数与网络规模。</li>
</ul>

<h2 id="0x02-网络性能与调优">0x02 网络性能与调优</h2>

<h3 id="7-linux-下如何查看网络连接和延迟">7. Linux 下如何查看网络连接和延迟</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ss -tuna</code> / <code class="language-plaintext highlighter-rouge">netstat -anp</code>：查看TCP/UDP连接</li>
  <li><code class="language-plaintext highlighter-rouge">ping</code>：ICMP往返延迟（RTT）</li>
  <li><code class="language-plaintext highlighter-rouge">traceroute</code>：每一跳延迟</li>
  <li><code class="language-plaintext highlighter-rouge">iperf3</code>：TCP/UDP 吞吐测试</li>
  <li><code class="language-plaintext highlighter-rouge">tcpdump</code>：抓包分析（过滤如 <code class="language-plaintext highlighter-rouge">tcp port 80</code>）</li>
</ul>

<h3 id="8-如何排查高-rtt-成因丢包现象以及网络拥塞情况">8. 如何排查高 RTT 成因、丢包现象，以及网络拥塞情况？</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ping</code> 看波动 &amp; 丢包</li>
  <li><code class="language-plaintext highlighter-rouge">traceroute</code> 定位异常出现在哪跳连接</li>
  <li><code class="language-plaintext highlighter-rouge">iperf</code> 确认对端吞吐是否正常</li>
  <li><code class="language-plaintext highlighter-rouge">tcpdump</code> 确认 TCP 数据包中参数情况，着重看重传次数、窗口大小</li>
</ul>

<p><strong>指标关注</strong>：</p>

<ul>
  <li>RTT 大 → 拥塞或链路延迟</li>
  <li>丢包高 → 链路质量差 or buffer 溢出</li>
  <li>窗口小 → 滞后于 BDP，需 window scaling</li>
</ul>

<h3 id="9-简述-tcp-拥塞控制四阶段和内容">9. 简述 TCP 拥塞控制四阶段和内容</h3>

<p>TCP 在传输过程中，为了保证稳定传输，所以需要持续探测目前可行的数据窗口大小。</p>

<ul>
  <li><strong>慢启动（Slow Start）</strong>：初期指数增长 $cwnd$，收到一个 ack 增加一个新窗口。初始拥塞窗口（cwnd）一般为 1~10 个 MSS，每收到一个 ACK，<strong>窗口加倍</strong>（指数增长），快速探测带宽，但风险高。</li>
  <li><strong>拥塞避免</strong>：线性增长 cwnd。当 cwnd ≥ ssthresh（慢启动阈值）时，进入拥塞避免。每 RTT 增长<strong>线性</strong>（每轮 +1 MSS），稳健但增长慢。</li>
  <li><strong>快重传</strong>：收到 3 个重复ACK，立即重传。不等超时，若收到接收方 <strong>3 个重复 ACK</strong>（说明某个包丢了）。立即重发丢失的数据包。</li>
  <li><strong>快恢复</strong>：减半 cwnd，进入拥塞避免而非重头来。快重传后，说明网络可用，但有拥塞。避免 cwnd 退回 1，减少性能损失。调整策略可以用伪代码表述：
    <ul>
      <li>ssthresh = cwnd / 2</li>
      <li>cwnd = ssthresh（或 ssthresh + 3）</li>
      <li>进入<strong>拥塞避免阶段</strong>而不是重回慢启动</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="reference">Reference</h2>

<ul>
  <li><a href="https://medium.com/@kusal95/tcp-3-way-handshake-process-1fd9a056a2f4">TCP 3-Way Handshake Process</a></li>
  <li><a href="https://javaguide.cn/cs-basics/network/tcp-connection-and-disconnection.html">TCP 三次握手和四次挥手（传输层）</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Nagle%27s_algorithm#Algorithm">Nagle’s algorithm</a></li>
  <li><a href="http://lunxunmedium.com/@elouadinouhaila566/the-nagle-algorithm-a-simple-solution-to-a-complex-problem-0c66715663dc">The Nagle Algorithm: A Simple Solution to a Complex Problem</a></li>
</ul>]]></content><author><name>Shujia Xu</name></author><category term="Linux" /><category term="Network" /><category term="TCP" /><category term="DNS" /><summary type="html"><![CDATA[在高频量化公司的面试中，听到的一些网络问题。从最基础的 TCP 三次握手开始，顺势聊到了高频交易中实用的一些网络优化方案。结合后续网上补课看到的一些技术，总结的一下这些题目相关的网络入门知识。]]></summary></entry><entry><title type="html">Linux 系统监控和 Debug</title><link href="https://sjxu-base.github.io/linux/Linux-Monitoring/" rel="alternate" type="text/html" title="Linux 系统监控和 Debug" /><published>2025-06-16T00:00:00+08:00</published><updated>2025-06-16T00:00:00+08:00</updated><id>https://sjxu-base.github.io/linux/Linux-Monitoring</id><content type="html" xml:base="https://sjxu-base.github.io/linux/Linux-Monitoring/"><![CDATA[<h1 id="0x01-系统监控工具">0x01 系统监控工具</h1>

<h2 id="cpu">CPU</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">top</code> / <code class="language-plaintext highlighter-rouge">htop</code>：实时 CPU 使用率（三个数字代表1/5/15分钟内CPU执行进程的平均数）</li>
  <li><code class="language-plaintext highlighter-rouge">vmstat</code>：查看 CPU 的 user/sys/io 等占比</li>
  <li><code class="language-plaintext highlighter-rouge">mpstat</code>（来自 <code class="language-plaintext highlighter-rouge">sysstat</code>）：多核 CPU 使用情况</li>
  <li><code class="language-plaintext highlighter-rouge">perf top / perf record</code>：函数级别的 CPU 开销分析</li>
</ul>

<h2 id="memory">Memory</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">top</code> / <code class="language-plaintext highlighter-rouge">htop</code>：进程级内存占用</li>
  <li><code class="language-plaintext highlighter-rouge">free -h</code>：系统整体内存占用</li>
  <li><code class="language-plaintext highlighter-rouge">vmstat</code>：内存分页、swap 情况</li>
  <li><code class="language-plaintext highlighter-rouge">perf stat</code>：查看 cache miss</li>
  <li><code class="language-plaintext highlighter-rouge">sar -r</code>（来自 sysstat）：内存统计历史</li>
</ul>

<h2 id="io">I/O</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">iotop</code>：进程级磁盘 I/O</li>
  <li><code class="language-plaintext highlighter-rouge">iostat</code>（sysstat）查看设备级磁盘使用率</li>
  <li><code class="language-plaintext highlighter-rouge">dstat</code>：实时磁盘读写 + 网络数据</li>
  <li><code class="language-plaintext highlighter-rouge">strace</code>：追踪文件 read/write 系统调用</li>
</ul>

<h2 id="进程与线程">进程与线程</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">strace -p &lt;pid&gt;</code>：查看进程的系统调用序列</li>
  <li><code class="language-plaintext highlighter-rouge">perf trace</code>：按系统调用频率分析程序瓶颈，用于排查程序卡顿、慢调用、未响应等</li>
</ul>

<h2 id="网络与端口">网络与端口</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ss -tulnp</code> / <code class="language-plaintext highlighter-rouge">netstat</code>：查看端口监听</li>
  <li><code class="language-plaintext highlighter-rouge">lsof -i :80</code>：查看哪个进程占用了 80 端口</li>
  <li><code class="language-plaintext highlighter-rouge">dstat -n</code>：实时网络流量</li>
  <li><code class="language-plaintext highlighter-rouge">iftop</code> / <code class="language-plaintext highlighter-rouge">nethogs</code>（额外工具）：进程级网络流量监控</li>
</ul>

<h2 id="文件句柄--fd">文件句柄 / FD</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">lsof</code>：列出进程打开的所有文件、socket、FIFO</li>
  <li><code class="language-plaintext highlighter-rouge">ls /proc/&lt;pid&gt;/fd/</code>：直接查看 FD 数</li>
</ul>

<h1 id="0x02-问题排查思路">0x02 问题排查思路</h1>

<h2 id="系统瓶颈判断">系统瓶颈判断</h2>

<p>使用 <code class="language-plaintext highlighter-rouge">vmstat 1 5</code> 来快速定位系统瓶颈。通过观察 <code class="language-plaintext highlighter-rouge">r</code>, <code class="language-plaintext highlighter-rouge">b</code>, <code class="language-plaintext highlighter-rouge">si/so</code>, <code class="language-plaintext highlighter-rouge">wa</code>, 和 <code class="language-plaintext highlighter-rouge">cs</code> 字段</p>

<p>可以判断系统是 CPU 饱和、I/O 阻塞、还是内存不足</p>

<p>进而决定是否深入用 <code class="language-plaintext highlighter-rouge">iotop</code>, <code class="language-plaintext highlighter-rouge">perf</code>, 或 <code class="language-plaintext highlighter-rouge">top -H</code> 等工具进一步排查。</p>
<blockquote>

  <table>
    <thead>
      <tr>
        <th>现象</th>
        <th>可能问题</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><code class="language-plaintext highlighter-rouge">r</code> ≫ CPU 核心数</td>
        <td>CPU 排队严重</td>
      </tr>
      <tr>
        <td><code class="language-plaintext highlighter-rouge">b</code> 持续不为 0</td>
        <td>I/O 阻塞严重</td>
      </tr>
      <tr>
        <td><code class="language-plaintext highlighter-rouge">si/so</code> 持续大</td>
        <td>swap 活跃，内存不足</td>
      </tr>
      <tr>
        <td><code class="language-plaintext highlighter-rouge">wa</code> &gt; 20%</td>
        <td>I/O 等待瓶颈，磁盘慢</td>
      </tr>
      <tr>
        <td><code class="language-plaintext highlighter-rouge">cs</code> 很高</td>
        <td>线程调度过多，可能线程泄漏</td>
      </tr>
      <tr>
        <td><code class="language-plaintext highlighter-rouge">id</code> ≈ 0</td>
        <td>CPU 饱和</td>
      </tr>
    </tbody>
  </table>
</blockquote>

<h2 id="内存泄漏诊断">内存泄漏诊断</h2>

<p>程序内存占用不断上升，<strong>free -h 中可用内存持续下降</strong>；应用长时间运行后变慢、卡顿，甚至 OOM 被杀死；<code class="language-plaintext highlighter-rouge">top</code>/<code class="language-plaintext highlighter-rouge">htop</code> 中 RSS 或 VIRT 持续增大。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>free <span class="nt">-h</span>     <span class="c"># 查看内存变化</span>
vmstat 1 5   <span class="c"># 1s一次采样5次 性能监控结果</span>
top <span class="nt">-o</span> %MEM      <span class="c"># 按内存排序</span>
ps aux <span class="nt">--sort</span><span class="o">=</span>-%mem | <span class="nb">head
</span>watch <span class="nt">-n</span> 1 pmap <span class="nt">-x</span> &lt;pid&gt; | <span class="nb">grep </span>total <span class="c"># 查看进程内存增长趋势</span>
<span class="nb">cat</span> /proc/&lt;pid&gt;/status        <span class="c"># 排查是否是泄漏还是缓存</span>
</code></pre></div></div>

<h2 id="cpu-使用率诊断">CPU 使用率诊断</h2>

<p><code class="language-plaintext highlighter-rouge">top</code> 中 <code class="language-plaintext highlighter-rouge">%us</code> 或 <code class="language-plaintext highlighter-rouge">%sy</code> 占用高，<code class="language-plaintext highlighter-rouge">idle</code> 逼近 0%；<code class="language-plaintext highlighter-rouge">load average</code> 高于 CPU 核心数；响应慢、线程卡顿。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>top        <span class="c"># 判断是单核爆？还是所有核爆？</span>
mpstat <span class="nt">-P</span> ALL 1 5 <span class="c"># 1s一次采样5次 CPU核心使用情况</span>
top <span class="nt">-o</span> %CPU    <span class="c"># 定位高占用进程</span>
ps aux <span class="nt">--sort</span><span class="o">=</span>-%cpu | <span class="nb">head</span> <span class="c"># 查找PID</span>
top <span class="nt">-H</span> <span class="nt">-p</span> &lt;pid&gt;  <span class="c">#查找线程级别占用</span>
</code></pre></div></div>

<h2 id="线程异常诊断">线程异常诊断</h2>

<p>RootCause：程序线程数迅速增加，占用大量资源；上下文切换暴增（<code class="language-plaintext highlighter-rouge">cs</code> in <code class="language-plaintext highlighter-rouge">vmstat</code>）；进程崩溃、卡死、响应慢。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps <span class="nt">-eLf</span> | <span class="nb">wc</span> <span class="nt">-l</span>            <span class="c"># 系统总线程数</span>
ps <span class="nt">-L</span> <span class="nt">-p</span> &lt;pid&gt; | <span class="nb">wc</span> <span class="nt">-l</span>     <span class="c"># 某进程的线程数</span>
watch <span class="s2">"ps -L -p &lt;pid&gt; | wc -l"</span> <span class="c"># 监控线程增长，判断线程泄漏</span>
<span class="c"># 最后查看线程堆栈，判断线程池未回收、死循环</span>
</code></pre></div></div>

<h1 id="0x03-日常系统启动与服务管理">0x03 日常系统启动与服务管理</h1>

<h2 id="基础工具systemdjournalctlsystemctl">基础工具：systemd、journalctl、systemctl</h2>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">systemd</code> 系统管理服务</p>

    <p><code class="language-plaintext highlighter-rouge">systemd</code> 本身是一个<strong>初始化系统</strong>（init system），用于取代传统的 <code class="language-plaintext highlighter-rouge">SysVinit</code> 和 <code class="language-plaintext highlighter-rouge">Upstart</code>。</p>

    <p>启动时由内核调用，负责<strong>初始化系统、挂载文件系统、启动服务</strong>等。同时还包含<strong>定时器、登录会话管理、日志管理</strong>等子模块。</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">systemctl</code> 系统管理工具</p>

    <p>操控 <code class="language-plaintext highlighter-rouge">systemd</code> 的主力工具，支持启动、停止、查看、配置服务等操作。</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">journalctl</code> 诊断工具</p>

    <p>用于查看 systemd 的日志系统**，是 <code class="language-plaintext highlighter-rouge">systemd</code> 内建的日志管理工具，用来替代传统的 <code class="language-plaintext highlighter-rouge">/var/log/messages</code> 或 <code class="language-plaintext highlighter-rouge">rsyslog</code></p>
  </li>
</ul>

<h2 id="基础操作查看系统日志服务状态故障定位">基础操作：查看系统日志、服务状态、故障定位</h2>

<p>使用 <code class="language-plaintext highlighter-rouge">journalctl</code> 查看系统日志，非 <code class="language-plaintext highlighter-rouge">systemd</code> 系统打开 <code class="language-plaintext highlighter-rouge">/var/log</code> 查看系统日志</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>journalctl <span class="nt">-xe</span>         <span class="c"># 查看最近的错误日志（最常用）</span>
journalctl <span class="nt">-u</span> nginx    <span class="c"># 查看 nginx 服务日志</span>
journalctl <span class="nt">-f</span>          <span class="c"># 实时滚动日志（tail -f）</span>
journalctl <span class="nt">-b</span>          <span class="c"># 查看上次启动以来的日志</span>
</code></pre></div></div>

<p>使用 <code class="language-plaintext highlighter-rouge">systemctl</code> 查看服务状态，非 <code class="language-plaintext highlighter-rouge">systemd</code> 系统使用 <code class="language-plaintext highlighter-rouge">service</code> 命令（SysVinit / Upstart 通用）或服务 <code class="language-plaintext highlighter-rouge">init</code>脚本（极简系统、容器环境或 <code class="language-plaintext highlighter-rouge">service</code> 命令不存在的系统）来查看服务状态</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl status nginx       <span class="c"># 查看 nginx 服务状态</span>
systemctl is-active nginx    <span class="c"># 是否运行中（running）</span>
systemctl is-enabled nginx   <span class="c"># 是否开机自启</span>
systemctl restart nginx      <span class="c"># 重启服务</span>
systemctl list-units <span class="nt">--type</span><span class="o">=</span>service  <span class="c"># 查看所有服务状态</span>
</code></pre></div></div>

<p>其他内容诊断</p>

<table>
  <thead>
    <tr>
      <th>工具</th>
      <th>作用</th>
      <th>示例</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">top</code>/<code class="language-plaintext highlighter-rouge">htop</code></td>
      <td>总览资源使用情况</td>
      <td>CPU、内存、进程消耗</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">vmstat</code></td>
      <td>快速判断是 CPU、I/O 还是内存瓶颈</td>
      <td><code class="language-plaintext highlighter-rouge">vmstat 1 5</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">iotop</code></td>
      <td>找出磁盘 I/O 高的进程</td>
      <td><code class="language-plaintext highlighter-rouge">iotop -o -P</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">lsof</code></td>
      <td>查看进程打开的文件和端口</td>
      <td><code class="language-plaintext highlighter-rouge">lsof -p &lt;pid&gt;</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">strace</code></td>
      <td>跟踪系统调用</td>
      <td><code class="language-plaintext highlighter-rouge">strace -p &lt;pid&gt;</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">journalctl</code></td>
      <td>查看服务失败的具体日志</td>
      <td><code class="language-plaintext highlighter-rouge">journalctl -xe</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dmesg</code></td>
      <td>查看内核级别错误信息</td>
      <td>如硬盘/内存故障</td>
    </tr>
  </tbody>
</table>

<h1 id="0x04-文件系统与权限">0x04 文件系统与权限</h1>

<h2 id="基础概念inode软硬链接文件权限">基础概念：inode、软硬链接、文件权限</h2>

<ul>
  <li>
    <p>inode（索引节点）</p>

    <p>Linux 文件系统中，每个文件（包括目录）对应的元数据结构，记录了文件除文件名外所有信息。</p>
  </li>
  <li>
    <p>硬链接（Hard Link）</p>

    <p>多个文件名指向同一个 inode 的情况，即多个目录项共享一个实际文件内容。拥有相同的 inode。</p>

    <p>删除任意一个名字，文件实际内容不会丢失（除非最后一个 link 被删）。</p>

    <p><strong>不能跨分区、不能对目录创建硬链接</strong>。</p>
  </li>
  <li>
    <p>软链接（Symbolic Link / symlink）</p>

    <p>一个特殊类型的文件，指向另一个文件的路径名，类似 Windows 的快捷方式。</p>

    <p>拥有<strong>不同 inode</strong>，可以跨分区、可以指向目录，</p>

    <p>若原始文件被删除，则软链接失效（<strong>变成悬挂链接</strong>），可以是绝对路径或相对路径。</p>
  </li>
  <li>
    <p>文件权限（Permission）</p>

    <p>**权限中三类用户的顺序是 <code class="language-plaintext highlighter-rouge">User</code>/<code class="language-plaintext highlighter-rouge">Group</code>/<code class="language-plaintext highlighter-rouge">Others</code>，然后用 rwx 来去做区分，</p>

    <ul>
      <li>
        <p>文件的特殊权限 <strong>Special Permissions</strong></p>

        <ul>
          <li>
            <p><strong>SUID（Set User ID）</strong>当用户执行某个<strong>可执行文件</strong>时，程序会<strong>临时以该文件的所有者身份运行</strong>。<code class="language-plaintext highlighter-rouge">chmod u+s filename</code></p>
          </li>
          <li>
            <p><strong>SGID（Set Group ID）</strong>程序运行时以文件所属“用户组”的身份运行（作用类似 SUID 但针对组），在该目录中新建的文件/目录，其 group 会被<strong>强制继承父目录的组</strong>。<code class="language-plaintext highlighter-rouge">chmod g+s filename_or_dir</code></p>
          </li>
          <li>
            <p><strong>Sticky Bit</strong> 只对<strong>目录</strong>有效。启用后，<strong>只有文件的拥有者或 root</strong> 才能删除该目录下的文件。<code class="language-plaintext highlighter-rouge">chmod +t directory</code></p>
          </li>
        </ul>
      </li>
      <li>
        <p>特殊权限总结</p>
      </li>
    </ul>

    <table>
      <thead>
        <tr>
          <th>名称</th>
          <th>标志</th>
          <th>适用对象</th>
          <th>功能作用</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><strong>SUID</strong></td>
          <td><code class="language-plaintext highlighter-rouge">s</code></td>
          <td>可执行文件</td>
          <td>运行时使用文件“所有者”的权限</td>
        </tr>
        <tr>
          <td><strong>SGID</strong></td>
          <td><code class="language-plaintext highlighter-rouge">s</code></td>
          <td>可执行文件 / 目录</td>
          <td>可执行文件：使用“所属组”权限；目录：<strong>新建文件继承组</strong></td>
        </tr>
        <tr>
          <td><strong>Sticky Bit</strong></td>
          <td><code class="language-plaintext highlighter-rouge">t</code></td>
          <td>目录</td>
          <td>限制删除行为：只有文件拥有者可以删除自己的文件</td>
        </tr>
      </tbody>
    </table>
  </li>
</ul>

<h2 id="基础操作文件权限变更">基础操作：文件权限变更</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">chmod</code> —— 修改文件权限（<strong>change mode</strong>）<code class="language-plaintext highlighter-rouge">chmod [权限模式] 文件名</code></li>
  <li><code class="language-plaintext highlighter-rouge">chown</code> —— 修改文件所有者（<strong>change owner</strong>）<code class="language-plaintext highlighter-rouge">chown [user][:group] 文件名</code></li>
  <li><code class="language-plaintext highlighter-rouge">chgrp</code> —— 修改文件所属组（<strong>change group</strong>）<code class="language-plaintext highlighter-rouge">chgrp [groupname] 文件名</code></li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">umask</code> —— 设置默认权限掩码（<strong>user file creation mask</strong>），减去掩码才是真实权限</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">umask</span>         <span class="c"># 查看当前 umask（如 0022）</span>
<span class="nb">umask </span>0002    <span class="c"># 设置新建文件默认权限为 664，新建目录为 775</span>
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="进阶概念文件系统设计">进阶概念：文件系统设计</h2>

<p>实际工作中，在部署系统时，经常会遇到不同的文件管理场景，例如数据库、日志等。</p>

<p>如果前期文件系统设计没有考虑好，后期使用过程中就有可能出现下面这些问题：</p>

<ul>
  <li>inode 不足</li>
  <li>系统写入瓶颈</li>
  <li>文件丢失</li>
</ul>

<p>所以最佳实践应该是，在构建系统之初就做好文件系统选型，具体可以参考下面这些场景与用途。</p>

<table>
  <thead>
    <tr>
      <th>文件系统</th>
      <th>总结</th>
      <th>常见用途</th>
      <th>特性</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>ext4</strong></td>
      <td>稳定耐用、安全默认，日常系统首选</td>
      <td>传统服务器 / Web / 应用部署</td>
      <td>稳定、默认、安全</td>
    </tr>
    <tr>
      <td><strong>XFS</strong></td>
      <td>并发高效，大文件日志吞吐之选</td>
      <td>日志服务 / 大文件批处理</td>
      <td>吞吐高、并发好</td>
    </tr>
    <tr>
      <td><strong>Btrfs</strong></td>
      <td>支持快照压缩，适合容器与开发场景</td>
      <td>快照 + 压缩 + 容器/开发</td>
      <td>灵活、支持子卷</td>
    </tr>
    <tr>
      <td><strong>ZFS</strong></td>
      <td>数据安全第一，适合归档/数据库</td>
      <td>数据库 / 存档 / 高可靠性存储</td>
      <td>自修复 + 快照 + 压缩</td>
    </tr>
    <tr>
      <td><strong>tmpfs</strong></td>
      <td>内存速度爆炸，但重启就没了</td>
      <td>高速缓存 / 临时文件 / <code class="language-plaintext highlighter-rouge">/tmp</code></td>
      <td>内存中极速读写，掉电即清空</td>
    </tr>
  </tbody>
</table>

<p>详细对比</p>

<table>
  <thead>
    <tr>
      <th>特性/文件系统</th>
      <th><strong>ext4</strong></th>
      <th><strong>XFS</strong></th>
      <th><strong>Btrfs</strong></th>
      <th><strong>ZFS</strong></th>
      <th><strong>tmpfs</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>文件系统类型</td>
      <td>journaling</td>
      <td>journaling</td>
      <td>copy-on-write + journaling</td>
      <td>copy-on-write + 自修复</td>
      <td>内存文件系统</td>
    </tr>
    <tr>
      <td>最大文件/分区</td>
      <td>16TB / 1EB</td>
      <td>8EB / 8EB</td>
      <td>16EB / 16EB</td>
      <td>256ZB / 16EB</td>
      <td>内存限制（动态扩展）</td>
    </tr>
    <tr>
      <td>快照支持</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅（功能强大）</td>
      <td>❌</td>
    </tr>
    <tr>
      <td>自修复能力</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅（CRC 校验）</td>
      <td>✅（ECC、自愈）</td>
      <td>❌</td>
    </tr>
    <tr>
      <td>并发写入性能</td>
      <td>中</td>
      <td>✅ 高</td>
      <td>中（多子卷支持）</td>
      <td>中（吞吐高，延迟高）</td>
      <td>极快（纯内存）</td>
    </tr>
    <tr>
      <td>小文件读写性能</td>
      <td>✅ 强</td>
      <td>中</td>
      <td>中</td>
      <td>中</td>
      <td>✅ 强</td>
    </tr>
    <tr>
      <td>在线扩容</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>—</td>
    </tr>
    <tr>
      <td>在线缩容</td>
      <td>❌（有限支持）</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>—</td>
    </tr>
    <tr>
      <td>原生压缩</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
    </tr>
    <tr>
      <td>原生 RAID</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅（简单）</td>
      <td>✅（RAID-Z 强大）</td>
      <td>❌</td>
    </tr>
    <tr>
      <td>是否掉电丢数据</td>
      <td>❌（持久存储）</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅（重启即失）</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Shujia Xu</name></author><category term="Linux" /><category term="Linux" /><summary type="html"><![CDATA[一些常见的Linux面试问题]]></summary></entry><entry><title type="html">C++ 常见语法面试题复习手册</title><link href="https://sjxu-base.github.io/language/Cpp-training/" rel="alternate" type="text/html" title="C++ 常见语法面试题复习手册" /><published>2025-05-20T00:00:00+08:00</published><updated>2025-05-20T00:00:00+08:00</updated><id>https://sjxu-base.github.io/language/Cpp-training</id><content type="html" xml:base="https://sjxu-base.github.io/language/Cpp-training/"><![CDATA[<h1 id="0x01-基础语法与语言特性">0x01 基础语法与语言特性</h1>

<h2 id="static-变量的作用">static 变量的作用</h2>

<ul>
  <li><strong>函数内</strong>：生命周期扩展至程序结束，作用域仅限函数。</li>
  <li><strong>类内</strong>：类变量，所有实例共享。</li>
  <li><strong>文件作用域</strong>：限制符号仅在当前编译单元可见。</li>
</ul>

<h2 id="const-和-define-定义的区别">const 和 #define 定义的区别</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">const</code>：类型安全，编译期常量，有作用域。</li>
  <li><code class="language-plaintext highlighter-rouge">#define</code>：预处理阶段替换，无类型检查。</li>
</ul>

<h2 id="引用-vs-指针的区别">引用 vs 指针的区别</h2>

<ul>
  <li>引用不可为 null，不可重绑定，更安全。</li>
  <li>指针可为 null，可重新赋值。</li>
</ul>

<h2 id="namespace-命名空间的使用">namespace 命名空间的使用</h2>

<ul>
  <li>作用：防止命名冲突。</li>
  <li>推荐使用别名或在函数内局部使用 <code class="language-plaintext highlighter-rouge">using</code>。</li>
</ul>

<h2 id="cpp-与-c-语言类型转换">Cpp 与 C 语言类型转换</h2>

<ul>
  <li>C 风格：<code class="language-plaintext highlighter-rouge">(int)x</code>（不推荐）</li>
  <li>C++ 风格：<code class="language-plaintext highlighter-rouge">static_cast</code>, <code class="language-plaintext highlighter-rouge">dynamic_cast</code>, <code class="language-plaintext highlighter-rouge">const_cast</code>, <code class="language-plaintext highlighter-rouge">reinterpret_cast</code></li>
</ul>

<h2 id="auto-变量类型推导">auto 变量类型推导</h2>

<ul>
  <li>自动根据初始化推导类型。</li>
  <li>无法用作函数参数类型。</li>
</ul>

<h2 id="decltype-推导表达式类型">decltype 推导表达式类型</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">decltype(expr)</code> 不会计算 <code class="language-plaintext highlighter-rouge">expr</code>，仅提取其类型。</li>
</ul>

<h1 id="0x02-面向对象相关功能">0x02 面向对象相关功能</h1>

<h2 id="struct-和-class-的区别">struct 和 class 的区别</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">struct</code> 默认 public，<code class="language-plaintext highlighter-rouge">class</code> 默认 private。</li>
  <li>用法一致，语义上 <code class="language-plaintext highlighter-rouge">struct</code> 常用于数据结构。</li>
</ul>

<h2 id="构造函数类型">构造函数类型</h2>

<ul>
  <li>默认构造：无参或默认值。</li>
  <li>拷贝构造：值传递或返回。</li>
  <li>移动构造：转移资源（<code class="language-plaintext highlighter-rouge">T&amp;&amp;</code> + <code class="language-plaintext highlighter-rouge">std::move</code>）。</li>
</ul>

<h2 id="虚析构函数">虚析构函数</h2>

<ul>
  <li>必须为虚，确保 delete 派生类时调用完整析构流程。</li>
</ul>

<h2 id="多继承--虚继承">多继承 &amp; 虚继承</h2>

<ul>
  <li>多继承可能产生菱形继承问题。</li>
  <li>虚继承使用 <code class="language-plaintext highlighter-rouge">virtual</code> 保证共享唯一基类实例。</li>
</ul>

<h2 id="虚函数-和-vtable-的区别">虚函数 和 vtable 的区别</h2>

<ul>
  <li>虚函数：支持运行时多态</li>
  <li><code class="language-plaintext highlighter-rouge">vtable</code>：每个多态类对象通过 <code class="language-plaintext highlighter-rouge">vptr</code> 访问虚函数表</li>
</ul>

<h2 id="overload-和-override-的区别">OverLoad 和 Override 的区别</h2>

<ul>
  <li>重载（overload）：同作用域、同名、不同参数</li>
  <li>重写（override）：继承中重新定义虚函数，签名必须完全匹配</li>
</ul>

<h2 id="抽象类--纯虚函数">抽象类 &amp; 纯虚函数</h2>

<ul>
  <li>抽象类不能实例化，用作接口。</li>
  <li><code class="language-plaintext highlighter-rouge">virtual void foo() = 0;</code></li>
</ul>

<h2 id="0x03-高级语法特性">0x03 高级语法特性</h2>

<h2 id="模板">模板</h2>

<ul>
  <li>支持泛型编程。</li>
  <li>特化：为特定类型提供不同实现。</li>
</ul>

<h2 id="智能指针">智能指针</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">unique_ptr</code>：独占所有权，不能拷贝。</li>
  <li><code class="language-plaintext highlighter-rouge">shared_ptr</code>：引用计数。</li>
  <li><code class="language-plaintext highlighter-rouge">weak_ptr</code>：观察者，解决循环引用问题。</li>
</ul>

<h2 id="右值引用--移动语义">右值引用 &amp; 移动语义</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">T&amp;&amp;</code>：绑定右值对象。</li>
  <li><code class="language-plaintext highlighter-rouge">std::move(x)</code>：将变量转换为右值引用，允许移动。</li>
</ul>

<h2 id="lambda-表达式">Lambda 表达式</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="o">=</span><span class="p">]</span> <span class="err">捕获全部外部变量（值）</span>
<span class="p">[</span><span class="o">&amp;</span><span class="p">]</span> <span class="err">捕获全部外部变量（引用）</span>
<span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">y</span><span class="p">]</span> <span class="err">捕获特定变量</span>
<span class="p">[</span><span class="k">this</span><span class="p">]</span> <span class="err">捕获当前对象</span>
</code></pre></div></div>

<h2 id="stl-容器">STL 容器</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">vector</code>：动态数组，随机访问快</li>
  <li><code class="language-plaintext highlighter-rouge">list</code>：双向链表，插入删除快，访问慢</li>
  <li><code class="language-plaintext highlighter-rouge">map</code>：红黑树</li>
  <li><code class="language-plaintext highlighter-rouge">unordered_map</code>：哈希表</li>
</ul>

<h2 id="异常处理">异常处理</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">try</span> <span class="p">{</span> 
  <span class="k">throw</span> <span class="p">...;</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(...)</span> <span class="p">{</span> 
  <span class="p">...</span> 
<span class="p">}</span>
<span class="c1">//noexcept: 声明函数不抛异常，可优化性能。</span>
</code></pre></div></div>

<h2 id="多线程与同步">多线程与同步</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="kr">thread</span> <span class="nf">t</span><span class="p">(</span><span class="n">func</span><span class="p">);</span>
<span class="n">t</span><span class="p">.</span><span class="n">join</span><span class="p">();</span>
<span class="n">std</span><span class="o">::</span><span class="n">mutex</span> <span class="n">mtx</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">lock_guard</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">mutex</span><span class="o">&gt;</span> <span class="n">lock</span><span class="p">(</span><span class="n">mtx</span><span class="p">);</span>
</code></pre></div></div>]]></content><author><name>Shujia Xu</name></author><category term="Language" /><category term="Cpp" /><summary type="html"><![CDATA[一些常见的 C++ 面试问题]]></summary></entry></feed>