<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN"><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://lightlisten.cn/feed.xml" rel="self" type="application/atom+xml" /><link href="https://lightlisten.cn/" rel="alternate" type="text/html" hreflang="zh-CN" /><updated>2026-09-09T21:08:28+08:00</updated><id>https://lightlisten.cn/feed.xml</id><title type="html">pavilion’s blog</title><subtitle>pavilion try rdma, os, hpc and so on.</subtitle><author><name>pavilion</name></author><entry><title type="html">gemm优化</title><link href="https://lightlisten.cn/cuda/2026/07/06/gemm%E4%BC%98%E5%8C%96.html" rel="alternate" type="text/html" title="gemm优化" /><published>2026-07-06T00:00:00+08:00</published><updated>2026-07-06T00:00:00+08:00</updated><id>https://lightlisten.cn/cuda/2026/07/06/gemm%E4%BC%98%E5%8C%96</id><content type="html" xml:base="https://lightlisten.cn/cuda/2026/07/06/gemm%E4%BC%98%E5%8C%96.html"><![CDATA[<h1 id="gemm优化">gemm优化</h1>

<h2 id="最终测试结果固定频率时性能比cublas更好">最终测试结果：固定频率时性能比cuBlas更好</h2>
<p>测试尺寸： 20480 * 20480 * 20480</p>

<blockquote>
  <p>测试环境
cpu: i7-13700K</p>

  <p>gpu: RTX4070ts</p>

  <p>cuda： 13.1</p>

  <p>vs: Microsoft Visual Studio Community 2022</p>
</blockquote>

<p>最终结果</p>
<blockquote>
  <p><strong>固定频率2100MHz时我们的实现比cuBlas更好</strong>(nvidia-smi -lgc 2100,2100)：</p>

  <p>MyKernel GPUexecution time:6783.493164ms，TFLOPS:<strong>25.326</strong></p>

  <p>cuBLAS GPU execution time:6831.067383 ms，TFLOPS:<strong>25.150</strong></p>
</blockquote>

<hr />
<blockquote>
  <p>不固定频率(通过nvidia-smi dmon 观察频率会掉到2100MHz)：</p>

  <p>MyKernel GPU execution time: 6571.359375 ms， TFLOPS: <strong>26.144</strong></p>

  <p>cuBLAS GPU execution time: 5911.912109 ms， TFLOPS: <strong>29.060</strong></p>
</blockquote>

<h2 id="性能分析">性能分析</h2>
<h3 id="理论计算--计算强度和机器平衡点">理论计算：  计算强度和机器平衡点</h3>
<p>AI = 内存量 / FMA 计算量
比如我的kernel中为64x64的共享内存时，从内存读到共享内存的数据量为： As + Bs = 2 × 64² × 4B = 32 KB（每线程 8 条 float4 = 128 B）
计算量：64³ = 262,144 次 FMA = 524,288 FLOP（每线程 4×4×64 = 1024 FMA）</p>

<p>那么 AI = 524288 FLOP ÷ 32768 B = 16 FLOP/Byte</p>

<p>而我的硬件为4070ts， 机器平衡点 （峰值算力 ÷ 显存带宽） = 44.10 TFLOPS ÷ 672.3 GB/s = 65.4</p>

<p>只有 AI &gt; 平衡点 时计算才是瓶颈，  所以理论上我这里瓶颈应该是带宽</p>

<h3 id="实际测试ncu">实际测试：ncu</h3>

<blockquote>
  <p>ncu -k myGemm –launch-skip 8 –launch-count 1 –set full .\gemm.exe</p>

  <p>或 只用关键字：
ncu -k myGemm –metrics sm__throughput.avg.pct_of_peak_sustained_elapsed,gpu__compute_memory_throughput.avg.pct_of_peak_sustained_elapsed,smsp__warp_issue_stalled_long_scoreboard_per_warp_active.pct,smsp__warp_issue_stalled_barrier_per_warp_active.pct,dram__bytes.sum .\gemm.exe</p>

</blockquote>

<p>得到结果</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  myGemm(float *, float *, float *, float, float, int, int, int) (320, 320, 1)x(16, 16, 1), Context 1, Stream 7, Device 0, CC 8.9
    Section: GPU Speed Of Light Throughput
    ----------------------- ----------- ----------------
    Metric Name             Metric Unit     Metric Value
    ----------------------- ----------- ----------------
    DRAM Frequency                  Ghz            10.19
    SM Frequency                    Ghz             2.17
    Elapsed Cycles                cycle    2,271,298,602
    Memory Throughput                 %            81.41
    DRAM Throughput                   %            81.41
    Duration                          s             1.05
    L1/TEX Cache Throughput           %            79.48
    L2 Cache Throughput               %            35.22
    SM Active Cycles              cycle 2,268,501,054.44
    Compute (SM) Throughput           %            52.35
    ----------------------- ----------- ----------------
</code></pre></div></div>

<p>可以看到确实加载内存的利用率Memory Throughput很大，计算利用率Compute (SM) Throughput不够
就是说主要还是卡内存，需要优化内存访问，符合理论分析。</p>

<h2 id="优化内容">优化内容</h2>

<h3 id="分片缓存1显存到共享内存">分片缓存1：显存到共享内存</h3>
<p>最简单的乘法实现无疑是每个线程计算一个结果数组的元素。但是这样的话采取上面的理论分析，其实可以看到，单次计算的计算量无法得到足够的内存带宽。我们需要让这个比例更协调。</p>

<p>最重要的思想就是分片。对于矩阵A:m x k  和 B：k x n来说，</p>
<blockquote>
  <p>A的每一行都会用来和B的所有n列都计算一次乘积。如果每次都重新读取A的那一行，无疑会导致n-1次多余的读取，浪费大量内存带宽</p>
</blockquote>

<p>因此我们可以通过缓存这个A行供给B的列来减少这种情况. 同理也要缓存B的列来供给A的行。 而显然我们无法把完整的矩阵存入高速存储空间。 因此只能通过切块方法达到优化效果。</p>

<p>我们的数据最开始放在显存，因此首先往上一级缓存就是到共享内存（不讨论L1等无法控制的缓存） 。</p>

<p>我们设定BM, BN, BK对应缓存矩阵A，B的大小为 BM x BK,   BK x BN。 一般设kernel为比较大的16 x 16. 为了方便表示，最初让取数据操作每个线程取1个数据存入，因此可以让BM=BN=BK=16. 每次取完，  然后每个线程使用缓存矩阵计算临时结果。 滚动k对应维度累计求和即可。</p>

<p>此时即可达到cuBlas的60%效果了</p>

<h3 id="分片缓存2共享内存到寄存器">分片缓存2：共享内存到寄存器</h3>
<p>此时继续分析仍然是内存受限， 而共享内存之上还有一级寄存器可用。因此我们可以继续做缓存， 用寄存器缓存计算结果C. 让每个线程的计算可以计算对应C的一个TM x TN的矩阵块。 而根据上述的理论计算时， 我的BM=BN=BK=64, 而TM = TN = 4。 理论计算显示需要让计算乘以4倍才比较均衡， 因此，我们需要调整BM=BN=128，而此时共享内存就不足了，因此必须让BK=8， TM=TN=8 从而减轻负载，通过多次迭代移动来遍历K来达到类似效果。</p>

<h3 id="向量化内存读写">向量化内存读写</h3>
<p>此时会有一个优化点是，我们按float操作的话，会形成多次for带来的多次指令。  我们可以通过float4变量来一次性实现类似效果。 而这样的话， 因为每轮K迭代都是BM * BK = 1024 = 4 * 16 * 16 = 4 x block线程数。 因此刚好每个线程取一个float4即可。非常完美。</p>

<p>同时，从共享内存读取到寄存器也可以类似操作，减少读共享内存次数75%.</p>

<p>此番修改后即可得到80%的cuBlas性能。</p>

<h3 id="杂项预取缓存1-swizzle-增强l2缓存命中">杂项：预取缓存1， Swizzle 增强L2缓存命中</h3>
<p>此时读显存、计算两个步骤为完全串行， 可以优化成两个步骤， 读下一批次的float4， 计算当前批次。 从而形成流水线。</p>

<p>此时继续观察每个block（也即sm分片对应的计算结果区域）的计算结果区域是按顺序依次排列的，也即从0-x按列优先，然后逐行遍历。这样有个问题是B数组的读取每次都是从DRAM读取，无法缓存。通过重新映射方法来再在显存和共享内存之间做一层缓存（L2）。 block顺序从依次，改成按group分组，满group则先跳到下一“行”，而不是继续这一列。比如按照BM, BN, BK分块后，原本结果数组是每行分别有16个块的。那么我们按group = 8设置， 第9个块不再处理第一行的第9个块，而是处理第2行的第一个块，此时计算这个块就会用到处理第一行第一列block时的B的列数据（在L2）。从而减少DRAM读次数，达到加速效果。</p>

<p>此时可以得到 90% 的cuBlas性能。</p>

<h3 id="关于gpu频率影响">关于GPU频率影响</h3>

<p>此时到了最奇妙的地方， 因为我们无论如何都差10%无法达到cuBlas的效果，而通过nvidia-smi dmon观察频率可以发现， 我们的计算过程中<strong>降频</strong>非常厉害，而cublas稍微少一些，这是我最不能理解的地方。 如果我们锁定频率，此时，就会惊奇的发现，我们的<strong>性能已经超过cuBlas</strong>了~。 推测是我们的实际优化策略和cublas有区别， 我们的方法里更容易触及功耗墙造成大幅降频。 如果后续继续优化的话， 我觉得这个是个很好的研究和探索的方向~</p>]]></content><author><name>pavilion</name></author><category term="cuda" /><category term="cuda" /><category term="gemm" /><summary type="html"><![CDATA[gemm优化 最终测试结果：固定频率时性能比cuBlas更好 测试尺寸： 20480 * 20480 * 20480 测试环境 cpu: i7-13700K gpu: RTX4070ts cuda： 13.1 vs: Microsoft Visual Studio Community 2022 最终结果 固定频率2100MHz时我们的实现比cuBlas更好(nvidia-smi -lgc 2100,2100)： MyKernel GPUexecution time:6783.493164ms，TFLOPS:25.326 cuBLAS GPU execution time:6831.067383 ms，TFLOPS:25.150 不固定频率(通过nvidia-smi dmon 观察频率会掉到2100MHz)： MyKernel GPU execution time: 6571.359375 ms， TFLOPS: 26.144 cuBLAS GPU execution time: 5911.912109 ms， TFLOPS: 29.060 性能分析 理论计算： 计算强度和机器平衡点 AI = 内存量 / FMA 计算量 比如我的kernel中为64x64的共享内存时，从内存读到共享内存的数据量为： As + Bs = 2 × 64² × 4B = 32 KB（每线程 8 条 float4 = 128 B） 计算量：64³ = 262,144 次 FMA = 524,288 FLOP（每线程 4×4×64 = 1024 FMA） 那么 AI = 524288 FLOP ÷ 32768 B = 16 FLOP/Byte 而我的硬件为4070ts， 机器平衡点 （峰值算力 ÷ 显存带宽） = 44.10 TFLOPS ÷ 672.3 GB/s = 65.4 只有 AI &gt; 平衡点 时计算才是瓶颈， 所以理论上我这里瓶颈应该是带宽 实际测试：ncu ncu -k myGemm –launch-skip 8 –launch-count 1 –set full .\gemm.exe 或 只用关键字： ncu -k myGemm –metrics sm__throughput.avg.pct_of_peak_sustained_elapsed,gpu__compute_memory_throughput.avg.pct_of_peak_sustained_elapsed,smsp__warp_issue_stalled_long_scoreboard_per_warp_active.pct,smsp__warp_issue_stalled_barrier_per_warp_active.pct,dram__bytes.sum .\gemm.exe 得到结果 myGemm(float *, float *, float *, float, float, int, int, int) (320, 320, 1)x(16, 16, 1), Context 1, Stream 7, Device 0, CC 8.9 Section: GPU Speed Of Light Throughput ----------------------- ----------- ---------------- Metric Name Metric Unit Metric Value ----------------------- ----------- ---------------- DRAM Frequency Ghz 10.19 SM Frequency Ghz 2.17 Elapsed Cycles cycle 2,271,298,602 Memory Throughput % 81.41 DRAM Throughput % 81.41 Duration s 1.05 L1/TEX Cache Throughput % 79.48 L2 Cache Throughput % 35.22 SM Active Cycles cycle 2,268,501,054.44 Compute (SM) Throughput % 52.35 ----------------------- ----------- ---------------- 可以看到确实加载内存的利用率Memory Throughput很大，计算利用率Compute (SM) Throughput不够 就是说主要还是卡内存，需要优化内存访问，符合理论分析。 优化内容 分片缓存1：显存到共享内存 最简单的乘法实现无疑是每个线程计算一个结果数组的元素。但是这样的话采取上面的理论分析，其实可以看到，单次计算的计算量无法得到足够的内存带宽。我们需要让这个比例更协调。 最重要的思想就是分片。对于矩阵A:m x k 和 B：k x n来说， A的每一行都会用来和B的所有n列都计算一次乘积。如果每次都重新读取A的那一行，无疑会导致n-1次多余的读取，浪费大量内存带宽 因此我们可以通过缓存这个A行供给B的列来减少这种情况. 同理也要缓存B的列来供给A的行。 而显然我们无法把完整的矩阵存入高速存储空间。 因此只能通过切块方法达到优化效果。 我们的数据最开始放在显存，因此首先往上一级缓存就是到共享内存（不讨论L1等无法控制的缓存） 。 我们设定BM, BN, BK对应缓存矩阵A，B的大小为 BM x BK, BK x BN。 一般设kernel为比较大的16 x 16. 为了方便表示，最初让取数据操作每个线程取1个数据存入，因此可以让BM=BN=BK=16. 每次取完， 然后每个线程使用缓存矩阵计算临时结果。 滚动k对应维度累计求和即可。 此时即可达到cuBlas的60%效果了 分片缓存2：共享内存到寄存器 此时继续分析仍然是内存受限， 而共享内存之上还有一级寄存器可用。因此我们可以继续做缓存， 用寄存器缓存计算结果C. 让每个线程的计算可以计算对应C的一个TM x TN的矩阵块。 而根据上述的理论计算时， 我的BM=BN=BK=64, 而TM = TN = 4。 理论计算显示需要让计算乘以4倍才比较均衡， 因此，我们需要调整BM=BN=128，而此时共享内存就不足了，因此必须让BK=8， TM=TN=8 从而减轻负载，通过多次迭代移动来遍历K来达到类似效果。 向量化内存读写 此时会有一个优化点是，我们按float操作的话，会形成多次for带来的多次指令。 我们可以通过float4变量来一次性实现类似效果。 而这样的话， 因为每轮K迭代都是BM * BK = 1024 = 4 * 16 * 16 = 4 x block线程数。 因此刚好每个线程取一个float4即可。非常完美。 同时，从共享内存读取到寄存器也可以类似操作，减少读共享内存次数75%. 此番修改后即可得到80%的cuBlas性能。 杂项：预取缓存1， Swizzle 增强L2缓存命中 此时读显存、计算两个步骤为完全串行， 可以优化成两个步骤， 读下一批次的float4， 计算当前批次。 从而形成流水线。 此时继续观察每个block（也即sm分片对应的计算结果区域）的计算结果区域是按顺序依次排列的，也即从0-x按列优先，然后逐行遍历。这样有个问题是B数组的读取每次都是从DRAM读取，无法缓存。通过重新映射方法来再在显存和共享内存之间做一层缓存（L2）。 block顺序从依次，改成按group分组，满group则先跳到下一“行”，而不是继续这一列。比如按照BM, BN, BK分块后，原本结果数组是每行分别有16个块的。那么我们按group = 8设置， 第9个块不再处理第一行的第9个块，而是处理第2行的第一个块，此时计算这个块就会用到处理第一行第一列block时的B的列数据（在L2）。从而减少DRAM读次数，达到加速效果。 此时可以得到 90% 的cuBlas性能。 关于GPU频率影响 此时到了最奇妙的地方， 因为我们无论如何都差10%无法达到cuBlas的效果，而通过nvidia-smi dmon观察频率可以发现， 我们的计算过程中降频非常厉害，而cublas稍微少一些，这是我最不能理解的地方。 如果我们锁定频率，此时，就会惊奇的发现，我们的性能已经超过cuBlas了~。 推测是我们的实际优化策略和cublas有区别， 我们的方法里更容易触及功耗墙造成大幅降频。 如果后续继续优化的话， 我觉得这个是个很好的研究和探索的方向~]]></summary></entry><entry><title type="html">外挂攻防</title><link href="https://lightlisten.cn/cheat/2026/06/01/%E5%A4%96%E6%8C%82%E6%94%BB%E9%98%B2.html" rel="alternate" type="text/html" title="外挂攻防" /><published>2026-06-01T00:00:00+08:00</published><updated>2026-06-01T00:00:00+08:00</updated><id>https://lightlisten.cn/cheat/2026/06/01/%E5%A4%96%E6%8C%82%E6%94%BB%E9%98%B2</id><content type="html" xml:base="https://lightlisten.cn/cheat/2026/06/01/%E5%A4%96%E6%8C%82%E6%94%BB%E9%98%B2.html"><![CDATA[<h1 id="外挂攻防">外挂攻防</h1>

<h2 id="很久以前的一个梦">很久以前的一个梦</h2>

<p>小时候，玩qq农场的时候就在想，啊啊啊，我的菜呢，为啥别人家的菜都这么及时收</p>

<p>然后就想要是我有个外挂定时收菜就好了~</p>

<p>然后就入行了程序员， 辗转若干年， 发现虽然成为了程序员，但是还是没有了解过外挂，这趁着有空，学习了一把~</p>

<p>声明： <strong>本过程没有任何真人受到伤害！</strong></p>

<h2 id="外挂的类型">外挂的类型</h2>

<p>学习一把之后发现了外挂的攻防游戏已经进展到了你死我活， 玩家要么放弃一切隐私否则只能成为陪玩的地步~</p>

<h3 id="直接干">直接干</h3>
<p>最简单的外挂当然是 直接读写游戏进程的内存。 windows居然能提供api OpenProcess直接获取读写其他进程内存的权限，这让我大为震惊。那么如果不做防范的话，当然直接读写就完事了，没有例外。如果是普通的单机游戏、 小公司的游戏的话，这样也足够了。当然，大名鼎鼎的csgo甚至也能这样直接拿到权限，这让我难以想象。</p>

<p>而如果采取dll注入方法插入游戏进程，也是这一类里，但是他们只要检查自己的进程代码内存就可以逮捕了，并无差异。</p>

<p>只不过这又有个问题，他们可以通过微软的api ObRegisterCallbacks 审核调用权限的请求，将坏蛋逮捕封号，这增大了人们开挂的难度。</p>

<h3 id="伪装成系统服务">伪装成系统服务</h3>

<p>这时候， 有坏蛋就想了， 如何能够绕过检测呢？ 有的小聪明就发现了， 有些进程本身就需要获取游戏进程内存的权限，如discord.exe， obs等。 那么我伪装成他们不就行了？ 于是就有了dll注入和句柄劫持。 我把我的外挂注入到这种白名单进程里，你无法不给我放行，那你又能奈我何？？ 毕竟你如果不放行，那游戏本身可能就无法正常运行，如discord。 而你又不能不让用户直播（obs）。</p>

<p>这时候想要打击就必须“看”其他进程，其他设备的内存，甚至严格一点的更会<strong>扫盘</strong>了（不过我仍然无法理解扫盘这一行为，因为实际上可以从远端读取二进制，没必要本地存，那扫了也没用，只能说是提高了门槛）。 而这明显涉及<strong>侵犯隐私</strong>， csgo这边相传并不打算如此，“玩个游戏就要破坏隐私”，这导致了他们的的反作弊系统vac让人诟病。</p>

<h3 id="伪装成内核驱动">伪装成内核驱动</h3>

<p>这时候用户做出了巨大的牺牲后， 外挂已经难以简单存在了，但是大坏蛋又又又想到了好主意，那就是做成内核驱动， 不走OpenProcess api调用，那你自然就无法检测。但是此时成本已经很高了， windows下驱动加载需要有正确的泄露状态的驱动签名，或者漏洞。而前者只要维护泄露库即可识别， 漏洞会被修复。 而更狠的还会通过ept让游戏的内存连内核态也不可读写，让外挂更加艰难。</p>

<h3 id="硬件接管">硬件接管</h3>
<p>但是更艰难的是直接通过dma直接旁路内存访问。 简单的就模拟一个网卡、声卡。通过dma获取内存，直接控制鼠标，显示人物位置等等。 但是iommu可以防止直接dma。</p>

<p>更复杂的就是像ict的包老师做的hmtt， 让内存接到特制板子上、板子再插入主板。这个板子把访存通过光纤传递到其他机器上处理后。 这种则难以处理。当然，作为深入研究过的人士， 我给出一种方法是，直接检测内存频率。因为她为了做逻辑处理，不得不降低内存频率。如果发现频率较低，直接认为他有嫌疑是有可行性的。</p>

<h3 id="ai-识别">AI 识别</h3>
<p>而终极方法就是不改动游戏机器，通过摄像头获取游戏界面，识别敌人后控制鼠标移动开枪~。 这更是无敌，只能通过检测鼠标异常移动来封禁了。 但是这个前提下，坏蛋的行为将被大幅度收敛，毕竟你必须表现得像个正常人没法开转，那么就存在交互，<strong>诛仙</strong>的可能。 而游戏的攻防正式进入ai时代， 外挂主要是需要模拟人的鼠标移动。 而游戏商则检测鼠标移动是否属于人类范畴。 而这注定无法彻底平息。成为永远的战争。</p>

<h2 id="我的尝试">我的尝试</h2>
<p>学习完之后，我权衡之后选择简单又不那么简单的第二种， 伪装成系统服务。 效果挺好，直接得到了所有角色的位置、血量等信息。</p>

<p>其实开始时遇到最大的问题都有解， 比如烦人的内存位置寻找。毕竟如果要自行通过cheat engine扫描内存，识别变化来定位，那得到猴年马月，消耗大量时间。 而某大型射击游戏比较特别，因为比较大型，他的内存位置直接被开源。让人研究基础外挂构造有了充足的舞台。</p>

<p>而其余内容根据想要做的架构按部就班即可完成，而在ai辅助下，这更让人倍感轻松。</p>

<p>最终成功完成了一个雷达挂件，完成了童年的小心愿~</p>

<p>当然再次重申， <strong>本文研究过程中只有机器人收到了伤害</strong></p>

<h2 id="一点思考">一点思考</h2>
<p>学习完之后，我内心久久不能平静， 因为这种扫盘扫内存的操作，让我们本地隐私无法保留， 以后可能再也没法去玩平台了，转向纯官方。 
个人感觉只能是通过加大开挂风险来平衡了，如平台都是按人封。 而官方则通过通行证加码账号价值，毕竟100+的账号，如果内部有道具则更贵，一般就不会开。</p>]]></content><author><name>pavilion</name></author><category term="cheat" /><category term="windows" /><category term="cs2" /><category term="cheat" /><summary type="html"><![CDATA[外挂攻防 很久以前的一个梦 小时候，玩qq农场的时候就在想，啊啊啊，我的菜呢，为啥别人家的菜都这么及时收 然后就想要是我有个外挂定时收菜就好了~ 然后就入行了程序员， 辗转若干年， 发现虽然成为了程序员，但是还是没有了解过外挂，这趁着有空，学习了一把~ 声明： 本过程没有任何真人受到伤害！ 外挂的类型 学习一把之后发现了外挂的攻防游戏已经进展到了你死我活， 玩家要么放弃一切隐私否则只能成为陪玩的地步~ 直接干 最简单的外挂当然是 直接读写游戏进程的内存。 windows居然能提供api OpenProcess直接获取读写其他进程内存的权限，这让我大为震惊。那么如果不做防范的话，当然直接读写就完事了，没有例外。如果是普通的单机游戏、 小公司的游戏的话，这样也足够了。当然，大名鼎鼎的csgo甚至也能这样直接拿到权限，这让我难以想象。 而如果采取dll注入方法插入游戏进程，也是这一类里，但是他们只要检查自己的进程代码内存就可以逮捕了，并无差异。 只不过这又有个问题，他们可以通过微软的api ObRegisterCallbacks 审核调用权限的请求，将坏蛋逮捕封号，这增大了人们开挂的难度。 伪装成系统服务 这时候， 有坏蛋就想了， 如何能够绕过检测呢？ 有的小聪明就发现了， 有些进程本身就需要获取游戏进程内存的权限，如discord.exe， obs等。 那么我伪装成他们不就行了？ 于是就有了dll注入和句柄劫持。 我把我的外挂注入到这种白名单进程里，你无法不给我放行，那你又能奈我何？？ 毕竟你如果不放行，那游戏本身可能就无法正常运行，如discord。 而你又不能不让用户直播（obs）。 这时候想要打击就必须“看”其他进程，其他设备的内存，甚至严格一点的更会扫盘了（不过我仍然无法理解扫盘这一行为，因为实际上可以从远端读取二进制，没必要本地存，那扫了也没用，只能说是提高了门槛）。 而这明显涉及侵犯隐私， csgo这边相传并不打算如此，“玩个游戏就要破坏隐私”，这导致了他们的的反作弊系统vac让人诟病。 伪装成内核驱动 这时候用户做出了巨大的牺牲后， 外挂已经难以简单存在了，但是大坏蛋又又又想到了好主意，那就是做成内核驱动， 不走OpenProcess api调用，那你自然就无法检测。但是此时成本已经很高了， windows下驱动加载需要有正确的泄露状态的驱动签名，或者漏洞。而前者只要维护泄露库即可识别， 漏洞会被修复。 而更狠的还会通过ept让游戏的内存连内核态也不可读写，让外挂更加艰难。 硬件接管 但是更艰难的是直接通过dma直接旁路内存访问。 简单的就模拟一个网卡、声卡。通过dma获取内存，直接控制鼠标，显示人物位置等等。 但是iommu可以防止直接dma。 更复杂的就是像ict的包老师做的hmtt， 让内存接到特制板子上、板子再插入主板。这个板子把访存通过光纤传递到其他机器上处理后。 这种则难以处理。当然，作为深入研究过的人士， 我给出一种方法是，直接检测内存频率。因为她为了做逻辑处理，不得不降低内存频率。如果发现频率较低，直接认为他有嫌疑是有可行性的。 AI 识别 而终极方法就是不改动游戏机器，通过摄像头获取游戏界面，识别敌人后控制鼠标移动开枪~。 这更是无敌，只能通过检测鼠标异常移动来封禁了。 但是这个前提下，坏蛋的行为将被大幅度收敛，毕竟你必须表现得像个正常人没法开转，那么就存在交互，诛仙的可能。 而游戏的攻防正式进入ai时代， 外挂主要是需要模拟人的鼠标移动。 而游戏商则检测鼠标移动是否属于人类范畴。 而这注定无法彻底平息。成为永远的战争。 我的尝试 学习完之后，我权衡之后选择简单又不那么简单的第二种， 伪装成系统服务。 效果挺好，直接得到了所有角色的位置、血量等信息。 其实开始时遇到最大的问题都有解， 比如烦人的内存位置寻找。毕竟如果要自行通过cheat engine扫描内存，识别变化来定位，那得到猴年马月，消耗大量时间。 而某大型射击游戏比较特别，因为比较大型，他的内存位置直接被开源。让人研究基础外挂构造有了充足的舞台。 而其余内容根据想要做的架构按部就班即可完成，而在ai辅助下，这更让人倍感轻松。 最终成功完成了一个雷达挂件，完成了童年的小心愿~ 当然再次重申， 本文研究过程中只有机器人收到了伤害 一点思考 学习完之后，我内心久久不能平静， 因为这种扫盘扫内存的操作，让我们本地隐私无法保留， 以后可能再也没法去玩平台了，转向纯官方。 个人感觉只能是通过加大开挂风险来平衡了，如平台都是按人封。 而官方则通过通行证加码账号价值，毕竟100+的账号，如果内部有道具则更贵，一般就不会开。]]></summary></entry><entry><title type="html">自定义系统调用</title><link href="https://lightlisten.cn/linux/2022/03/25/%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8.html" rel="alternate" type="text/html" title="自定义系统调用" /><published>2022-03-25T00:00:00+08:00</published><updated>2022-03-25T00:00:00+08:00</updated><id>https://lightlisten.cn/linux/2022/03/25/%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8</id><content type="html" xml:base="https://lightlisten.cn/linux/2022/03/25/%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8.html"><![CDATA[<h1 id="自定义系统调用">自定义系统调用</h1>

<h3 id="添加系统调用号">添加系统调用号</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//arch\x86\entry\syscalls\syscall_64.tbl</span>
<span class="c1">//添加一行</span>
<span class="mi">335</span> <span class="n">common</span>  <span class="n">lt_syscall</span>   <span class="n">sys_lt_syscall</span>
</code></pre></div></div>

<h3 id="定义系统调用函数">定义系统调用函数</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//找个文件，或自己新建一个文件添加定义</span>
<span class="n">SYSCALL_DEFINE1</span><span class="p">(</span><span class="n">lt_syscall</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span><span class="p">,</span> <span class="n">len</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">return</span> <span class="n">len</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>


</code></pre></div></div>

<h2 id="测试">测试</h2>

<p>编写用户程序如下调用即可</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 调用号，参数</span>
<span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">syscall</span><span class="p">(</span><span class="mi">335</span><span class="p">,</span>  <span class="mi">114514</span><span class="p">);</span>
</code></pre></div></div>

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

<p><a href="[Linux添加系统调用_王清欢Randy的博客-CSDN博客_linux 系统调用](https://blog.csdn.net/qq_41773806/article/details/90048720?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0.pc_relevant_aa&amp;spm=1001.2101.3001.4242.1&amp;utm_relevant_index=3)">Linux添加系统调用</a></p>]]></content><author><name>pavilion</name></author><category term="linux" /><category term="linux" /><category term="ubuntu" /><category term="syscall" /><summary type="html"><![CDATA[自定义系统调用 添加系统调用号 //arch\x86\entry\syscalls\syscall_64.tbl //添加一行 335 common lt_syscall sys_lt_syscall 定义系统调用函数 //找个文件，或自己新建一个文件添加定义 SYSCALL_DEFINE1(lt_syscall, unsigned int, len) { return len+1; } 测试 编写用户程序如下调用即可 // 调用号，参数 int ret = syscall(335, 114514); 参考资料 Linux添加系统调用]]></summary></entry><entry><title type="html">CARASH工具使用</title><link href="https://lightlisten.cn/rdma/2022/01/11/CRASH%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8.html" rel="alternate" type="text/html" title="CARASH工具使用" /><published>2022-01-11T00:00:00+08:00</published><updated>2022-01-11T00:00:00+08:00</updated><id>https://lightlisten.cn/rdma/2022/01/11/CRASH%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8</id><content type="html" xml:base="https://lightlisten.cn/rdma/2022/01/11/CRASH%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8.html"><![CDATA[<h1 id="carash工具使用">CARASH工具使用</h1>

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

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt install linux-crashdump 

vim /etc/default/grub.d/kexec-tools.config
# change reserved memory from 128M to 256M
# 测试起来只有512能work， 多了少了都是奇怪的报错。本质上应该没有什么意义


#重新编译内核，主要是减小内核和驱动部分的大小
make -j10
objcopy --strip-debug ./vmlinux.o
make INSTALL_MOD_STRIP=1 modules_install
make install

# in one
make -j10 &amp;&amp; objcopy --strip-debug ./vmlinux.o &amp;&amp; make INSTALL_MOD_STRIP=1 modules_install &amp;&amp; make install

#查看状态
kdump-config show

# 重启后进行功能测试
# 手动触发内核崩溃
echo c &gt; /proc/sysrq-trigger


# 设置死锁自动panic以便搜集信息，实际上是最后一个，其他事开启检测，一般都是自动开的
echo 1 &gt; /proc/sys/kernel/watchdog
echo 1 &gt; /proc/sys/kernel/soft_watchdog
echo 1 &gt; /proc/sys/kernel/softlockup_panic


# 然而实际上有时候会一直卡在重启界面，没有提示，不过实际上能够备份。只是看机器要等一段时间。。。我一般是为了确保能work，等10分钟。
</code></pre></div></div>

<p>仍然有bug</p>

<p>看起来是crash后msr寄存器状态错误。  corrupted hw-pmu resource msr 38d is b0</p>

<p>极度迷惑性的信息。实际上毫不相干，维护内核那帮人都直呼懒得管。</p>

<p>不管，再加上取消debug信息</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># make -j10</span>
objcopy <span class="nt">--strip-debug</span> ./vmlinux.o
</code></pre></div></div>

<p>看起来就能work了，但是有因为我们是在linux内核上，使用fastswap的内核原本必须要装载那个内核模块才能用swap。</p>

<p>但是这里crash过程中也要用，直接爆炸。</p>

<p>把同步接口去掉即可。</p>

<table>
  <thead>
    <tr>
      <th>内存容量</th>
      <th>预留内存容量</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0GB～12GB</td>
      <td>64MB</td>
    </tr>
    <tr>
      <td>12GB～48GB</td>
      <td>128MB</td>
    </tr>
    <tr>
      <td>48GB～128GB</td>
      <td>512MB</td>
    </tr>
    <tr>
      <td>128GB～256GB</td>
      <td>512MB/768MB/896MB</td>
    </tr>
  </tbody>
</table>

<p>看见保存好在reboot了可以直接硬重启，因为我这里等了一个小时都没重启起来，直接冲了是可以的</p>

<p>复活后</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 第一个参数为内核符号。可以在编译内核的地方找到，我这里就是用的这个。</span>
crash /home/lt/lightswap/linux-4.11-hmtt/vmlinux   /var/crash/202201121743/dump.202201121743
</code></pre></div></div>

<p>可以进入crash控制台</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>WARNING: kernel relocated [418MB]: patching 92251 gdb minimal_symbol values

      KERNEL: /home/lt/lightswap/linux-4.11-hmtt/vmlinux               
    DUMPFILE: /var/crash/202201121743/dump.202201121743  [PARTIAL DUMP]
        CPUS: 40
        DATE: Wed Jan 12 17:37:13 2022
      UPTIME: 00:03:57
LOAD AVERAGE: 9.47, 2.81, 0.97
       TASKS: 848
    NODENAME: 54
     RELEASE: 4.11.0-hmttv1+
     VERSION: #196 SMP Wed Jan 12 16:42:02 CST 2022
     MACHINE: x86_64  (2200 Mhz)
      MEMORY: 127.7 GB
       PANIC: "sysrq: SysRq : Trigger a crash"
         PID: 4965
     COMMAND: "bash"
        TASK: ffff98c68603bb00  [THREAD_INFO: ffff98c68603bb00]
         CPU: 1
       STATE: TASK_RUNNING (SYSRQ)

crash&gt; bt
PID: 4965   TASK: ffff98c68603bb00  CPU: 1   COMMAND: "bash"
 #0 [ffffb0778d947a60] machine_kexec at ffffffff9b25deda
 #1 [ffffb0778d947ac0] __crash_kexec at ffffffff9b31d71d
 #2 [ffffb0778d947b88] __crash_kexec at ffffffff9b31d7f5
 #3 [ffffb0778d947ba0] crash_kexec at ffffffff9b31d83b
 #4 [ffffb0778d947bc0] oops_end at ffffffff9b23089b
 #5 [ffffb0778d947be8] no_context at ffffffff9b26d5ee
 #6 [ffffb0778d947c48] __bad_area_nosemaphore at ffffffff9b26d8b1
 #7 [ffffb0778d947c88] bad_area_nosemaphore at ffffffff9b26d9f4
 #8 [ffffb0778d947c98] __do_page_fault at ffffffff9b26dd9e
 #9 [ffffb0778d947d00] do_page_fault at ffffffff9b26e1d2
#10 [ffffb0778d947d20] page_fault at ffffffff9baea268
    [exception RIP: sysrq_handle_crash+22]
    RIP: ffffffff9b77c626  RSP: ffffb0778d947dd0  RFLAGS: 00010282
    RAX: 000000000000000f  RBX: 0000000000000063  RCX: 0000000000000000
    RDX: 0000000000000000  RSI: ffff98c73ea4dc28  RDI: 0000000000000063
    RBP: ffffb0778d947dd0   R8: 00000000000832b8   R9: 000000000000060c
    R10: 0000000000000001  R11: ffffffff9c46276d  R12: 0000000000000004
    R13: 0000000000000000  R14: ffffffff9c116040  R15: 0000000000000000
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
#11 [ffffb0778d947dd8] __handle_sysrq at ffffffff9b77ce0a
#12 [ffffb0778d947e08] write_sysrq_trigger at ffffffff9b77d28f
#13 [ffffb0778d947e20] proc_reg_write at ffffffff9b4d3962
#14 [ffffb0778d947e40] __vfs_write at ffffffff9b459297
#15 [ffffb0778d947ec8] vfs_write at ffffffff9b459bd8
#16 [ffffb0778d947f08] sys_write at ffffffff9b45b1e5
#17 [ffffb0778d947f50] entry_SYSCALL_64_fastpath at ffffffff9bae8e3b
    RIP: 00007fed5a6013c0  RSP: 00007ffc9db851b8  RFLAGS: 00000246
    RAX: ffffffffffffffda  RBX: 00007fed5a8cf620  RCX: 00007fed5a6013c0
    RDX: 0000000000000002  RSI: 00000000020f5008  RDI: 0000000000000001
    RBP: 0000000000000001   R8: 00007fed5a8d0780   R9: 00007fed5af08700
    R10: 0000000000000001  R11: 0000000000000246  R12: 0000000000000001
    R13: 000000000226ef38  R14: 0000000000000000  R15: 00000000004d0af9
    ORIG_RAX: 0000000000000001  CS: 0033  SS: 002b
crash&gt; 
</code></pre></div></div>

<h2 id="关闭">关闭</h2>

<p>修改： /etc/default/kdump-tools</p>

<p>查看状态</p>

<p>kdump-config show</p>

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

<p>https://zhuanlan.zhihu.com/p/104292358</p>

<p>https://www.cnblogs.com/lshs/p/6038935.html</p>

<p>https://askubuntu.com/questions/805617/kdump-config-show-gives-not-ready-to-kdump-but-memory-was-reserved</p>]]></content><author><name>pavilion</name></author><category term="rdma" /><category term="linux" /><category term="ubuntu" /><category term="crash" /><summary type="html"><![CDATA[CARASH工具使用]]></summary></entry><entry><title type="html">Black-box Concurrent Data Structures for NUMA Architectures</title><link href="https://lightlisten.cn/rdma/2021/11/05/Black-box-Concurrent-Data-Structures-for-NUMA-Architectures.html" rel="alternate" type="text/html" title="Black-box Concurrent Data Structures for NUMA Architectures" /><published>2021-11-05T00:00:00+08:00</published><updated>2021-11-05T00:00:00+08:00</updated><id>https://lightlisten.cn/rdma/2021/11/05/Black-box%20Concurrent%20Data%20Structures%20for%20NUMA%20Architectures</id><content type="html" xml:base="https://lightlisten.cn/rdma/2021/11/05/Black-box-Concurrent-Data-Structures-for-NUMA-Architectures.html"><![CDATA[<h1 id="black-box-concurrent-data-structures-for-numa-architectures">Black-box Concurrent Data Structures for NUMA Architectures</h1>

<h1 id="背景">背景</h1>

<p>当前高性能服务器都是NUMA机器</p>

<p>全局数据时常需要在不同线程或者说numa节点中进行共享。比如操作系统中的内存管理的数据，多核应用的全局数据等。这些数据都需要较好的并行性支持（Concurrent data structure）。</p>

<p>关键在于更好的扩展性和并行性， 与编程难度之间的平衡</p>

<h1 id="相关工作">相关工作</h1>

<p>•锁或者cas等方式来自己写，自由度很高但是对NUMA并不敏感，这将可能损害整体的性能</p>

<p>•已有的black-box方法，如Flat combining (w/o RWL)，他们并不是为NUMA开发，仍然容易受到NUMA结构的影响而损失性能。</p>

<p>•NUMA-aware algorithm, 针对特定数据结构开发，不通用而且很少。</p>

<h1 id="设计">设计</h1>

<p>•提出了一种能够适应NUMA结构的黑箱算法，能够比较方便地实现数据结构的并行，并获得很好的性能。</p>

<blockquote>
  <p>参考分布式系统：每个节点实现一个数据备份（replica），使用一个共享的日志来进行同步</p>

  <p>参考共享内存算法：采取层次化的numa感知设计实现的日志来实现可序列化级别的一致性</p>
</blockquote>

<h2 id="结构">结构</h2>

<p>需要数据结构提供接口，然后就能够比较方便的使用他的算法，得到一致性。</p>

<p><code class="language-plaintext highlighter-rouge">Create</code>将产生一个这个数据结构的实例。</p>

<p>而<code class="language-plaintext highlighter-rouge">Execute</code>将执行对传入的数据实例（ptr）执行操作（op，如push， pop等），可以参数（args）。</p>

<p><code class="language-plaintext highlighter-rouge">IsReadOnly</code>则用来判断一个操作是不是只读的。</p>

<p>然后当我们想要修改这个数据实例的时候调用他们提供的<strong>ExecuteConcurrent</strong>来代替Execute就可以了。</p>

<h2 id="节点内部一致性">节点内部一致性</h2>

<p>•执行者（ combiner 整合者？），<strong>每个节点同一时间都只有一个线程能够成为执行者，而只有执行者才能执行修改日志和备份的操作。获得锁</strong>并写玩日志后会获取节点内写者锁。</p>

<p>•写操作会首先把操作和参数存放到当前cpu对应的一个预留位置。执行者获取当前的所有待执行任务，记录到日志中，并把日志依次执行，直到当前任务完成。</p>

<h2 id="节点间一致性">节点间一致性</h2>

<p>•按照日志顺序执行每个日志项的操作。</p>

<p>•用写操作进入日志的时候，日志的末尾的位置作为这个操作执行的时间顺序。</p>

<p>•如果说在执行过程中日志中有新的写入，那也认为那是“未来”的事情。</p>

<h2 id="只读优化">只读优化</h2>

<p>•只读操作的并行</p>

<p>•读操作则尝试把他当作写操作执行（避免空等写操作到来）</p>

<p>•不然就等日志执行到进入函数的位置，尝试获取读者锁执行。</p>

<p>•主要不同在于使用的是进入函数时，日志第一条未被执行的日志的位置completedTail作为读操作的“发生时间”。只要当前备份更新到超过这个时间就算足够新了。</p>

<p>•这个操作主要是因为读操作不会留在日志中，需要一个位置来作为操作发生时间，保证可序列化。而他只要保证所读的是进入函数时，所有备份中最新的那份（&gt;completedTail，认为足够新）。</p>

<p>•更新的时候一次更新一批待执行日志，但不会坚持去执行完所有日志。</p>

<h1 id="算法">算法</h1>

<p><img src="../assets/images/img/paper3.png" alt="alg" /></p>

<h2 id="数据结构">数据结构</h2>

<p><strong>全局</strong></p>

<p>•日志shareLog</p>

<p>•日志记录下标logTail，表示当前最后写的位置</p>

<p>•日志第一个未执行的位置completedTail</p>

<p><strong>每个节点一个</strong></p>

<p>•数据备份</p>

<p>•下标logTail，表示当前备份执行到日志的位置</p>

<p>•整合锁，每个节点一个，获取到就去整合（执行日志，执行excute操作），不然就等其他人做完就行。</p>

<p>•读写锁，执行前需要根据类型获取。Excute需要写锁，不然读锁就行</p>

<p>每个cpu核心</p>

<p>•Slot 和response，用来存储当前任务和结果</p>

<h2 id="逻辑">逻辑</h2>

<p>执行excute的时候，</p>

<p>首先判断操作是否只读，是则记录当前全局最新备份的版本comletedTail，等待或成为执行者更新当前备份版本localTail到足够新的版本。</p>

<p>然后尝试获取读者锁，然后执行，释放锁。</p>

<p>如果是执行操作，</p>

<p>1.记录当前操作到指定区域。</p>

<p>2.尝试获取执行者锁，成为执行者。或者是等别人执行完。</p>

<p>3.搜集所有线程的待执行任务，写到日志中，</p>

<p>4.获取写者锁，更新当前备份版本，如果当前版本为全局最新则尝试更新全局最新为当前版本。</p>

<p>5.执行所收集的所有任务。</p>

<p>6.释放所有锁</p>

<h1 id="优化细节">优化细节</h1>

<h2 id="优化">优化</h2>

<p>•改进分布式读写锁</p>

<p>•每个cpu有一个锁（称为读者锁），写者锁就是要获取所有的读者锁。</p>

<p>•改进为：有一个写者锁，写者去获取这个锁，然后等所有读者锁释放即可。释放的时候释放这个写者锁即可。读者获取锁的时候，先等待写者锁释放，然后获取读者锁，再检查一次写者锁。被锁则释放，否则开始使用。   这样的话，读者写者都只要进行一次原子写就可以了。而因为写者只会有一个且很多工作在非关键区（写日志），不会导致读者饥饿的情况。</p>

<p>•循环日志队列</p>

<p>•对架构的优化-比如对齐之类的</p>

<h2 id="具体可能遇到的问题">具体可能遇到的问题</h2>

<p>•线程数量，允许比核数更多的线程，形成更大的批处理，提高效率。</p>

<p>•日志长度，或许可以动态扩展，但是并没有。实际是一个1M容量的循环队列。</p>

<p>•近cpu申请内存。</p>

<p>•有时候可能某个节点长时间没更新，导致日志爆满。设定一个线程，定时执行下一次。但是一般应用不需要这个。</p>

<p>•多种数据结构混合。可以组合成一个包含他们的数据结构。</p>

<h2 id="结果">结果</h2>

<h2 id="常用数据结构测试">常用数据结构测试</h2>

<p>•基本上都取得最好的效果。</p>

<p>•以Skip list priority queue为例</p>

<p>•在无更新操作的时候，LF没有锁开销一枝独秀，NR，FC+以及RWL都采用RWL差在需要获取锁的开销，而NR则进一步需要检查日志，是不是最新备份。</p>

<p>•10%更新的时候，大家都会受到NUMA的影响而性能下降。但NR在线程超过一个节点（28个超线程）的时候性能损失更少。比其他方法好1.7x-41x</p>

<p>•在需要一直更新的时候，大家的性能损失都相当严重。不过同样，NR损失更少。2.4x-9.4x。</p>

<p>•每次读写的时候在这个数据结构外围写e个随机位置的时候，SL就没有这个效果了。解释是可以造成cache污染的效果。</p>

<p>•但一些特定的结构，如栈，在NA使用特定技术消元（elimination）来优化的情况下能够比NR好很多之外。（取得几乎是线性的扩展能力）</p>

<h2 id="其他并行数据结构对比扩展性">其他并行数据结构对比扩展性</h2>

<p>•数据结构：一个简单的n=200K个元素的buffer，每个操作会访问其中固定的一个元素（模拟指针，比如树的根之类的角色），然后剩余的（C-1 = 7）个元素则是均匀随机地访问。</p>

<p>•可以看到，核数比较少地时候，还有FC，FC+，RWL可以稍微跟NR相比，但是在线程数逐渐增大，NR的表现会逐渐地变得突出。在10%更新的情况下能达到4x的加速</p>

<h2 id="实际应用测试">实际应用测试</h2>

<p>•实际应用redis测试</p>

<p>•读写混合的YCSB benchmark来做测试</p>

<p>•先创建一个10000个元素的排序集。用多个线程不断地对他进行读或更新随机（均匀分布）个元素。</p>

<p>•可以看到较低更新率的时候，NR的吞吐率都是最高的。能够获得2.6x-14x的加速比。</p>

<p>而较高更新率的时候，NR的性能也几乎没有损失。能够获得1.1x-4.4x的加速比</p>

<h2 id="优化选项效果">优化选项效果</h2>

<p>•每个改进的效果，可以看到</p>

<p>•#4并行更新备份（如果让所有）有效改进跨node读写的时候的性能</p>

<p>•#1和#5的改进有效的改进节点内的效果。</p>

<p>•#3中可以看到允许读者在执行者写日志的时候工作的能有比较好的效果。</p>

<p><img src="../assets/images/img/paper4.png" alt="alg" /></p>

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

<p><a href="https://dl.acm.org/doi/10.1145/3093336.3037721">Black-box Concurrent Data Structures for NUMA Architectures</a></p>]]></content><author><name>pavilion</name></author><category term="rdma" /><category term="numa" /><category term="data structure" /><category term="ASPLOS" /><category term="os" /><category term="paper" /><summary type="html"><![CDATA[Black-box Concurrent Data Structures for NUMA Architectures 背景 当前高性能服务器都是NUMA机器 全局数据时常需要在不同线程或者说numa节点中进行共享。比如操作系统中的内存管理的数据，多核应用的全局数据等。这些数据都需要较好的并行性支持（Concurrent data structure）。 关键在于更好的扩展性和并行性， 与编程难度之间的平衡 相关工作 •锁或者cas等方式来自己写，自由度很高但是对NUMA并不敏感，这将可能损害整体的性能 •已有的black-box方法，如Flat combining (w/o RWL)，他们并不是为NUMA开发，仍然容易受到NUMA结构的影响而损失性能。 •NUMA-aware algorithm, 针对特定数据结构开发，不通用而且很少。 设计 •提出了一种能够适应NUMA结构的黑箱算法，能够比较方便地实现数据结构的并行，并获得很好的性能。 参考分布式系统：每个节点实现一个数据备份（replica），使用一个共享的日志来进行同步 参考共享内存算法：采取层次化的numa感知设计实现的日志来实现可序列化级别的一致性 结构 需要数据结构提供接口，然后就能够比较方便的使用他的算法，得到一致性。 Create将产生一个这个数据结构的实例。 而Execute将执行对传入的数据实例（ptr）执行操作（op，如push， pop等），可以参数（args）。 IsReadOnly则用来判断一个操作是不是只读的。 然后当我们想要修改这个数据实例的时候调用他们提供的ExecuteConcurrent来代替Execute就可以了。 节点内部一致性 •执行者（ combiner 整合者？），每个节点同一时间都只有一个线程能够成为执行者，而只有执行者才能执行修改日志和备份的操作。获得锁并写玩日志后会获取节点内写者锁。 •写操作会首先把操作和参数存放到当前cpu对应的一个预留位置。执行者获取当前的所有待执行任务，记录到日志中，并把日志依次执行，直到当前任务完成。 节点间一致性 •按照日志顺序执行每个日志项的操作。 •用写操作进入日志的时候，日志的末尾的位置作为这个操作执行的时间顺序。 •如果说在执行过程中日志中有新的写入，那也认为那是“未来”的事情。 只读优化 •只读操作的并行 •读操作则尝试把他当作写操作执行（避免空等写操作到来） •不然就等日志执行到进入函数的位置，尝试获取读者锁执行。 •主要不同在于使用的是进入函数时，日志第一条未被执行的日志的位置completedTail作为读操作的“发生时间”。只要当前备份更新到超过这个时间就算足够新了。 •这个操作主要是因为读操作不会留在日志中，需要一个位置来作为操作发生时间，保证可序列化。而他只要保证所读的是进入函数时，所有备份中最新的那份（&gt;completedTail，认为足够新）。 •更新的时候一次更新一批待执行日志，但不会坚持去执行完所有日志。 算法 数据结构 全局 •日志shareLog •日志记录下标logTail，表示当前最后写的位置 •日志第一个未执行的位置completedTail 每个节点一个 •数据备份 •下标logTail，表示当前备份执行到日志的位置 •整合锁，每个节点一个，获取到就去整合（执行日志，执行excute操作），不然就等其他人做完就行。 •读写锁，执行前需要根据类型获取。Excute需要写锁，不然读锁就行 每个cpu核心 •Slot 和response，用来存储当前任务和结果 逻辑 执行excute的时候， 首先判断操作是否只读，是则记录当前全局最新备份的版本comletedTail，等待或成为执行者更新当前备份版本localTail到足够新的版本。 然后尝试获取读者锁，然后执行，释放锁。 如果是执行操作， 1.记录当前操作到指定区域。 2.尝试获取执行者锁，成为执行者。或者是等别人执行完。 3.搜集所有线程的待执行任务，写到日志中， 4.获取写者锁，更新当前备份版本，如果当前版本为全局最新则尝试更新全局最新为当前版本。 5.执行所收集的所有任务。 6.释放所有锁 优化细节 优化 •改进分布式读写锁 •每个cpu有一个锁（称为读者锁），写者锁就是要获取所有的读者锁。 •改进为：有一个写者锁，写者去获取这个锁，然后等所有读者锁释放即可。释放的时候释放这个写者锁即可。读者获取锁的时候，先等待写者锁释放，然后获取读者锁，再检查一次写者锁。被锁则释放，否则开始使用。 这样的话，读者写者都只要进行一次原子写就可以了。而因为写者只会有一个且很多工作在非关键区（写日志），不会导致读者饥饿的情况。 •循环日志队列 •对架构的优化-比如对齐之类的 具体可能遇到的问题 •线程数量，允许比核数更多的线程，形成更大的批处理，提高效率。 •日志长度，或许可以动态扩展，但是并没有。实际是一个1M容量的循环队列。 •近cpu申请内存。 •有时候可能某个节点长时间没更新，导致日志爆满。设定一个线程，定时执行下一次。但是一般应用不需要这个。 •多种数据结构混合。可以组合成一个包含他们的数据结构。 结果 常用数据结构测试 •基本上都取得最好的效果。 •以Skip list priority queue为例 •在无更新操作的时候，LF没有锁开销一枝独秀，NR，FC+以及RWL都采用RWL差在需要获取锁的开销，而NR则进一步需要检查日志，是不是最新备份。 •10%更新的时候，大家都会受到NUMA的影响而性能下降。但NR在线程超过一个节点（28个超线程）的时候性能损失更少。比其他方法好1.7x-41x •在需要一直更新的时候，大家的性能损失都相当严重。不过同样，NR损失更少。2.4x-9.4x。 •每次读写的时候在这个数据结构外围写e个随机位置的时候，SL就没有这个效果了。解释是可以造成cache污染的效果。 •但一些特定的结构，如栈，在NA使用特定技术消元（elimination）来优化的情况下能够比NR好很多之外。（取得几乎是线性的扩展能力） 其他并行数据结构对比扩展性 •数据结构：一个简单的n=200K个元素的buffer，每个操作会访问其中固定的一个元素（模拟指针，比如树的根之类的角色），然后剩余的（C-1 = 7）个元素则是均匀随机地访问。 •可以看到，核数比较少地时候，还有FC，FC+，RWL可以稍微跟NR相比，但是在线程数逐渐增大，NR的表现会逐渐地变得突出。在10%更新的情况下能达到4x的加速 实际应用测试 •实际应用redis测试 •读写混合的YCSB benchmark来做测试 •先创建一个10000个元素的排序集。用多个线程不断地对他进行读或更新随机（均匀分布）个元素。 •可以看到较低更新率的时候，NR的吞吐率都是最高的。能够获得2.6x-14x的加速比。 而较高更新率的时候，NR的性能也几乎没有损失。能够获得1.1x-4.4x的加速比 优化选项效果 •每个改进的效果，可以看到 •#4并行更新备份（如果让所有）有效改进跨node读写的时候的性能 •#1和#5的改进有效的改进节点内的效果。 •#3中可以看到允许读者在执行者写日志的时候工作的能有比较好的效果。 参考资料 Black-box Concurrent Data Structures for NUMA Architectures]]></summary></entry><entry><title type="html">gblic安装</title><link href="https://lightlisten.cn/linux/2021/10/27/glibc%E5%AE%89%E8%A3%85.html" rel="alternate" type="text/html" title="gblic安装" /><published>2021-10-27T00:00:00+08:00</published><updated>2021-10-27T00:00:00+08:00</updated><id>https://lightlisten.cn/linux/2021/10/27/glibc%E5%AE%89%E8%A3%85</id><content type="html" xml:base="https://lightlisten.cn/linux/2021/10/27/glibc%E5%AE%89%E8%A3%85.html"><![CDATA[<h1 id="gblic安装">gblic安装</h1>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://sourceware.org/git/glibc.git
cd glibc
# 选择你的stable版本
git checkout release/2.23/master

# 有时候会说gawk太旧
sudo apt-get install gawk
sudo apt-get install bison

mkdir glibc-build
cd glibc-build
../glibc-2.23/configure -prefix=/some/path
make
make install


</code></pre></div></div>]]></content><author><name>pavilion</name></author><category term="linux" /><category term="glibc" /><category term="kernel" /><summary type="html"><![CDATA[gblic安装 git clone https://sourceware.org/git/glibc.git cd glibc # 选择你的stable版本 git checkout release/2.23/master # 有时候会说gawk太旧 sudo apt-get install gawk sudo apt-get install bison mkdir glibc-build cd glibc-build ../glibc-2.23/configure -prefix=/some/path make make install]]></summary></entry><entry><title type="html">匿名内存的一生</title><link href="https://lightlisten.cn/linux/2021/10/20/%E5%8C%BF%E5%90%8D%E5%86%85%E5%AD%98%E7%9A%84%E4%B8%80%E7%94%9F.html" rel="alternate" type="text/html" title="匿名内存的一生" /><published>2021-10-20T00:00:00+08:00</published><updated>2021-10-20T00:00:00+08:00</updated><id>https://lightlisten.cn/linux/2021/10/20/%E5%8C%BF%E5%90%8D%E5%86%85%E5%AD%98%E7%9A%84%E4%B8%80%E7%94%9F</id><content type="html" xml:base="https://lightlisten.cn/linux/2021/10/20/%E5%8C%BF%E5%90%8D%E5%86%85%E5%AD%98%E7%9A%84%E4%B8%80%E7%94%9F.html"><![CDATA[<blockquote>
  <p>前文再接，现在来说说，<code class="language-plaintext highlighter-rouge">linux</code>操作系统的<code class="language-plaintext highlighter-rouge">内存管理</code>部分. 当然， 这里主要是描述<code class="language-plaintext highlighter-rouge">匿名内存</code>部分</p>
</blockquote>

<h1 id="内存简介">内存简介</h1>

<blockquote>
  <p>可能有一种误解，内存申请完之后，在初始化之前是匿名内存，初始化后就不是了。但是对于内核而言，这是错误的。</p>

  <p>某位作者</p>
</blockquote>

<p>内存映射可以分成两类<a href="http://landley.net/writing/memory-faq.txt">src</a>，<code class="language-plaintext highlighter-rouge">匿名内存映射</code>和<code class="language-plaintext highlighter-rouge">文件内存映射</code>。简单而言，有文件作为备份的内存都是作为文件内存映射,没有则是匿名内存映射。</p>

<p>我们常用的malloc申请的内存显然就是匿名内存。下面主要讲述我们是怎么linux内核交互来获取和释放这些内存的。</p>

<h1 id="申请">申请</h1>

<p>对于linux而言，一般都是使用malloc来进行申请。当然也有一些所谓的自己的设计，但是都是通过系统调用<code class="language-plaintext highlighter-rouge">do_brk</code>进行内存申请。</p>

<p>当然，这里的”申请“，并不是直接获得内存。而只是打出申请。虽然会通过，但系统不一定给的。<a href="https://www.kernel.org/doc/Documentation/vm/overcommit-accounting">只是一张信用不明的支票</a>。</p>

<p><code class="language-plaintext highlighter-rouge">do_brk</code>通过扩充申请程序的vma，malloc中是堆空间，或者说是虚地址范围。让程序能够访问这些虚拟地址。意味着当你需要的时候可以去银行找柜员。</p>

<h1 id="初始化">初始化</h1>

<p>当你尝试第一次访问这些申请好内存的时候，会触发<code class="language-plaintext highlighter-rouge">缺页中断</code>，进入linux内核来进行处理。</p>

<p><code class="language-plaintext highlighter-rouge">do_page_fault</code>会首先检查进程对应的mm包含的vma的范围，也就是程序中打好申请的虚地址是否包含当前尝试访问的页。如果发现这是一张”假支票“ 越界访问，将直接报错结束进程，也就是segment fault。</p>

<p>而正常情况下，将构造结构体struct vm_fault然后<a href="http://www.ltstriker.cn/linux/2021/08/18/%E4%B8%80%E6%AC%A1%E8%AE%BF%E5%AD%98.html">最终</a>进入handle_pte_fault进行处理。对于我们当前状态，因为还没有初始化，连pte都不存在，自然进入do_anonymous_page进行处理。</p>

<p>里面将申请一个空闲页面并挂接映射，设置pte。这个时候程序就能够正常访问这个内存页了。</p>

<p>退出，告辞。</p>

<h1 id="交换">交换</h1>

<p>这里，可以参考<a href="http://www.ltstriker.cn/linux/2021/08/18/%E4%B8%80%E6%AC%A1%E8%AE%BF%E5%AD%98.html">上一篇文章</a>里面有一个大概的介绍。</p>

<h1 id="退出">退出</h1>

<blockquote>
  <p>那这个内存怎么返回到系统中呢？</p>
</blockquote>

<p>主动调用<code class="language-plaintext highlighter-rouge">munmap</code>或者程序退出的时候统一释放。</p>

<p><code class="language-plaintext highlighter-rouge">munmap</code>主要是对vma的范围进行限制，跟do_brk相反。</p>

<p>程序退出，是通过调用do_exit实现的。里面会调用exit_mm最终会遍历vma，对虚地址映射的所有页面进行释放。内存最终回到操作系统。</p>

<p>最后在<code class="language-plaintext highlighter-rouge">free_pages_and_swap_cache</code>对对应的内存和swap清空，彻底删除。</p>

<h1 id="其他">其他</h1>

<p>可能有小伙伴，问了，那具体是怎么进行映射，解除映射，系统怎么分配内存和回收内存呢？</p>

<p>敬请期待后续：</p>

<p><strong>内存映射二三</strong></p>

<p><strong>内存收发</strong></p>

<h1 id="引流之主">引流之主</h1>

<p>点点收藏我(作者)的<a href="http://www.ltstriker.cn/">个人博客</a>,  获取第一时间的更新呀~</p>

<p>如果对什么内容感兴趣，也可以跟我交流、催更~</p>

<p>不定时更新哦~</p>

<h1 id="参考资料">参考资料</h1>
<p>linux-4.11.0代码</p>

<p>《奔跑吧！linux内核》</p>

<p>《计算机体系结构》胡伟武</p>]]></content><author><name>pavilion</name></author><category term="linux" /><category term="memory" /><category term="swap" /><category term="cache" /><category term="kernel" /><summary type="html"><![CDATA[前文再接，现在来说说，linux操作系统的内存管理部分. 当然， 这里主要是描述匿名内存部分 内存简介 可能有一种误解，内存申请完之后，在初始化之前是匿名内存，初始化后就不是了。但是对于内核而言，这是错误的。 某位作者 内存映射可以分成两类src，匿名内存映射和文件内存映射。简单而言，有文件作为备份的内存都是作为文件内存映射,没有则是匿名内存映射。 我们常用的malloc申请的内存显然就是匿名内存。下面主要讲述我们是怎么linux内核交互来获取和释放这些内存的。 申请 对于linux而言，一般都是使用malloc来进行申请。当然也有一些所谓的自己的设计，但是都是通过系统调用do_brk进行内存申请。 当然，这里的”申请“，并不是直接获得内存。而只是打出申请。虽然会通过，但系统不一定给的。只是一张信用不明的支票。 do_brk通过扩充申请程序的vma，malloc中是堆空间，或者说是虚地址范围。让程序能够访问这些虚拟地址。意味着当你需要的时候可以去银行找柜员。 初始化 当你尝试第一次访问这些申请好内存的时候，会触发缺页中断，进入linux内核来进行处理。 do_page_fault会首先检查进程对应的mm包含的vma的范围，也就是程序中打好申请的虚地址是否包含当前尝试访问的页。如果发现这是一张”假支票“ 越界访问，将直接报错结束进程，也就是segment fault。 而正常情况下，将构造结构体struct vm_fault然后最终进入handle_pte_fault进行处理。对于我们当前状态，因为还没有初始化，连pte都不存在，自然进入do_anonymous_page进行处理。 里面将申请一个空闲页面并挂接映射，设置pte。这个时候程序就能够正常访问这个内存页了。 退出，告辞。 交换 这里，可以参考上一篇文章里面有一个大概的介绍。 退出 那这个内存怎么返回到系统中呢？ 主动调用munmap或者程序退出的时候统一释放。 munmap主要是对vma的范围进行限制，跟do_brk相反。 程序退出，是通过调用do_exit实现的。里面会调用exit_mm最终会遍历vma，对虚地址映射的所有页面进行释放。内存最终回到操作系统。 最后在free_pages_and_swap_cache对对应的内存和swap清空，彻底删除。 其他 可能有小伙伴，问了，那具体是怎么进行映射，解除映射，系统怎么分配内存和回收内存呢？ 敬请期待后续： 内存映射二三 内存收发 引流之主 点点收藏我(作者)的个人博客, 获取第一时间的更新呀~ 如果对什么内容感兴趣，也可以跟我交流、催更~ 不定时更新哦~ 参考资料 linux-4.11.0代码 《奔跑吧！linux内核》 《计算机体系结构》胡伟武]]></summary></entry><entry><title type="html">RDMA over Commodity Ethernet at scale</title><link href="https://lightlisten.cn/rdma/2021/08/31/rdma-over-commodity-ethernet-at-scale.html" rel="alternate" type="text/html" title="RDMA over Commodity Ethernet at scale" /><published>2021-08-31T00:00:00+08:00</published><updated>2021-08-31T00:00:00+08:00</updated><id>https://lightlisten.cn/rdma/2021/08/31/rdma-over-commodity-ethernet-at-scale</id><content type="html" xml:base="https://lightlisten.cn/rdma/2021/08/31/rdma-over-commodity-ethernet-at-scale.html"><![CDATA[<h1 id="rdma-over-commodity-ethernet-at-scale">RDMA over Commodity Ethernet at scale</h1>

<h2 id="改进pfc">改进PFC</h2>
<p><img src="/assets/images/img/paper1.png" alt="dead lock" />
过去的PFC基于2层的协议VLAN，在以太网帧中夹带VLAN TAG来表示优先级。这会带来两个问题。</p>
<ol>
  <li>必须使用trunk模式的端口。操作系统的一些支持服务比如安装升级命令就很难使用，二数据中心中的自动化管理如他们的PXE就没办法用了。</li>
  <li>难以转成3层的IP转发而不是继续用mac的二层桥接。3层网络可以有更好的扩展性，监控，公开而且标准等特性。但这将导致在跨子网的时候无法继续使用VLAN。</li>
</ol>

<p>解决方案：PFC报文不变，在IP报文中新增字段DSCP来取代VLAN TAG，来完成包的优先级的功能。</p>

<h2 id="安全性挑战">安全性挑战</h2>
<h3 id="rdma传输活锁-livelock">RDMA传输活锁 livelock</h3>
<blockquote>
  <p>livelock， 链接一直在传东西，但是实际上完全没成功传过去。</p>

  <p>实验： 每次传一个4MB(4000个包)，设定每隔256个丢一个包(seq&amp;0xff == 0xff)，即丢包率1/256.这个时候假设从0开始传，那个第256个包就会丢，然后需要从0开始重传。然后在256继续丢。</p>

  <p>原因： RDMA使用的是go back 0算法来进行重传。因为假设极少丢包，使用goback0可以不需要管理那么多的状态。</p>

  <p>解决方法： 改成go back N。从丢掉的那个包开始重传。同样简单，而且不会死锁。</p>
  <h3 id="pfc死锁">PFC死锁</h3>
  <p><img src="/assets/images/img/paper.png" alt="dead lock" />
图片里面S1尝试发送紫色包到S3，黑色包到S5. 而S4打算发送蓝色包到S2. S2和S3挂掉了，S5对应的对口T1.p2拥堵状态。</p>

  <p>s1的包经过T0.p0,T0.p2, La.P0,La.P1到达T1.p3, 这个时候T1上S3的MAC地址已经没了，ARP还在，那么就会洪泛这个包。这个时候p3仍然不会删掉紫色包，直到他去到队列头。而黑色包因为拥挤的T1.p2而无法转发过去。因此T1.p3,La.p1,La.p0,T0.p2,To.p0,S1都会被PFC暂停发包。</p>

  <p>而S4发的包同样地经过T1.p1,T1.p4, Lb.p1, Lb.p0到达T0.p3，而S2的记录没了，洪泛蓝色包，发现T0.p2无法转发，触发PFC，暂停掉T0.p3,Lb.p0,Lb.p1,T1.p4,T1.p1,S4.</p>

  <p>这个时候死循环形成即使S5的包都拿走了，也没办法解决。因为T1.p4包含的紫色包没有释放，T1.p3的紫色包不会被丢弃。因此死锁会持续</p>
</blockquote>

<blockquote>
  <p>主要原因是PFC和ARP的交互。交换机里ARP的记录存活时间是4小时，而MAC表的是5分钟，因此存在一种可能就是ARP表能找到，但MAC表里面没有。默认的行为里，交换机会进行洪泛，来找对应的机器。而如果有port拥挤或者说是被暂停了的话，这个传输路径的端口都会被暂停。就像图里的两条反向路径，互相锁死。即使后来拥挤解除，端口的死锁也已经解不开了。</p>

  <p>解决方法: 找到ARP但找不到MAC就丢掉，这样就不需要洪泛来查找，被其他无关端口停住。</p>

  <p>(opt)统一存活时间会带来额外的开销，卸载到cpu同理</p>
</blockquote>

<blockquote>
  <p>找不到ARP也会洪泛？</p>

  <p>不会，那说明机器就关了，不需要找。。。</p>
  <h3 id="nic-pfc暂停帧风暴">NIC PFC暂停帧风暴</h3>
  <p>起因是网卡接收端流水线出了个bug，但是后来修正了。但是提出了两个预防措施。</p>
</blockquote>

<p>NIC PFC暂停帧风暴指的是一个网卡使用PFC暂停了整个网络。</p>

<ol>
  <li>网卡端，发现流水线停住一段时间（100ms）就关掉无损模式，不再发出PFC帧，开始丢包。因为他们发现这时网卡一般就不能用了。</li>
  <li>交换机端，发现有端口无法清空，而且不停进来PFC帧就把端口的无损模式关掉，直到一段时间（200ms）不在收到PFC帧,再打开。因为网卡可能被重置了。</li>
  <li>一起用也没关系，网卡会丢报的。不会影响网络。</li>
</ol>

<h3 id="接收端过慢">接收端过慢</h3>
<p>因为接收端有一个MTT存储vpn2ppn的映射，没命中的时候就需要替换。而他只有2K，默认4KB的页而言只能处理8MB的内存。替换和获取新的虚拟地址都需要时间。接受流水会受到很大的影响。</p>

<p>进而会导致很多的PFC暂停帧出现。只是没有风暴那么严重，但对网络的性能还是会有影响。</p>

<p>缓解办法</p>
<ol>
  <li>大页</li>
  <li>交换机动态buffer共享</li>
</ol>

<h2 id="rdma的管理和测量">RDMA的管理和测量</h2>
<blockquote>
  <p>记录每个时间段的包数量，byte总数，PFC数量等用来监控集群状态。</p>

  <p>建立一个RDMA的Pingmesh用来动态监测集群的延迟变化，发512byte的包到交换机和服务器来测。</p>
</blockquote>

<h3 id="延迟和带宽">延迟和带宽</h3>
<p>在他们的数据中心上，总体的延迟状态对比，尾延迟大幅降低。</p>

<p>用一个三层的576台服务器来测试带宽，podset和spine之间的带宽能到到2.56Tb/s.而ToR之间贷款为3.0Tb/s这大概是总带宽的60%。（说是因为ECMP的哈希碰撞，不是很懂）</p>

<p>用一个两层的24台服务器来测试，维持每台服务器7Gb/s的带宽，测试集群中的延迟，发现集群的RDMA延迟从99th的50us，99.9th的80us上升到400us和800us。而这里tcp的99th延迟大概是500us。随着带宽增大，延迟会上升。</p>

<h2 id="配置相关">配置相关</h2>
<p>其他主要是一些配置细节，效果很好，问题都避免了。
不是很懂。。。没有实感。</p>

<h2 id="参考资料">参考资料</h2>]]></content><author><name>pavilion</name></author><category term="rdma" /><category term="memory" /><category term="rdma" /><category term="ib" /><category term="paper" /><summary type="html"><![CDATA[RDMA over Commodity Ethernet at scale 改进PFC 过去的PFC基于2层的协议VLAN，在以太网帧中夹带VLAN TAG来表示优先级。这会带来两个问题。 必须使用trunk模式的端口。操作系统的一些支持服务比如安装升级命令就很难使用，二数据中心中的自动化管理如他们的PXE就没办法用了。 难以转成3层的IP转发而不是继续用mac的二层桥接。3层网络可以有更好的扩展性，监控，公开而且标准等特性。但这将导致在跨子网的时候无法继续使用VLAN。 解决方案：PFC报文不变，在IP报文中新增字段DSCP来取代VLAN TAG，来完成包的优先级的功能。 安全性挑战 RDMA传输活锁 livelock livelock， 链接一直在传东西，但是实际上完全没成功传过去。 实验： 每次传一个4MB(4000个包)，设定每隔256个丢一个包(seq&amp;0xff == 0xff)，即丢包率1/256.这个时候假设从0开始传，那个第256个包就会丢，然后需要从0开始重传。然后在256继续丢。 原因： RDMA使用的是go back 0算法来进行重传。因为假设极少丢包，使用goback0可以不需要管理那么多的状态。 解决方法： 改成go back N。从丢掉的那个包开始重传。同样简单，而且不会死锁。 PFC死锁 图片里面S1尝试发送紫色包到S3，黑色包到S5. 而S4打算发送蓝色包到S2. S2和S3挂掉了，S5对应的对口T1.p2拥堵状态。 s1的包经过T0.p0,T0.p2, La.P0,La.P1到达T1.p3, 这个时候T1上S3的MAC地址已经没了，ARP还在，那么就会洪泛这个包。这个时候p3仍然不会删掉紫色包，直到他去到队列头。而黑色包因为拥挤的T1.p2而无法转发过去。因此T1.p3,La.p1,La.p0,T0.p2,To.p0,S1都会被PFC暂停发包。 而S4发的包同样地经过T1.p1,T1.p4, Lb.p1, Lb.p0到达T0.p3，而S2的记录没了，洪泛蓝色包，发现T0.p2无法转发，触发PFC，暂停掉T0.p3,Lb.p0,Lb.p1,T1.p4,T1.p1,S4. 这个时候死循环形成即使S5的包都拿走了，也没办法解决。因为T1.p4包含的紫色包没有释放，T1.p3的紫色包不会被丢弃。因此死锁会持续 主要原因是PFC和ARP的交互。交换机里ARP的记录存活时间是4小时，而MAC表的是5分钟，因此存在一种可能就是ARP表能找到，但MAC表里面没有。默认的行为里，交换机会进行洪泛，来找对应的机器。而如果有port拥挤或者说是被暂停了的话，这个传输路径的端口都会被暂停。就像图里的两条反向路径，互相锁死。即使后来拥挤解除，端口的死锁也已经解不开了。 解决方法: 找到ARP但找不到MAC就丢掉，这样就不需要洪泛来查找，被其他无关端口停住。 (opt)统一存活时间会带来额外的开销，卸载到cpu同理 找不到ARP也会洪泛？ 不会，那说明机器就关了，不需要找。。。 NIC PFC暂停帧风暴 起因是网卡接收端流水线出了个bug，但是后来修正了。但是提出了两个预防措施。 NIC PFC暂停帧风暴指的是一个网卡使用PFC暂停了整个网络。 网卡端，发现流水线停住一段时间（100ms）就关掉无损模式，不再发出PFC帧，开始丢包。因为他们发现这时网卡一般就不能用了。 交换机端，发现有端口无法清空，而且不停进来PFC帧就把端口的无损模式关掉，直到一段时间（200ms）不在收到PFC帧,再打开。因为网卡可能被重置了。 一起用也没关系，网卡会丢报的。不会影响网络。 接收端过慢 因为接收端有一个MTT存储vpn2ppn的映射，没命中的时候就需要替换。而他只有2K，默认4KB的页而言只能处理8MB的内存。替换和获取新的虚拟地址都需要时间。接受流水会受到很大的影响。 进而会导致很多的PFC暂停帧出现。只是没有风暴那么严重，但对网络的性能还是会有影响。 缓解办法 大页 交换机动态buffer共享 RDMA的管理和测量 记录每个时间段的包数量，byte总数，PFC数量等用来监控集群状态。 建立一个RDMA的Pingmesh用来动态监测集群的延迟变化，发512byte的包到交换机和服务器来测。 延迟和带宽 在他们的数据中心上，总体的延迟状态对比，尾延迟大幅降低。 用一个三层的576台服务器来测试带宽，podset和spine之间的带宽能到到2.56Tb/s.而ToR之间贷款为3.0Tb/s这大概是总带宽的60%。（说是因为ECMP的哈希碰撞，不是很懂） 用一个两层的24台服务器来测试，维持每台服务器7Gb/s的带宽，测试集群中的延迟，发现集群的RDMA延迟从99th的50us，99.9th的80us上升到400us和800us。而这里tcp的99th延迟大概是500us。随着带宽增大，延迟会上升。 配置相关 其他主要是一些配置细节，效果很好，问题都避免了。 不是很懂。。。没有实感。 参考资料]]></summary></entry><entry><title type="html">内存二三</title><link href="https://lightlisten.cn/linux/2021/08/27/%E5%86%85%E5%AD%98%E4%BA%8C%E4%B8%89.html" rel="alternate" type="text/html" title="内存二三" /><published>2021-08-27T00:00:00+08:00</published><updated>2021-08-27T00:00:00+08:00</updated><id>https://lightlisten.cn/linux/2021/08/27/%E5%86%85%E5%AD%98%E4%BA%8C%E4%B8%89</id><content type="html" xml:base="https://lightlisten.cn/linux/2021/08/27/%E5%86%85%E5%AD%98%E4%BA%8C%E4%B8%89.html"><![CDATA[<h1 id="内存">内存</h1>
<h2 id="内存管理">内存管理</h2>
<p>关于匿名内存，</p>

<blockquote>
  <p><strong>Anonymous memory</strong> is a memory mapping with no file or device backing it. This is how programs allocate memory from the operating system for use  by things like the stack and heap.</p>
</blockquote>

<p>我们平时用的内存一般都是匿名的。（过去误以为未初始化才是，确实有点问题。。。）</p>

<p>如果是映射或者共用的（KSM），那才是其他类型。</p>

<h2 id="参考资料">参考资料</h2>
<p><a href="https://landley.net/writing/memory-faq.txt">some text</a></p>

<p>linux-4.11.0代码</p>

<p>《奔跑吧！linux内核》</p>

<p>《计算机体系结构》胡伟武</p>]]></content><author><name>pavilion</name></author><category term="linux" /><category term="memory" /><category term="swap" /><category term="cache" /><summary type="html"><![CDATA[内存 内存管理 关于匿名内存， Anonymous memory is a memory mapping with no file or device backing it. This is how programs allocate memory from the operating system for use by things like the stack and heap. 我们平时用的内存一般都是匿名的。（过去误以为未初始化才是，确实有点问题。。。） 如果是映射或者共用的（KSM），那才是其他类型。 参考资料 some text linux-4.11.0代码 《奔跑吧！linux内核》 《计算机体系结构》胡伟武]]></summary></entry><entry><title type="html">寻寻觅觅，或许未知</title><link href="https://lightlisten.cn/markdown/2021/08/18/try-and-play-!.html" rel="alternate" type="text/html" title="寻寻觅觅，或许未知" /><published>2021-08-18T00:00:00+08:00</published><updated>2021-08-18T00:00:00+08:00</updated><id>https://lightlisten.cn/markdown/2021/08/18/try-and-play-!</id><content type="html" xml:base="https://lightlisten.cn/markdown/2021/08/18/try-and-play-!.html"><![CDATA[<p>TRY AND PLAY!</p>]]></content><author><name>pavilion</name></author><category term="markdown" /><category term="log" /><summary type="html"><![CDATA[TRY AND PLAY!]]></summary></entry></feed>