<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://xcemaxx.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://xcemaxx.github.io/" rel="alternate" type="text/html" /><updated>2026-03-15T09:25:30+00:00</updated><id>https://xcemaxx.github.io/feed.xml</id><title type="html">Блог XCemaXX</title><subtitle>An amazing website.</subtitle><author><name>Andreev Semen</name></author><entry><title type="html">Конкурентность и атомики</title><link href="https://xcemaxx.github.io/books/concurrency/" rel="alternate" type="text/html" title="Конкурентность и атомики" /><published>2026-03-15T00:00:00+00:00</published><updated>2026-03-15T00:00:00+00:00</updated><id>https://xcemaxx.github.io/books/concurrency</id><content type="html" xml:base="https://xcemaxx.github.io/books/concurrency/"><![CDATA[<p>C++ Concurrency in action [2020] Anthony Wiliams</p>

<p><a href="https://mara.nl/atomics/">Rust Atomics and Locks</a> [2023] Mara Bos</p>

<p class="notice--info text-center">Средняя и хорошая книги об основах</p>

<figure style="display: flex; justify-content: center;">
  <img src="/assets/images/2026-03-15-concurrency.png" style="width: 100%; max-width: 600px;" alt="cache latencies" />
</figure>

<p>Поводом глубже погрузиться в тему конкурентности <a href="/notes/address-sanitizer">стал пойманный мной баг</a> в крейте <a href="https://crates.io/crates/thingbuf">thingbuf</a> (<a href="https://github.com/hawkw/thingbuf/issues/100">issue</a>). После этого я прикупил книгу <strong>C++ Concurrency in Action</strong>, и параллельно начал читать онлайн <strong>Rust Atomics and Locks</strong>.</p>

<p>Изначально хотел написать свою SCSP-очередь с переиспользованием слотов 🚲. Но после прочтения понял, что ну его. Лучше починить заведенный баг.</p>

<h1 id="memory-ordering">Memory ordering</h1>

<p>Я уже освещал тему <a href="/notes/atomics-lock-free/">атомиков</a>, но после чтения книг появилось, что добавить.</p>

<p>Внутри одного потока всегда сохраняется программный порядок (<em>sequenced-before</em>) независимо от <code class="language-plaintext highlighter-rouge">Ordering</code>. То есть в рамках одного потока операции ведут себя так, как будто выполняются строго последовательно (при отсутствии других потоков, изменяющих те же данные).</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">RELAXED</span><span class="p">:</span> <span class="n">AtomicI64</span> <span class="o">=</span> <span class="nn">AtomicI64</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="k">fn</span> <span class="nf">one_tread_only</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">let</span> <span class="n">t0</span> <span class="o">=</span> <span class="n">RELAXED</span><span class="nf">.load</span><span class="p">(</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span>
  <span class="n">RELAXED</span><span class="nf">.store</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span>
  <span class="k">let</span> <span class="n">t1</span> <span class="o">=</span> <span class="n">RELAXED</span><span class="nf">.load</span><span class="p">(</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span>
  <span class="nd">assert_eq!</span><span class="p">(</span><span class="n">t0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="nd">assert_eq!</span><span class="p">(</span><span class="n">t1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>При <code class="language-plaintext highlighter-rouge">Relaxed</code> операции разных потоков не связаны отношением <em>happens-before</em>. Однако для каждой атомарной переменной существует *лобальный modification order — единый порядок всех её записей, общий для всех потоков. Это означает, что если поток однажды прочитал значение атомарной переменной, он не может позже прочитать более старое значение этой же переменной. При этом разные потоки могут одновременно наблюдать разные значения.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">RELAXED</span><span class="p">:</span> <span class="n">AtomicI64</span> <span class="o">=</span> <span class="nn">AtomicI64</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="n">thread1</span><span class="p">:</span> <span class="p">{</span> <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..=</span><span class="mi">1_000_000</span> <span class="p">{</span> <span class="n">RELAXED</span><span class="nf">.store</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span> <span class="p">}</span> <span class="p">};</span>
<span class="n">thread2</span><span class="p">:</span> <span class="p">{</span>
  <span class="k">let</span> <span class="k">mut</span> <span class="n">last</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">while</span> <span class="n">last</span> <span class="o">!=</span><span class="mi">1_000_000</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">t</span> <span class="o">=</span> <span class="n">RELAXED</span><span class="nf">.load</span><span class="p">(</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span>
    <span class="k">if</span> <span class="n">t</span> <span class="o">&lt;</span> <span class="n">last</span> <span class="p">{</span>
        <span class="nd">panic!</span><span class="p">(</span><span class="s">"impossible: saw RELAXED go backwards: {} -&gt; {}"</span><span class="p">,</span> <span class="n">last</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">last</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span>
<span class="p">}}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Acquire</code>/<code class="language-plaintext highlighter-rouge">Release</code> позволяют установить отношение <em>happens-before</em> между потоками. Если поток 1 выполнил <code class="language-plaintext highlighter-rouge">N</code> произвольных записей в память, а затем записал значение в атомарную переменную с <code class="language-plaintext highlighter-rouge">Release</code>, то все предыдущие записи становятся видимыми другим потокам после синхронизации. Если поток 2 прочитает эту же атомарную переменную с <code class="language-plaintext highlighter-rouge">Acquire</code> и увидит запись, сделанную потоком 1, то между потоками возникает отношение <em>happens-before</em>. В этом случае поток 2 гарантированно увидит все <code class="language-plaintext highlighter-rouge">N</code> записей, сделанные потоком 1 до <code class="language-plaintext highlighter-rouge">Release</code>.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="k">mut</span> <span class="n">DATA</span><span class="p">:</span> <span class="nb">u64</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">thread1</span><span class="p">:</span> <span class="p">{</span>
    <span class="n">A</span><span class="nf">.store</span><span class="p">(</span><span class="n">SOME_VAL</span><span class="p">,</span> <span class="n">Relaxed</span><span class="p">);</span> <span class="c1">// можно и Release </span>
    <span class="k">unsafe</span> <span class="p">{</span> <span class="n">DATA</span> <span class="o">=</span> <span class="n">SOME_VAL2</span> <span class="p">};</span> <span class="c1">// Safety: thread2 не может читать DATA до установки B</span>
    <span class="n">B</span><span class="nf">.store</span><span class="p">(</span><span class="n">SOME_VAL</span><span class="p">,</span> <span class="n">Release</span><span class="p">);</span> 
<span class="p">}</span>
<span class="n">thread2</span><span class="p">:</span> <span class="p">{</span>
    <span class="k">while</span> <span class="n">B</span><span class="nf">.load</span><span class="p">(</span><span class="n">Acquire</span><span class="p">)</span> <span class="o">!=</span> <span class="n">SOME_VAL</span> <span class="p">{</span> <span class="p">}</span>
    <span class="k">let</span> <span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="nf">.load</span><span class="p">(</span><span class="n">Relaxed</span><span class="p">);</span> <span class="c1">// можно и Acquire</span>
    <span class="nd">assert!</span><span class="p">(</span><span class="n">a</span> <span class="o">==</span> <span class="n">SOME_VAL</span><span class="p">);</span> <span class="c1">// OK</span>
    <span class="nd">assert!</span><span class="p">(</span><span class="k">unsafe</span> <span class="p">{</span> <span class="n">DATA</span> <span class="p">}</span> <span class="o">==</span> <span class="n">SOME_VAL2</span><span class="p">);</span> <span class="c1">// из-за Acquire/Release, безопасно читать DATA</span>
<span class="p">}</span>
</code></pre></div></div>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">spawn</code> и <code class="language-plaintext highlighter-rouge">join</code> потоков создают отношение <em>happens-before</em>, аналогичное <code class="language-plaintext highlighter-rouge">Release/Acquire</code>, влияя на <code class="language-plaintext highlighter-rouge">Relaxed</code> операции над атомиками.</p>
</blockquote>

<p>На основе этого отношения можно написать примитивный мьютекс:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="k">mut</span> <span class="n">DATA</span><span class="p">:</span> <span class="nb">u64</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">static</span> <span class="n">LOCKED</span><span class="p">:</span> <span class="n">AtomicBool</span> <span class="o">=</span> <span class="nn">AtomicBool</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="k">false</span><span class="p">);</span>

<span class="k">fn</span> <span class="nf">f</span><span class="p">(</span><span class="n">new_val</span><span class="p">:</span> <span class="nb">u64</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="n">LOCKED</span><span class="nf">.swap</span><span class="p">(</span><span class="k">true</span><span class="p">,</span> <span class="n">Acquire</span><span class="p">)</span> <span class="o">==</span> <span class="k">false</span> <span class="p">{</span>
        <span class="c1">// Safety: мы владеем эксклюзивным доступом к DATA</span>
        <span class="k">unsafe</span> <span class="p">{</span> <span class="n">DATA</span> <span class="o">=</span> <span class="n">new_val</span> <span class="p">};</span>
        <span class="n">LOCKED</span><span class="nf">.store</span><span class="p">(</span><span class="k">false</span><span class="p">,</span> <span class="n">Release</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Ordering::SeqCst</code> это <code class="language-plaintext highlighter-rouge">Acquire/Release</code> плюс <strong>глобальный порядок операций</strong>. Если поток записал или прочитал значение атомарной переменной с <code class="language-plaintext highlighter-rouge">SeqCst</code>, другие потоки будут наблюдать эти операции в одном и том же глобальном порядке (при условии, что они тоже используют <code class="language-plaintext highlighter-rouge">SeqCst</code>).</p>

<p>На практике <code class="language-plaintext highlighter-rouge">SeqCst</code> для большинства алгоритмов не нужен. Если требуется строгая синхронизация, лучше использовать <code class="language-plaintext highlighter-rouge">Relaxed</code> операции и добавить явный <code class="language-plaintext highlighter-rouge">std::sync::atomic::fence(Ordering::SeqCst)</code>.</p>

<h1 id="атомики-в-ассемблере">Атомики в ассемблере</h1>
<p>В <a href="https://mara.nl/atomics/hardware.html">седьмой главе книги по Rust</a> разбирается ассемблер, стоящий за атомиками, и различия между инструкциями на ARM, RISC и x86. Это самая интересная глава всей книги, потому что она отражает, чем на самом деле являются memory ordering и атомики.</p>

<h2 id="x86">x86</h2>
<p>Если использовать <code class="language-plaintext highlighter-rouge">Relaxed</code> ordering с <code class="language-plaintext highlighter-rouge">load</code>/<code class="language-plaintext highlighter-rouge">store</code>, то ассемблер обычной операции не отличается от операции с атомиком: <code class="language-plaintext highlighter-rouge">mov dword ptr [rdi], 0</code>.</p>

<p>Для операций <strong>Read-Modify-Write</strong> (<code class="language-plaintext highlighter-rouge">add</code>, <code class="language-plaintext highlighter-rouge">sub</code>, <code class="language-plaintext highlighter-rouge">and</code>, <code class="language-plaintext highlighter-rouge">not</code>, <code class="language-plaintext highlighter-rouge">or</code>, <code class="language-plaintext highlighter-rouge">xor</code>) на x86 используется <code class="language-plaintext highlighter-rouge">lock</code>-префикс: <code class="language-plaintext highlighter-rouge">lock add dword ptr [rdi], 10</code>. Эта инструкция подходит, если не нужно знать значение переменной до изменения. Если старое значение требуется, используется <code class="language-plaintext highlighter-rouge">CAS</code>-цикл:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># x.fetch_or(10, Relaxed)
mov eax, dword ptr [rdi]
.L1:
  mov ecx, eax
  or ecx, 10
  lock cmpxchg dword ptr [rdi], ecx
  jne .L1
</code></pre></div></div>

<p>Есть и специализированные инструкции для некоторых операций, например, <code class="language-plaintext highlighter-rouge">xadd</code>, <code class="language-plaintext highlighter-rouge">bts</code>, <code class="language-plaintext highlighter-rouge">btr</code>, <code class="language-plaintext highlighter-rouge">btc</code>, которые можно использовать с <code class="language-plaintext highlighter-rouge">lock</code>-префиксом.</p>

<p>Вишенка на торте: на x86 <code class="language-plaintext highlighter-rouge">load</code> и <code class="language-plaintext highlighter-rouge">read-modify-write</code> операции генерируют одинаковый ассемблер как для <code class="language-plaintext highlighter-rouge">Relaxed</code>, так и для <code class="language-plaintext highlighter-rouge">SeqCst</code>.
<code class="language-plaintext highlighter-rouge">store</code> одинаков для <code class="language-plaintext highlighter-rouge">Relaxed</code> и <code class="language-plaintext highlighter-rouge">Release</code>, но отличается для <code class="language-plaintext highlighter-rouge">SeqCst</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># x.store(0, SeqCst)
xor eax, eax
xchg dword ptr [rdi], eax
</code></pre></div></div>

<p>Т.е. если пишешь код на x86 с <code class="language-plaintext highlighter-rouge">SeqCst</code> для <code class="language-plaintext highlighter-rouge">load</code> или модификаций, то он не будет отличаться от <code class="language-plaintext highlighter-rouge">Relaxed</code>. Поэтому при ревью кода можно быть чуть менее строгим к избыточному <code class="language-plaintext highlighter-rouge">SeqCst</code>. Делать так постоянно всё же не стоит, но знать стоимость ordering полезно.</p>

<p>Как следствие, <code class="language-plaintext highlighter-rouge">std::sync::atomic::fence</code> на x86 разворачивается в ничто во всех случаях, кроме <code class="language-plaintext highlighter-rouge">SeqCst</code>. В последнем случае появляется инструкция <code class="language-plaintext highlighter-rouge">mfence</code>.</p>

<h2 id="arm">ARM</h2>

<p>Если использовать <code class="language-plaintext highlighter-rouge">Relaxed</code> ordering с <code class="language-plaintext highlighter-rouge">load</code>/<code class="language-plaintext highlighter-rouge">store</code>, то обычные и атомарные операции также выглядят одинаково: <code class="language-plaintext highlighter-rouge">str wzr, [x0]</code>.</p>

<p>Для операций <code class="language-plaintext highlighter-rouge">Read-Modify-Write</code> ARM использует пару инструкций <strong>load-linked / store-conditional (LL/SC)</strong>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># x.fetch_add(10, Relaxed)
.L1:
  ldxr w8, [x0]
  add w9, w8, #10
  stxr w10, w9, [x0]   # не сохранит, если значение изменилось после ldxr
  cbnz w10, .L1        # stxr пишет 0 при успехе, иначе 1
</code></pre></div></div>

<p>Инструкция <code class="language-plaintext highlighter-rouge">stxr</code> может давать <strong>ложные срабатывания</strong>. Процессор, скорее всего, отслеживает изменения <strong>кэш-линий</strong>, а не отдельных атомарных переменных. Поэтому если изменилась другая часть той же кэш-линии, <code class="language-plaintext highlighter-rouge">stxr</code>  даст ложно-положительный результат.</p>

<p>Если нужно отменить отслеживание <code class="language-plaintext highlighter-rouge">ldxr</code>, компилятор добавляет инструкцию <code class="language-plaintext highlighter-rouge">clrex</code>.</p>

<p>В новых версиях ARM появились и специализированные атомарные инструкции (<code class="language-plaintext highlighter-rouge">fetch_add</code>, <code class="language-plaintext highlighter-rouge">fetch_max</code> и другие), которые работают быстрее.</p>

<p>На ARM <code class="language-plaintext highlighter-rouge">memory ordering</code> влияет на выбор инструкций гораздо сильнее, чем на x86. Различаются инструкции:</p>

<table>
  <thead>
    <tr>
      <th>Ordering</th>
      <th>Load</th>
      <th>Store</th>
      <th>Exclusive Load</th>
      <th>Exclusive Store</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Relaxed</td>
      <td><code class="language-plaintext highlighter-rouge">ldr</code></td>
      <td><code class="language-plaintext highlighter-rouge">str</code></td>
      <td><code class="language-plaintext highlighter-rouge">ldxr</code></td>
      <td><code class="language-plaintext highlighter-rouge">stxr</code></td>
      <td>load/store register, load/store exclusive register</td>
    </tr>
    <tr>
      <td>Acquire</td>
      <td><code class="language-plaintext highlighter-rouge">ldar</code></td>
      <td>—</td>
      <td><code class="language-plaintext highlighter-rouge">ldaxr</code></td>
      <td>—</td>
      <td>load-acquire register, load-acquire exclusive register</td>
    </tr>
    <tr>
      <td>Release</td>
      <td>—</td>
      <td><code class="language-plaintext highlighter-rouge">stlr</code></td>
      <td>—</td>
      <td><code class="language-plaintext highlighter-rouge">stlxr</code></td>
      <td>store-release register, store-release exclusive register</td>
    </tr>
  </tbody>
</table>

<p>Для <code class="language-plaintext highlighter-rouge">SeqCst</code> используются комбинации <code class="language-plaintext highlighter-rouge">Acquire</code>, <code class="language-plaintext highlighter-rouge">Release</code> и <code class="language-plaintext highlighter-rouge">AcqRel</code> в зависимости от операции. Именно поэтому на ARM важно различать <code class="language-plaintext highlighter-rouge">Relaxed</code> и более строгие ordering.</p>

<p>Как следствие, <code class="language-plaintext highlighter-rouge">std::sync::atomic::fence</code> на ARM генерирует реальные инструкции: <code class="language-plaintext highlighter-rouge">Acquire</code> → <code class="language-plaintext highlighter-rouge">dmb ishld</code>, остальные варианты → <code class="language-plaintext highlighter-rouge">dmb ish</code>.</p>

<h1 id="c-concurrency-in-action">C++ Concurrency in action</h1>
<p>Книга занимает около 640 страниц, но по факту примерно половина — это приложение с распечаткой <code class="language-plaintext highlighter-rouge">cppreference</code>. Реально полезного текста около 300 страниц.</p>

<p>Начинается все с классического введения в многопоточность C++. Разбираются потоки, разные виды мьютексов (<code class="language-plaintext highlighter-rouge">mutex</code>, <code class="language-plaintext highlighter-rouge">shared_mutex</code>, <code class="language-plaintext highlighter-rouge">recursive_mutex</code>), RAII-обёртки (<code class="language-plaintext highlighter-rouge">lock_guard</code>, <code class="language-plaintext highlighter-rouge">unique_lock</code>, <code class="language-plaintext highlighter-rouge">scoped_lock</code>), а также работа с несколькими мьютексами через <code class="language-plaintext highlighter-rouge">std::lock</code>. Есть и менее очевидные вещи, например, инициализация через <code class="language-plaintext highlighter-rouge">once_flag</code> и <code class="language-plaintext highlighter-rouge">call_once</code> как альтернатива статическим переменным.</p>

<p>Дальше подробно разбираются высокоуровневые механизмы синхронизации: условные переменные, <code class="language-plaintext highlighter-rouge">latch</code>, <code class="language-plaintext highlighter-rouge">barrier</code>, <code class="language-plaintext highlighter-rouge">future</code>-механизмы вместе с <code class="language-plaintext highlighter-rouge">std::promise</code> и <code class="language-plaintext highlighter-rouge">std::packaged_task</code>.</p>

<h2 id="сравнение-c-с-rust">Сравнение C++ с Rust</h2>
<p>В Rust нет прямых аналогов <code class="language-plaintext highlighter-rouge">std::future</code>, так как в C++ это, по сути, контейнер для передачи результата. В Rust используют каналы, <code class="language-plaintext highlighter-rouge">spawn_blocking</code> или полноценный <code class="language-plaintext highlighter-rouge">async/await</code> runtime. При этом сами Rust-фьючерсы — это ленивый state machine, которому требуется executor.</p>

<p>В C++ нормальный <code class="language-plaintext highlighter-rouge">then</code>-chaining и операции вроде <code class="language-plaintext highlighter-rouge">when_all</code> / <code class="language-plaintext highlighter-rouge">when_any</code> появятся только в C++26 вместе с <code class="language-plaintext highlighter-rouge">std::execution</code>. В Rust подобные вещи (<code class="language-plaintext highlighter-rouge">select</code>, <code class="language-plaintext highlighter-rouge">join_all</code>) уже давно есть в async-экосистеме.</p>

<p>Некоторые примитивы совпадают:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">std::latch</code> можно сравнить с <code class="language-plaintext highlighter-rouge">crossbeam::WaitGroup</code></li>
  <li><code class="language-plaintext highlighter-rouge">std::barrier</code> есть в стандартной библиотеке Rust (<code class="language-plaintext highlighter-rouge">std::sync::Barrier</code>)</li>
</ul>

<h2 id="интересности">Интересности</h2>

<p>Самый сильный раздел — разработка собственных concurrent-структур данных. Автор постепенно усложняет примеры: сначала блокирующие структуры (<code class="language-plaintext highlighter-rouge">stack</code>, <code class="language-plaintext highlighter-rouge">queue</code>, <code class="language-plaintext highlighter-rouge">hash_map</code>, <code class="language-plaintext highlighter-rouge">linked_list</code>) с увеличением числа мьютексов. Например, в списке мьютекс размещается прямо в каждой ноде.</p>

<p>Дальше книга переходит к lock-free структурам: стеку и очереди. Эта часть заметно сложнее: я её не осилил и прочитал только поверхностно. Для реализации RCU приводятся два подхода: hazard pointers и подсчёт ссылок. Выглядит это, как минималистичный сборщик мусора, где удаление элементов откладывается до момента, когда ни один поток больше не работает с ними.</p>

<p>Отдельно выделю два момента. Первый, распараллеливание рекурсивных алгоритмов: на каждом шаге половина задачи отправляется в пул потоков, а вторая половина продолжает выполняться в текущем потоке. Второй, активное использование <code class="language-plaintext highlighter-rouge">thread_local</code>. В своей практике я почти не применял поток-локальные переменные (разве что для генераторов случайных чисел), а в книге они часто используются для снижения конкуренции за общий ресурс.</p>

<p>В книге обсуждаются общие принципы разработки конкурентного кода:</p>
<ul>
  <li><strong>распределение работы</strong>: пулы воркеров (с механизмом work-stealing), организация пайплайнов, распределение работы в рекурсивных алгоритмах с ограничением общего числа потоков</li>
  <li><strong>кэш</strong>: если данные лежат слишком близко, можно получить false sharing на одной и той же линии. Если слишком далеко — ухудшается locality. Если хочется пощупать эти эффекты руками, рекомендую лабораторные <code class="language-plaintext highlighter-rouge">data_packing</code> и <code class="language-plaintext highlighter-rouge">false_sharing</code> из <a href="https://github.com/dendibakh/perf-ninja/tree/main/labs/memory_bound">курса perf-ninja</a></li>
  <li><strong>конкуренция за ресурсы</strong>: когда потоков много, стоит помнить, что кэш у ядра общий, и потоки на одном ядре будут его делить. Переключения контекста никуда не исчезают</li>
  <li><strong>безопасность исключений</strong>: для этого хорошо подходят <code class="language-plaintext highlighter-rouge">std::packaged_task</code> и <code class="language-plaintext highlighter-rouge">std::async</code>. Без них обеспечить корректную обработку исключений в собственном пуле потоков будет довольно сложно</li>
</ul>

<h1 id="rust-atomics-and-locks">Rust Atomics and Locks</h1>

<p>Книга хорошо объясняет фундаментальные механизмы синхронизации и постепенно показывает, как из атомиков строятся более сложные примитивы.</p>

<p>Иногда интересные детали встречаются в самых неожиданных местах. Пример скрытой синхронизации: в Rust <code class="language-plaintext highlighter-rouge">println!</code> всегда потокобезопасен: внутри используется <code class="language-plaintext highlighter-rouge">std::io::Stdout::lock()</code>. Отключить это нельзя. Если нужно писать в stdout без этого лока, остаётся путь через <code class="language-plaintext highlighter-rouge">libc::write</code>.</p>

<p>В C++ еще больше нюансов в стандартном вводе-выводе: для ускорения отключают синхронизацию через <code class="language-plaintext highlighter-rouge">sync_with_stdio(false)</code>, а также разрывают связь <code class="language-plaintext highlighter-rouge">cin</code> и <code class="language-plaintext highlighter-rouge">cout</code> с помощью <code class="language-plaintext highlighter-rouge">cin.tie(nullptr)</code>, чтобы ввод не флашил вывод автоматически. В Rust таких механизмов по умолчанию нет, поведение проще и прозрачнее.</p>

<p>В начале книги разбираются базовые способы передачи данных между потоками и использование стандартных примитивов синхронизации. Один из практических советов — избегать безымянных локов внутри выражений, потому что время жизни блокировки может оказаться больше, чем ожидается:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">v</span><span class="p">:</span> <span class="n">Mutex</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">i32</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nf">init</span><span class="p">();</span>
<span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="o">=</span> <span class="n">v</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.pop</span><span class="p">()</span> <span class="p">{</span>
 <span class="nf">process</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
<span class="p">}</span> <span class="c1">// lock dropped after the whole if</span>
<span class="k">if</span> <span class="n">v</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.pop</span><span class="p">()</span> <span class="o">==</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// lock dropped after condition</span>
  <span class="nf">call</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>В книге рассматривается механизм <code class="language-plaintext highlighter-rouge">thread::park</code> / <code class="language-plaintext highlighter-rouge">unpark</code>. Это «условная переменная для бедных»: он работает только между конкретными потоками и требует явного доступа к <code class="language-plaintext highlighter-rouge">thread_handle</code>.</p>

<p>Отдельный большой блок посвящён атомикам и <code class="language-plaintext highlighter-rouge">CAS</code>-циклам. Материал читается легко: сначала вводится базовый паттерн <code class="language-plaintext highlighter-rouge">compare-and-swap</code>, а такие вещи, как гонки, переупорядочивание операций и ABA-проблема, лишь обозначаются. Подробное обсуждение появляется позже. Такой подход позволяет не перегружать первые главы и держать фокус на основной идее.</p>

<p>Для <code class="language-plaintext highlighter-rouge">CAS</code>-loop в Rust есть удобная обёртка <code class="language-plaintext highlighter-rouge">fetch_update</code>: внутри она реализует тот же цикл сравнения и замены, а пользователю остаётся только передать функцию, описывающую переход из текущего значения в новое.</p>

<p>Дальше книга переходит к простым примитивам: <code class="language-plaintext highlighter-rouge">spinlock</code>, каналы и <code class="language-plaintext highlighter-rouge">Arc</code>. Про <code class="language-plaintext highlighter-rouge">Arc</code> я уже писал раньше, и <a href="https://github.com/XCemaXX/rust_study_path/blob/main/06_tiny_stuff/06_std_types/my_arc.rs">даже реализовывал</a>. Кажется, этот пример был в <a href="https://doc.rust-lang.org/nomicon/arc-mutex/arc.html">The Rustonomicon</a>. В книге он разобран подробнее и довольно интересный.</p>

<p>В финальных главах автор сравнивает примитивы синхронизации в разных операционных системах и показывает, как реализовать собственные <code class="language-plaintext highlighter-rouge">Mutex</code>, <code class="language-plaintext highlighter-rouge">Condition Variable</code> и <code class="language-plaintext highlighter-rouge">RwLock</code>. Я добавил эти реализации в свой <a href="https://github.com/XCemaXX/rust_study_path/tree/main/06_tiny_stuff/06_std_types/lock_primitives">учебный репозиторий</a>.</p>

<h1 id="bonus">Bonus</h1>
<p>В статье про атомики не упомянул, что есть отличная браузерная игра <a href="https://deadlockempire.github.io">The Deadlock Empire</a>, посвящённая конкурентности и типичным ошибкам многопоточности. В ней ты игрешь за злого планировщика потоков, который приводит программу к дедлокам, гонкам данных и другим сбоям.</p>

<p>Знаю еще пару игр: <a href="https://store.steampowered.com/app/375820/Human_Resource_Machine/">Human Resource Machine</a> и <a href="https://store.steampowered.com/app/792100/7_Billion_Humans/">7 Billion Humans</a>, в которых через задачки моделируются аспекты параллельного и офисного «многопоточного» труда, но сам до них я пока так и не добрался, только положил в Steam‑wishlist.</p>

<p>Дополнительно можно почитать статью на хабре <a href="https://habr.com/ru/articles/972856/">Введение в атомики. C++</a>.</p>]]></content><author><name>Andreev Semen</name></author><category term="Books" /><category term="book" /><summary type="html"><![CDATA[C++ Concurrency in action [2020] Anthony Wiliams]]></summary></entry><entry><title type="html">Вайбкодим: Dota2 Item Efficiency Analysis</title><link href="https://xcemaxx.github.io/notes/ai-era-programming/" rel="alternate" type="text/html" title="Вайбкодим: Dota2 Item Efficiency Analysis" /><published>2026-03-03T00:00:00+00:00</published><updated>2026-03-03T00:00:00+00:00</updated><id>https://xcemaxx.github.io/notes/ai-era-programming</id><content type="html" xml:base="https://xcemaxx.github.io/notes/ai-era-programming/"><![CDATA[<p>Весной 2020 года я решил сделать проект: посчитать эффективную стоимость предметов в Dota 2. Тогда всё было в ручном режиме — Excel, формулы и поиск данных по разным источникам.</p>

<p>В 2026 я вернулся к этой идее, но уже с AI-агентами. Оказалось, что такую задачу теперь можно реализовать на новом уровне.</p>

<p>Это статья скорее про опыт работы с AI, чем про Dota: что стало проще, где всё ещё сложно и как правильно организовать процесс.</p>

<h1 id="проект-dota2-item-efficiency-analysis">Проект Dota2 Item Efficiency Analysis</h1>
<figure>
  <img src="/assets/images/2026-03-03-dota-pathes-comp.png" style="width: 100%; max-width: 900px;" alt="dota-comparison" />
</figure>
<h2 id="версия-01--excel">Версия 0.1 — Excel</h2>
<p>В 2020 году, на патче 7.25, я вручную вбивал цифры в Excel. Идея была простой: вывести «эффективную стоимость» предмета через набор геймдизайнерских аксиом. С базовыми статами всё считалось относительно легко. Дальше начинались ауры, пассивки, активные способности — и сложность оценки росла.</p>

<p>На реализацию ушёл месяц или два. В рамках одного патча PoC работал хорошо и давал осмысленные результаты. Проблемы начинались с выходом следующего обновления. Каждый патч означал ручную проверку изменений, ввод новых значений и правку формул. Excel отлично подошёл для прототипа, но масштабировать такую систему невозможно.</p>

<p>К тому же xls-файл сложно распространять и поддерживать. GitHub Pages тогда я не использовал. В итоге проект отправился на полку.</p>

<p>Результат работы показан на рисунке слева. Справа уже версия со второго подхода, о которой ниже.</p>

<h2 id="версия-10--автоматизация-с-ai">Версия 1.0 — автоматизация с AI</h2>
<p>В 2026 году я решил вернуться к проекту <a href="https://xcemaxx.github.io/dota2-truecost/">проектом Dota2 Item Efficiency Analysis</a>.<br />
На этот раз цель была другой: максимальная автоматизация. Никакого ручного ввода цифр. Только парсинг файлов игры и один раз сформулированные правила расчёта.</p>

<p>Пайплайн получился прямолинейным:</p>
<ul>
  <li>Парсим items.txt из файлов игры → получаем items.json.</li>
  <li>Применяем правила из YAML файла → считаем эффективные стоимости → новый JSON.</li>
  <li>Генерируем статические веб-страницы по патчу: <a href="https://xcemaxx.github.io/dota2-truecost/patch_7.40c/items_table.html">таблицу</a> и <a href="https://xcemaxx.github.io/dota2-truecost/patch_7.40c/interactive_chart.html">график</a></li>
  <li>Строим страницу <a href="https://xcemaxx.github.io/dota2-truecost/comparison.html">сравнение патчей между собой</a></li>
</ul>

<p>Код, структуру yaml файла и все-все-все я отдал на откуп AI-агента.</p>

<p><a href="https://github.com/XCemaXX/dota2-truecost">Код проекта на github</a>. На него тоже ушел месяц.</p>

<h2 id="контекст--главная-проблема">Контекст — главная проблема</h2>
<p>Данные из старого Excel-файла по патчу 7.25 стали отправной точкой. На их основе AI смог сформировать первый черновик аксиом, даже без дополнительных источников. Но дальше всё шло со скрипом.</p>

<p>Проблема была в том, что items.txt содержит только численные данные. Агент не понимал игрового мира: что такое аура, как работает пассивка, что стакается, а что нет. Он видел лишь числа и короткие названия.</p>

<p>Чтобы дать ему контекст, я попросил написать crawler на Python, который обошёл страницы Liquipedia по всем предметам и собрал структурированный JSON с их описаниями. В последующих инструкциях добавлял: «При работе используй файл liquipedia.json для выявления игровых механик».</p>

<p>Эта доработка сработала отлично. Агент начал предлагать осмысленные варианты расчёта стоимостей, улучшать аксиомы стало проще. AI при наличии структурированного контекста он работает значительно лучше.</p>

<h2 id="как-я-организовал-работу-агентов">Как я организовал работу агентов</h2>
<p>Я не писал код, а выстраивал процесс. Основная задача была в том, чтобы проверить корректность геймдизайнерских аксиом.</p>

<p>Цикл работы над проектом и реализацией фич выглядел так: сначала я формировал вводные, потом агент составлял план проекта, выполнял его ревью, реализовывал задачи, проверял результат и делал ревью реализации. Все эти шаги выполнял агент, включая ревью.</p>

<p>Больше всего времени ушло на валидацию правил расчёта стоимостей — предметов больше 150. В диалоге с ИИ я последовательно проходил по каждому предмету, оценивал предложения и корректировал аксиомы.</p>

<p>В этом проекте мой фокус полностью сместился с кода на предметную область.</p>

<h1 id="практические-выводы">Практические выводы</h1>
<p>При работе с AI-агентами я выделил для себя несколько принципов.</p>
<ol>
  <li>Цели должны быть конкретными (лучше по SMART). Чтобы получить результат, важно точно понимать, чего хочешь. Если не знаешь, как достичь цели, AI может построить детальный план — главное, чётко формулировать задачу.</li>
  <li>Декомпозиция — ключевой навык. Разделяя работу на этапы, проще контролировать процесс и выявлять ошибки.</li>
  <li>Циклом обратной связи: исправь → проверь, позволяет пустить агента в долгое автономное плаванье, что ускоряет прогресс и улучшает качество результатов.</li>
  <li>Контекст нужно контролировать. План сначала формируется в отдельном файле, затем исполняется с чистым контекстом. Ревью проводится аналогично. Главного агента лучше использовать как project manager, распределяя задачи между вспомогательными агентами.</li>
  <li>Skills имеют большой потенциал для повторяющихся задач, например, для организации процесса по предыдущему пункту. Я применял их минимально — для поэтапного анализа групп предметов.</li>
</ol>

<h1 id="улучшения-организации-работы">Улучшения организации работы</h1>
<p>Проект показал, что AI уверенно создаёт PoC с нуля и работает внутри существующей архитектуры. Но есть промежуточный этап, который я пока не доработал — переработка архитектуры после PoC. Любой PoC содержит временные решения, костыли и куски, выросшие из экспериментов.</p>

<p>Идея, которую хочу попробовать: после успешного PoC разобрать архитектуру как чужой проект, найти слабые места и переписать его заново. Вторая версия не должна наступить на грабли PoC.</p>

<p>Я также понял, что проекту не хватает лёгкой, но осмысленной бюрократии. Даже для PoC полезно составить ТЗ с ограничениями, целями и принципами. Этот документ стоит держать прямо в репозитории и обновлять по мере развития проекта. Это создаёт точку отсчёта, к которой можно всегда обратиться.</p>

<p>Хочу добавить простую систему задач — мини-Jira внутри проекта. Папка с todo и done, где в done остаются короткие записи о том, что сделано и почему выбран именно такой подход. Это важно ради контекста: при рефакторинге легче понять, какие части ценны, а какие устарели. История решений снижает хаос.</p>

<p>Одним словом, к AI-проекту стоит подходить как к корпоративному проекту: анализ, планирование, ТЗ, история решений, тестирование, добавление новых функций. Разница только в том, что теперь всё живёт в одной папке рядом с кодом. Корпорации используют такой подход не из любви к процессам, а потому что он работает.</p>

<h1 id="планы-по-обучению-работе-с-ai">Планы по обучению работе с AI</h1>
<p>Похоже, мы вошли в эру лёгкой реализации любой программной идеи. Раньше мысли часто оставались в голове. Сейчас довести идею до PoC — вопрос не месяцев, а недель, а иногда и дней. Идеи можно фиксировать списком и проверять на практике. Вероятность того, что одна из них окажется бриллиантом, растёт. Не потому что идей стало больше, а потому что снизился порог входа в реализацию.</p>

<p>Проект по Dota в основном был про работу с данными и формализацию предметной области. Следующий шаг хочется сделать более программистским — построить процесс вокруг TDD. Агенты умеют писать тесты быстро, но действуют по-человечески: сначала код, потом тесты. Интересно проверить хваленый рост качества разработки с TDD, когда стоимость написания тестов для AI практически нулевая. Этот подход может раскрыться с новой силой.</p>

<p>На примете есть ремастеры пары университетских проектов. Сейчас они лежат как отчёты по курсовым и лабораторным. Хочется переписать их и выложить через GitHub Pages, чтобы они сперестали быть архивными файлами.</p>

<h1 id="отношении-к-ии">Отношении к ИИ</h1>
<p>Я искренне восхищён мощностью современных инструментов. AI всё проникает в рабочие процессы, документация пишется быстрее, прототипы появляются  мгновенно, технический долг исчезает. Но изменения не только в программировании. Видео-генерация, графика — всё это впечатляет. Теперь идеи не обязаны умирать в голове. Их можно быстро довести до рабочего состояния.</p>

<p>Лучше всего мое отношение к ИИ описывает разворот из энциклопедии Профессора Фортрана.</p>
<figure>
  <img src="/assets/images/2026-03-03-fortran-ai.jpg" style="width: 100%; max-width: 900px;" alt="fortran" />
</figure>
<p>Еще на ум приходит роман “Луна жестко стелет” Роберта Хайнлайна, в котором главный герой - лучший промт-инженер всея луны.</p>]]></content><author><name>Andreev Semen</name></author><category term="Notes" /><category term="ai" /><summary type="html"><![CDATA[Весной 2020 года я решил сделать проект: посчитать эффективную стоимость предметов в Dota 2. Тогда всё было в ручном режиме — Excel, формулы и поиск данных по разным источникам.]]></summary></entry><entry><title type="html">Making our own executable packer</title><link href="https://xcemaxx.github.io/article/exec-packer/" rel="alternate" type="text/html" title="Making our own executable packer" /><published>2026-01-10T00:00:00+00:00</published><updated>2026-01-10T00:00:00+00:00</updated><id>https://xcemaxx.github.io/article/exec-packer</id><content type="html" xml:base="https://xcemaxx.github.io/article/exec-packer/"><![CDATA[<p>Все последние посты были про книги - этот будет про практику.</p>

<p>Невероятный гайд <a href="https://fasterthanli.me/series/making-our-own-executable-packer">Making our own executable packer</a> от Amos Wenger. Про то, как написать свой загрузчик и упаковщих ELF-файлов на Rust. По сути, наивная версия <a href="https://upx.github.io">UPX</a>. Идеальный повод погрузиться во внутренности Linux на  Rust.</p>
<blockquote>
  <p>Сайт частенько был недоступен, поэтому я пользовался <a href="https://web.archive.org/web/20250915070946/https://fasterthanli.me/series/making-our-own-executable-packer">https://web.archive.org</a></p>
</blockquote>

<p>По ходу гайда затрагивается много интересного из мира Linux:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">mmap</code> файлов на Rust</li>
  <li>сборка бинарей:
    <ul>
      <li>ассемблер + линковка: <code class="language-plaintext highlighter-rouge">nasm</code>, <code class="language-plaintext highlighter-rouge">ld</code></li>
      <li>C без libc: <code class="language-plaintext highlighter-rouge">syscall</code> на asm, <code class="language-plaintext highlighter-rouge">gcc -nostartfiles -nodefaultlibs</code></li>
      <li>Rust без libc: приседаем с <code class="language-plaintext highlighter-rouge">no_std</code>, <code class="language-plaintext highlighter-rouge">build.rs</code> и <code class="language-plaintext highlighter-rouge">build-std</code></li>
      <li>база по Makefile</li>
    </ul>
  </li>
  <li>утилиты: <code class="language-plaintext highlighter-rouge">objdump</code>, <code class="language-plaintext highlighter-rouge">readelf</code>, <code class="language-plaintext highlighter-rouge">strace</code>, <code class="language-plaintext highlighter-rouge">dd</code>, <code class="language-plaintext highlighter-rouge">nm</code>, <code class="language-plaintext highlighter-rouge">ugdb</code>, <code class="language-plaintext highlighter-rouge">gdb</code></li>
  <li>gdb-скрипты на Python + Rust toolkit для анализа mmap текущего pid - такого я еще не видел</li>
  <li>устройство релокаций в ELF и <code class="language-plaintext highlighter-rouge">.o</code></li>
  <li>разница между <code class="language-plaintext highlighter-rouge">fPIC</code> и <code class="language-plaintext highlighter-rouge">non-fPIC</code></li>
  <li>краткая история libc</li>
</ul>

<p>Будет много <code class="language-plaintext highlighter-rouge">unsafe</code>-кода: работа с указателями, загрузка сегментов, ручная инициализация окружения. За собой заметил, что <code class="language-plaintext highlighter-rouge">unsafe</code>-блоки работают как задумано: каждый <code class="language-plaintext highlighter-rouge">unsafe</code> заставляет остановиться и подумать, а точно ли всё правильно. В C/C++ указатели для меня - обычный инструмент, о котором не задумываешься при использовании.</p>

<p>Для продакшена такой unsafe вряд ли будет полезен (если не писать драйверы или bare metal). Но как учебный опыт, почему бы и нет.</p>

<h1 id="долгая-дорога">Долгая дорога</h1>
<p>Легкой прогулкой это точно не назвать. Сам гайд писался два года. У меня повторение заняло около полутора месяцев.
Результат выложил в <a href="https://github.com/XCemaXX/rust_study_path/commit/ee985d3322bdb9a72a5f84ee2a292907f251d2e8">свой «обучательный» репозиторий на GitHub</a>. Идея выкладывать появилась не сразу, поэтому первый коммит начинается сразу со <a href="https://github.com/XCemaXX/rust_study_path/commit/97c4443704e9038110023bc2ba3064c74074ae62">stage3 гайда</a>.</p>

<p>Обычно я люблю гайды на C++ или Python, чтобы самому продумать портирование на Rust, как было с <a href="/notes/ray-tracer-v3/">Ray Tracer</a>. Здесь формат «сразу на Rust» зашёл. Задач для самостоятельного разбора хватало.</p>

<h1 id="решаем-проблемы">Решаем проблемы</h1>
<p>Гайд написан шесть лет назад. Крейты старые, libc старая. Я же запускал всё в 2025 году — WSL, Ubuntu 22/24, ядро 6.x, glibc 2.35–2.39.</p>

<p>Уже в первой части пришлось переписывать код: <code class="language-plaintext highlighter-rouge">nom v5.1</code> сильно отличается от <code class="language-plaintext highlighter-rouge">nom v8</code>. Но это цветочки, ягодки появились на 14 части туториала.</p>

<h2 id="ода-ии-ассистенту">Ода ИИ-ассистенту</h2>
<p>У автора в финале stage14 запускались <code class="language-plaintext highlighter-rouge">ls</code> и <code class="language-plaintext highlighter-rouge">nano</code>. У меня был стабильный segfault. Сравнивая обычный запуск и запуск через мой загрузчик, я понял, что не инициализирован <code class="language-plaintext highlighter-rouge">_rtld_global</code>. Временно прописал адрес вручную в регистр через <code class="language-plaintext highlighter-rouge">gdb</code> — дошёл до исполнения <code class="language-plaintext highlighter-rouge">setlocale</code>, где всё снова упало.
Дальше быстро продвинуться не вышло, и я отдал задачу ИИ-ассистенту (Cursor). Скормил ему пару последних частей туториала, код проекта, логи <code class="language-plaintext highlighter-rouge">gdb</code>. Попросил запустить <code class="language-plaintext highlighter-rouge">nano</code>.</p>

<p>Ассистент работал на Ubuntu 24, где всплыли ещё два типа релокаций и новых тегов (я вел разработку на Ubuntu 22). Это он поправил быстро. Потом упёрся в ту же проблему с <code class="language-plaintext highlighter-rouge">_rtld_global</code>.</p>

<p>С помощью серии вызовов <code class="language-plaintext highlighter-rouge">gdb, nm, objdump, readelf</code> и модификации кода проекта он разобрался, как хаками можно инициализировать <code class="language-plaintext highlighter-rouge">_rtld_global</code> и обойти эту проблему. Ассистент не может вызывать gdb в интерактивном режиме, поэтому он забавно делал это офлайн:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gdb <span class="nt">-q</span> <span class="nt">--batch</span> <span class="nt">-ex</span> <span class="s1">'set pagination off'</span> <span class="nt">-ex</span> <span class="s1">'set debuginfod enabled off'</span> <span class="nt">-ex</span> <span class="s1">'b "elk::process::jmp"'</span>
<span class="nt">-ex</span> <span class="s1">'run'</span> <span class="nt">-ex</span> <span class="s1">'autosym'</span> <span class="nt">-ex</span> <span class="s1">'p/x  $r14'</span> <span class="nt">-ex</span> <span class="s1">'set $r14 = &amp;_rtld_global'</span> <span class="nt">-ex</span> <span class="s1">'c'</span> <span class="nt">-ex</span> <span class="s1">'p/x $rdi'</span> 
<span class="nt">-ex</span> <span class="s1">'x/s $rdi'</span> <span class="nt">-ex</span> <span class="s1">'bt'</span> <span class="nt">--args</span> ./target/debug/elk run /bin/ls <span class="nt">--</span> <span class="nt">--help</span>
</code></pre></div></div>
<p>Проблема с <code class="language-plaintext highlighter-rouge">setlocale</code> оказалась сложнее. После пары часов экспериментов ассистент предложил замокать <code class="language-plaintext highlighter-rouge">setlocale</code> и <code class="language-plaintext highlighter-rouge">__ctype_*_loc</code>, чтобы вернуть валидные указатели на таблицы в памяти.</p>

<p>Ассистент проверял свой код на запуске <code class="language-plaintext highlighter-rouge">nano</code>, он падал. Я в какой-то момент, решил запустить <code class="language-plaintext highlighter-rouge">nano --help</code>, и segfault’a не было. Тоже самое произошло чуть позже с <code class="language-plaintext highlighter-rouge">ls --help</code>. А когда были замоканы нормально локали, то уже заработал и просто <code class="language-plaintext highlighter-rouge">ls</code>.</p>

<p>После 3 часов такой работы я ещё час потратил на вычищение мусора, чтобы прийти к минимальному набору моков и хаков. Итог - я доволен, <code class="language-plaintext highlighter-rouge">ls</code> работает, <code class="language-plaintext highlighter-rouge">nano --help</code> работает. Полноценный nano не работает, как у автора, но по заверению ИИ дальше нужен другой подход, за рамками этого гайда.</p>

<p>ИИ - крутой инструмент. Позволяет в трудных ситуациях прорваться и решить проблему быстрее. Да, он не идеально все сделает, но доработку напильником можно проделать самому, главное сделать PoC. В PoCах ассистенты хороши.</p>

<p>За ИИ стоит следить: проверять команды, иногда править его gdb-скрипты, читать логи. Если хочешь научиться, а не просто «чтобы работало», нужно следить за ходом его мыслей, обоснованием вызова команд и изменений кода. За собой заметил, что после двух часов работы в таком режиме, я пустил дело на самотёк - перестал внимательно следить за действиями ассистента, просто нажимал далее. Т.е. когнитивная нагрузка высокая, и не получится 8 часов так работать. Нужно давать себе перерыв.</p>

<p>Главный вывод: ключевой навык программиста - декомпозиция. При хорошей декомпозиции и изолированности задач ИИ-ассистент дает плоды. Да, в больших кодовых базах могут быть проблемы из-за ограничения контекста. Но и у человека контекст не безграничен, и он также будет плавать.</p>

<h1 id="итог">Итог</h1>
<p>Результат гайда <a href="https://github.com/XCemaXX/rust_study_path/commit/ee985d3322bdb9a72a5f84ee2a292907f251d2e8">опубликовал на GitHub</a>.
Самые интересные места:</p>
<ul>
  <li><a href="https://github.com/XCemaXX/rust_study_path/commit/9541b8a6b10d4ca43aadc5ae2532b48c5675997a">исправление для Ubuntu 22</a></li>
  <li><a href="https://github.com/XCemaXX/rust_study_path/commit/fb07eb7e094923e38725372eced509c2c092d470">исправление для Ubuntu 24</a></li>
  <li><a href="https://github.com/XCemaXX/rust_study_path/commit/4b3b35d4cda43a407a35a2a9752ed8297d941732">исправление финального упаковщика</a></li>
</ul>

<h1 id="bonus">Bonus</h1>

<p>В ноябре поучаствовал в тренировках Яндекса: <a href="https://www.youtube.com/live/YHCE8WqJubs">Тренировки. Забег по алгоритмам (8.0)</a>.<br />
В этот раз ребята немного поленились и не стали записывать новые лекции. На каждую домашку дали по две темы из прошлых тренировок с ссылками на эти уроки. Это нисколько не делает тренировки хуже, я ими доволен.</p>

<p>Решил 36 из 40 задач - мой лучший результат. Одной задачи не хватило до топ-300, которых зовут на собес по упрощённому треку. Может, в следующий раз я попаду туда 😂</p>

<figure>
  <img src="/assets/images/2026-01-10-yandex-8.0.png" style="width: 100%; max-width: 900px;" alt="cert yandex 8.0" />
</figure>]]></content><author><name>Andreev Semen</name></author><category term="Article" /><category term="programming" /><category term="rust" /><category term="study" /><category term="linux" /><category term="ai" /><summary type="html"><![CDATA[Все последние посты были про книги - этот будет про практику.]]></summary></entry><entry><title type="html">Запуск Rust приложения с ASan</title><link href="https://xcemaxx.github.io/notes/address-sanitizer/" rel="alternate" type="text/html" title="Запуск Rust приложения с ASan" /><published>2025-12-14T00:00:00+00:00</published><updated>2025-12-14T00:00:00+00:00</updated><id>https://xcemaxx.github.io/notes/address-sanitizer</id><content type="html" xml:base="https://xcemaxx.github.io/notes/address-sanitizer/"><![CDATA[<p>Непросто писать многопоточный код. Точнее, непросто правильно пользоваться примитивами синхронизации.</p>

<p>Недавно поймал баг в крейте <a href="https://crates.io/crates/thingbuf">thingbuf</a> и завел <a href="https://github.com/hawkw/thingbuf/issues/100">Issue</a>. Это показывает, что open source не волшебство: разработчики такие же люди и тоже ошибаются.</p>

<p>В разборе проблемы сильно помог санитайзер. Чтобы запустить его в Rust, нужно знать как это делать. Оставлю на память список команд.</p>

<p>Замечу, под санитайзером тесты не запускаются. Они используют макросы, которые с ASan не работают. Поэтому я либо запускаю examples, либо создаю отдельный тестовый крейт для воспроизведения бага.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Понадобится nightly Rust</span>
rustup toolchain <span class="nb">install </span>nightly
rustc +nightly <span class="nt">--version</span>
rustup override <span class="nb">set </span>nightly
rustup override <span class="nb">unset</span> <span class="c"># отключить для дальнейшей разработки</span>

<span class="c"># ASan</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>clang llvm libclang-rt-dev

<span class="nv">RUSTFLAGS</span><span class="o">=</span><span class="s2">"-Zsanitizer=address -Clink-arg=-fno-omit-frame-pointer </span><span class="se">\</span><span class="s2">
  -Cforce-frame-pointers=yes -Cdebuginfo=2"</span> cargo run <span class="nt">--target</span> x86_64-unknown-linux-gnu

<span class="c"># Найти бинарник example</span>
<span class="nv">RUSTFLAGS</span><span class="o">=</span><span class="s2">"-Zsanitizer=address -Clink-arg=-fno-omit-frame-pointer </span><span class="se">\</span><span class="s2">
  -Cforce-frame-pointers=yes -Cdebuginfo=2"</span> cargo <span class="nb">test</span> <span class="nt">--no-run</span> <span class="nt">--message-format</span><span class="o">=</span>json <span class="se">\</span>
  <span class="nt">--target</span> x86_64-unknown-linux-gnu <span class="o">&gt;</span> example_bin.json

<span class="c"># ASAN_OPTIONS — без пробелов</span>
<span class="nv">ASAN_OPTIONS</span><span class="o">=</span><span class="s2">"detect_leaks=1:fast_unwind_on_malloc=0:alloc_dealloc_mismatch=0 </span><span class="se">\</span><span class="s2">
  :detect_stack_use_after_return=1:halt_on_error=1:abort_on_error=1:strict_init_order=1 </span><span class="se">\</span><span class="s2">
  :symbolize=1:verbosity=2"</span> <span class="s2">"./target/x86_64-unknown-linux-gnu/debug/deps/test_proj-256ea6bc7957389e"</span>

<span class="c"># Запуск под gdb аналогичен</span>
gdb <span class="nt">--args</span> <span class="nb">env </span><span class="nv">ASAN_OPTIONS</span><span class="o">=</span><span class="s2">"..."</span> <span class="s2">"./target/x86_64-unknown-linux-gnu/debug/deps/test_proj-256ea6bc7957389e"</span>
</code></pre></div></div>

<p>Прикупил себе книжку C++ Concurrency in action [2019] Энтони Уильямс. Изучу вместе с другой <a href="https://marabos.nl/atomics/">Rust Atomics and Locks [2023] Мара Бос</a>.</p>

<p>Стану мастером многопотока 😎</p>

<p>Напишу свою SCSP очередь с переиспользованием слотов 🚲</p>]]></content><author><name>Andreev Semen</name></author><category term="Notes" /><category term="utilities" /><category term="rust" /><summary type="html"><![CDATA[Непросто писать многопоточный код. Точнее, непросто правильно пользоваться примитивами синхронизации.]]></summary></entry><entry><title type="html">Effective Rust. David Drysdale</title><link href="https://xcemaxx.github.io/books/effective-rust/" rel="alternate" type="text/html" title="Effective Rust. David Drysdale" /><published>2025-12-07T00:00:00+00:00</published><updated>2025-12-07T00:00:00+00:00</updated><id>https://xcemaxx.github.io/books/effective-rust</id><content type="html" xml:base="https://xcemaxx.github.io/books/effective-rust/"><![CDATA[<p><a href="https://www.lurklurk.org/effective-rust">Effective Rust. David Drysdale</a></p>

<p class="notice--info text-center">Хорошая книга для закрепления базы</p>

<p>Эту книгу проглотил, если сравнивать с предыдущей <a href="/books/algo-kormen/">про алгоритмы</a>: пара недель вместо пары месяцев.</p>

<p>Две главы 3.1 и 3.2 посвящены lifetime и borrow-checker. Кажется, что каждая книга по Rust считает своим долгом объяснить эту тему заново: мол, сложно, уникально, никто не понимает. Я не согласен. У меня в обычном коде проблем уже не возникает. Сложности начинаются, когда появляется <code class="language-plaintext highlighter-rouge">unsafe</code>, указатели и желание передать такие структуры в асинхронный рантайм.</p>

<p>В книге есть много отсылок к C++, и это оправдано. Я соглашусь, что объяснять Rust разработчику C++ проще. Возвращаясь к lifetime, в C++ только концепции <code class="language-plaintext highlighter-rouge">lvalue/rvalue</code> сложнее, чем вся тема lifetime.</p>

<h2 id="повторение---мать-учения">Повторение - мать учения</h2>

<p>Хороший набор базовых вещей, которые полезно вспомнить:</p>
<ul>
  <li>использовать типы для отражения семантики;</li>
  <li>паттерны NewType, Builder;</li>
  <li>различия между трейтами <code class="language-plaintext highlighter-rouge">AsRef</code>, <code class="language-plaintext highlighter-rouge">Borrow</code>, <code class="language-plaintext highlighter-rouge">ToOwned</code>;</li>
  <li>итераторы и их множество методов: концепция с одним <code class="language-plaintext highlighter-rouge">next</code>, с автоматической реализацией всех остальных интерфейсных методов, меня восхищает;</li>
  <li><code class="language-plaintext highlighter-rouge">Drop</code> = RAII</li>
  <li>иерархия трейтов: <code class="language-plaintext highlighter-rouge">FnOnce</code> &gt; <code class="language-plaintext highlighter-rouge">Fnmut</code> &gt; <code class="language-plaintext highlighter-rouge">Fn</code>.</li>
  <li>преждеврменная оптимизация - зло;</li>
  <li>рассудительное использование макросов (декларативные и процедурные). Мне их почти не приходится писать, мало опыта с ними. Наверное, к лучшему;</li>
  <li>главное отличие <code class="language-plaintext highlighter-rouge">core::</code> от <code class="language-plaintext highlighter-rouge">std::</code> - отсутствие аллокатора <code class="language-plaintext highlighter-rouge">alloc::</code></li>
</ul>

<h2 id="новое">Новое:</h2>
<ul>
  <li>Рефлексию в Rust лучше избегать, но если уж нужно: <code class="language-plaintext highlighter-rouge">std::any::{type_name, Any::type_id, TypeId}</code>. Я не фанат рефлексии, но знать полезно.</li>
  <li>Имена фич делят namespace с зависимыми крейтами: крейт может стать фичей, если объявить <code class="language-plaintext highlighter-rouge">optional = true</code>.</li>
  <li>Если интерфейс крейта использует типы из зависимых кретов, то стоит реэкспортировать либо крейт целиком, либо типы. По дизайну API лучше почитать отдельный <a href="https://rust-lang.github.io/api-guidelines/about.html">Rust API Guidelines</a>.</li>
</ul>

<h3 id="полезные-крейты">Полезные крейты:</h3>
<ul>
  <li>Для ошибок есть два отличных крейта: <a href="https://docs.rs/thiserror/latest/thiserror/">this_error</a> - если нужно потом их матчить, подходит для библиотек; <a href="https://docs.rs/anyhow/latest/anyhow/">anyhow</a>/<a href="https://docs.rs/eyre/latest/eyre/">eyre</a> - если планиурется просто их отображать, подходит для приложений.</li>
  <li>Крейт <a href="https://docs.rs/ouroboros/latest/ouroboros/">ouroboros</a> для self-referencing структур.</li>
  <li>Документация: <code class="language-plaintext highlighter-rouge">cargo doc --no-deps --open</code>, <code class="language-plaintext highlighter-rouge">broken_intra_doc_links</code>, <code class="language-plaintext highlighter-rouge">#![warn(missing_docs)]</code></li>
  <li>Управление деревом зависимостей: <a href="https://docs.rs/crate/cargo-udeps/latest">cargo-udeps</a>, <a href="https://docs.rs/crate/cargo-deny/latest">deny</a>, <a href="https://doc.rust-lang.org/cargo/commands/cargo-tree.html">cargo tree</a>, <a href="https://docs.rs/crate/cargo-machete/latest">cargo-machete</a></li>
  <li>FFI: <a href="https://rust-lang.github.io/rust-bindgen/">bingen</a> и <a href="https://cxx.rs">cxx</a>.</li>
</ul>

<h3 id="1000-и-1-книга-по-rust">1000 и 1 книга по Rust</h3>
<p>В тексте много ссылок на другие Rust-книги, что навело меня на мысль сделать отдельный <a href="/notes/list-rust-books/">пост со списком всех Rust book, которые я встречал</a>. А себе отметил две для дальнейшего чтения:</p>
<ul>
  <li><a href="https://abseil.io/resources/swe-book">Software Engineering at Google</a></li>
  <li><a href="https://marabos.nl/atomics/">Rust Atomics and Locks</a></li>
</ul>

<h2 id="бонус">Бонус</h2>
<p>В тему отлично ложится статья <a href="https://habr.com/ru/companies/beget/articles/967076/">Закрепи меня покрепче: Pin, самоссылки и почему всё падает</a>, где советуют крейт <a href="https://docs.rs/pin-project/latest/pin_project/">pin_project</a>.</p>]]></content><author><name>Andreev Semen</name></author><category term="Books" /><category term="book" /><category term="rust" /><summary type="html"><![CDATA[Effective Rust. David Drysdale]]></summary></entry><entry><title type="html">List Rust books</title><link href="https://xcemaxx.github.io/notes/list-rust-books/" rel="alternate" type="text/html" title="List Rust books" /><published>2025-11-30T00:00:00+00:00</published><updated>2025-11-30T00:00:00+00:00</updated><id>https://xcemaxx.github.io/notes/list-rust-books</id><content type="html" xml:base="https://xcemaxx.github.io/notes/list-rust-books/"><![CDATA[<p>В ходе обучения Rust я встретил множество книг на просторах интернета, которые перестали умещаться у меня в голове. Поэтому я решил собрать их в один список.</p>

<p>Не все материалы из этого перечня я уже прочитал. Да и не все собираюсь читать. Некоторые книги носят справочный характер, которые читать от корки до корки смысла нет. Другие я уже перерос, изучив дургие источники.</p>

<h2 id="docs">Docs</h2>
<ol>
  <li><a href="https://doc.rust-lang.org/book/index.html">The Rust Programming Language</a>, <a href="https://rust-book.cs.brown.edu">The Rust Programming Language(interactive)</a> ✅</li>
  <li><a href="https://doc.rust-lang.org/stable/nomicon/">The Rustonomicon</a> ✅</li>
  <li><a href="https://doc.rust-lang.org/reference/index.html">The Rust Reference</a></li>
  <li><a href="https://rust-fuzz.github.io/book/">Rust Fuzz Book</a></li>
  <li><a href="https://doc.rust-lang.org/unstable-book/the-unstable-book.html">The Rust Unstable Book</a></li>
  <li><a href="https://doc.rust-lang.org/cargo/index.html">The Cargo Book</a></li>
  <li><a href="https://rust-lang.github.io/api-guidelines/about.html">Rust API Guidelines</a></li>
  <li><a href="https://rust-lang.github.io/rustup/index.html">The rustup book</a></li>
</ol>

<h2 id="study">Study</h2>
<ol>
  <li><a href="https://www.lurklurk.org/effective-rust">Effective Rust</a> ✅</li>
  <li><a href="https://rust-unofficial.github.io/patterns/">Rust Design Patterns</a> ✅</li>
  <li><a href="https://nnethercote.github.io/perf-book/title-page.html">The Rust Performance Book</a></li>
  <li><a href="https://rust-lang.github.io/async-book/">Asynchronous Programming in Rust</a></li>
  <li><a href="https://lukaswirth.dev/tlborm/">The Little Book of Rust Macros</a></li>
  <li><a href="https://lifetime-variance.sunshowers.io/index.html">Lifetime Variance Example</a> ✅</li>
  <li><a href="https://docs.rust-embedded.org/embedonomicon/">The Embedonomicon</a></li>
</ol>

<h2 id="practice-oriented">Practice-oriented</h2>
<ol>
  <li><a href="https://google.github.io/comprehensive-rust/index.html">Comprehensive Rust</a> ✅</li>
  <li><a href="https://rust-unofficial.github.io/too-many-lists/">Learning Rust With Entirely Too Many Linked Lists</a> ✅</li>
  <li><a href="https://rust-lang-nursery.github.io/rust-cookbook/intro.html">Rust Cookbook</a> ✅</li>
  <li><a href="https://practice.course.rs/why-exercise.html">Rust By Practice</a></li>
  <li><a href="https://doc.rust-lang.org/rust-by-example/index.html">Rust by Example</a></li>
</ol>

<h2 id="books-about-rust-but-not-in-rust-book-style">Books about Rust, but not in Rust-book style</h2>
<ol>
  <li><a href="https://marabos.nl/atomics/">Rust Atomics and Locks</a> ✅</li>
</ol>]]></content><author><name>Andreev Semen</name></author><category term="Notes" /><category term="study" /><category term="rust" /><summary type="html"><![CDATA[В ходе обучения Rust я встретил множество книг на просторах интернета, которые перестали умещаться у меня в голове. Поэтому я решил собрать их в один список.]]></summary></entry><entry><title type="html">Реклама на youtube и её отсутствие</title><link href="https://xcemaxx.github.io/notes/ad-block/" rel="alternate" type="text/html" title="Реклама на youtube и её отсутствие" /><published>2025-11-23T00:00:00+00:00</published><updated>2025-11-23T00:00:00+00:00</updated><id>https://xcemaxx.github.io/notes/ad-block</id><content type="html" xml:base="https://xcemaxx.github.io/notes/ad-block/"><![CDATA[<p>Сколько помню, всегда пользовался расширением <a href="https://chromewebstore.google.com/detail/adblock-—-block-ads-acros/gighmmpiobklfepjocnamgkkbiglidom">AdBlock</a>, который успешно справлялся с блокировкой всей рекламы. Последние пару недель перестал работать нормально.</p>

<p>Пришлось поискать альтернативы, помог reddit. Хороший человек Reid Harrison оформил пост <a href="https://honest-software.com/adblock/">The Ultimate Adblocking &amp; Privacy Guide</a> о том, что можно использовать для блокировки рекламы. Описал подходы через браузер Brave, фильтрацию через DNS и расширения к популярным браузерам. Последним пунктом я и воспользовался, поставил себе расширение <a href="https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh">uBlock Origin Lite</a>.</p>

<p>После недели теста могу сказать, что расширение справляется отлично со своей работой.</p>

<p>Тяжела стала жизнь в текущем интернете. Кроме рекламы бывает youtube не доступен. С этим может помочь VPN. Есть и другие методы: 
<a href="https://github.com/Flowseal/zapret-discord-youtube">zapret-discord-youtube</a>, и сборка на его основе <em>YTDisBystro</em>. Возможно, штуки полезные, но благо мне ими воспользоваться еще не приходилось.</p>]]></content><author><name>Andreev Semen</name></author><category term="Notes" /><category term="utilities" /><summary type="html"><![CDATA[Сколько помню, всегда пользовался расширением AdBlock, который успешно справлялся с блокировкой всей рекламы. Последние пару недель перестал работать нормально.]]></summary></entry><entry><title type="html">Алгоритмы. Томас Кормен</title><link href="https://xcemaxx.github.io/books/algo-kormen/" rel="alternate" type="text/html" title="Алгоритмы. Томас Кормен" /><published>2025-11-16T00:00:00+00:00</published><updated>2025-11-16T00:00:00+00:00</updated><id>https://xcemaxx.github.io/books/algo-kormen</id><content type="html" xml:base="https://xcemaxx.github.io/books/algo-kormen/"><![CDATA[<p>Алгоритмы. Построение и анализ [2007] Томас Кормен</p>

<p class="notice--info text-center">Слишком сложно для глупого современного программиста</p>

<figure style="display: flex; justify-content: center;">
  <img src="/assets/images/2025-11-16-algo-kormen.png" style="width: 100%; max-width: 600px;" alt="cache latencies" />
</figure>

<p>Базовая база - алгоритмы с математикой, доказательствами корректности и оценками сложности. Та самая книга, которую хорошо бы читать на первых курсах университета. Навёрстываю упущенное.</p>

<p>Прочитал полностью, но усвоил далеко не всё. Формальные доказательства и математические выкладки даются тяжело. Даже сложные алгоритмы с разбором на бумаге даются тяжело. В <a href="/notes/yandex-contest/">контестах от яндекса</a> с видео разбором всё усваивается в разы легче: показал, объяснил и вперед нарешивать задачки на тему.</p>

<p>Вот раньше хорошие программисты были по умолчанию математиками. Сейчас уже нет. Программирование не требует глубоких математических знаний. Основы нужны, но не более. Эта книга требует от тебя этого “более”. Начинать изучать алгоритмы по ней не стоит. И в целом она настолько специфична, что лучше потратить время на чтение чего то другого. Например, у того же автора есть маленькая книжка <strong>“Алгоритмы. Вводный курс”</strong>. Она простая, все по делу, мне понравилась.</p>

<h2 id="что-запомнилось">Что запомнилось</h2>
<h3 id="heap-sort">Heap sort</h3>
<p>Обычно в обучающих ресурсах приводят merge и select сортировки, а вот “пирамидальная” сортировка прошла как то мимо меня. Взял и написал сам. Понравилась она мне.</p>

<h3 id="красно-чёрное-дерево-и-rust">Красно-чёрное дерево и Rust</h3>
<p>Захотел ради учебы написать красно-черное дерево. Думал, что смогу избежать проблем с мутабельностью в Rust с помощью отдельной арены - вместо указателей хранить индексы. Но реализация операции поворота поставила меня в тупик - либо писать unsafe для модификации нескольких узлов одновременно, либо добавлять Box/RefCell. Ничего из этого не захотелось, поэтому план кодинга красно-черного полностью провалился.</p>

<h3 id="матрицы-и-линейное-программирование">Матрицы и линейное программирование</h3>
<p>Неожиданно понравилась тема.</p>]]></content><author><name>Andreev Semen</name></author><category term="Books" /><category term="book" /><summary type="html"><![CDATA[Алгоритмы. Построение и анализ [2007] Томас Кормен]]></summary></entry><entry><title type="html">Патент: Загрузка единожды собранного LKM под любую версию ядра Linux</title><link href="https://xcemaxx.github.io/notes/patent-lkm/" rel="alternate" type="text/html" title="Патент: Загрузка единожды собранного LKM под любую версию ядра Linux" /><published>2025-11-02T00:00:00+00:00</published><updated>2025-11-02T00:00:00+00:00</updated><id>https://xcemaxx.github.io/notes/patent-lkm</id><content type="html" xml:base="https://xcemaxx.github.io/notes/patent-lkm/"><![CDATA[<p>Вышел патент от нашей команды разработчиков в «Лаборатории Касперского». За него мы получили награду <strong>Innovation of the year award 2023</strong>.</p>

<p><a href="https://patents.google.com/patent/RU2844647C1/">Патент RU2844647C1 “Система и способ загрузки драйвера в операционную систему” можно найти по ссылке</a>.</p>
<figure style="display: flex; justify-content: center;">
  <img src="/assets/images/2025-11-02-patent-lkm.jpg" style="width: 100%; max-width: 400px;" alt="cache latencies" />
</figure>

<p>Основная идея патента - один раз собрать ядро кернел-модуля, и с помощью ловкости рук “дособрать” полноценный модуль ядра уже под конкретное ядро Linux и запустить его. Кроме ловкости рук требуется заранее подготовленная метаинформация о каждом ядре и работающий в системе модуль-донор.</p>

<p>Во время работы над патентом я занимался множеством интересных задач:</p>
<ul>
  <li>погрузился в исходники ядра Linux, точнее систему хуков: fanotify, fsnotify, k/uprobes, USDT, lttng-ust;</li>
  <li>писал простой модуль ядра;</li>
  <li>разрабатывал Python-краулеры для сбора всех версий ядер Debian и Ubuntu.</li>
</ul>

<p>Хотя я уже и уволилися из Касперского, считаю эту компанию отличным местом для работы. Даже немного скучаю 💚💚💚</p>]]></content><author><name>Andreev Semen</name></author><category term="Notes" /><category term="linux" /><summary type="html"><![CDATA[Вышел патент от нашей команды разработчиков в «Лаборатории Касперского». За него мы получили награду Innovation of the year award 2023.]]></summary></entry><entry><title type="html">Что каждый программист должен знать о памяти</title><link href="https://xcemaxx.github.io/books/should-know-memory/" rel="alternate" type="text/html" title="Что каждый программист должен знать о памяти" /><published>2025-10-05T00:00:00+00:00</published><updated>2025-10-05T00:00:00+00:00</updated><id>https://xcemaxx.github.io/books/should-know-memory</id><content type="html" xml:base="https://xcemaxx.github.io/books/should-know-memory/"><![CDATA[<p><a href="https://rus-linux.net/lib.php?name=/MyLDP/hard/memory/memory.html">Что каждый программист должен знать о памяти [2007] Ulrich Drepper</a></p>

<p class="notice--info text-center">A Solid Average</p>

<p>Нужно ли её читать — вопрос. Уж точно не “каждому”.</p>

<p>Книге уже 17 лет, но информация актуальна: устройство памяти и кэшей практически не изменилось. Разве L3 уже не диковинка.</p>

<p>На примерах кода показаны последствия изменения рабочего набора данных, количества потоков, расположения критического слова. Автор показывает, как влияют размеры кэша/TLB, количество промахов и переключений контекста, механизм предварительной загрузки.</p>

<p>Читается сухо, но альтернатив по теме я не встречал. Если знаете что-то похожее — буду благодарен за ссылки.</p>

<p>Я бы советовал читать выборочно: части 2, 3 и 5 — “Кэш-память процессора”, “Виртуальная память”, “Оптимизация кэша”. Возможно, начало 6 части и 7 — “Мультипотоковая оптимизация”, “Инструменты”.</p>

<p>Остальное мало применимо, специфично. Например, я в своей работе не привязываю потоки к ядру и не ориентируюсь на NUMA архитектуру. Да и часть инструментов профилирования можно заменить на современный подход.</p>
<blockquote>
  <p>Английская версия книги доступна по ссылке: <a href="https://lwn.net/Articles/250967/">What every programmer should know about memory</a></p>
</blockquote>

<h1 id="практика">Практика</h1>
<p>Не удержался и провёл эксперимент на своей машине: проверял, можно ли определить размер кэшей с помощью кода — можно.</p>
<figure>
  <img src="/assets/images/2025-10-05-cache-latency.png" style="width: 100%; max-width: 700px;" alt="cache latencies" />
</figure>
<p>Визуализировал всё на Python — можно попросить любой AI инструмент, он этот код напишет на раз два. Впрочем, и код ниже был написан с помощью AI - в нём нет ничего необычного, разве пара моментов с <code class="language-plaintext highlighter-rouge">madvise</code>, разогревом и <code class="language-plaintext highlighter-rouge">black_box</code>, чтобы компилятор ничего не оптимизировал.</p>

<p>Программа создаёт связный список в памяти, пробегает его несколько раз и измеряет среднюю задержку доступа. Таким образом можно наглядно увидеть влияние кэширования и размера рабочего набора.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">Node</span> <span class="p">{</span>
    <span class="n">next</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="n">Node</span><span class="p">,</span>
    <span class="n">_pad</span><span class="p">:</span> <span class="p">[</span><span class="nb">usize</span><span class="p">;</span> <span class="mi">0</span><span class="p">],</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="n">LinkedListArena</span> <span class="p">{</span>
    <span class="n">ptr</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span><span class="p">,</span>
    <span class="n">layout</span><span class="p">:</span> <span class="n">Layout</span><span class="p">,</span>
    <span class="n">head</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="n">Node</span><span class="p">,</span>
    <span class="n">num_elements</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
    <span class="n">total_bytes</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">LinkedListArena</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">num_elements</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">element_size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="nd">assert!</span><span class="p">(</span><span class="n">element_size</span> <span class="o">&gt;=</span> <span class="nn">mem</span><span class="p">::</span><span class="nn">size_of</span><span class="p">::</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span><span class="p">());</span>
        <span class="nd">assert!</span><span class="p">(</span><span class="n">element_size</span> <span class="o">%</span> <span class="nn">mem</span><span class="p">::</span><span class="nn">size_of</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
        <span class="k">let</span> <span class="n">total_bytes</span> <span class="o">=</span> <span class="n">num_elements</span>
            <span class="nf">.checked_mul</span><span class="p">(</span><span class="n">element_size</span><span class="p">)</span>
            <span class="nf">.expect</span><span class="p">(</span><span class="s">"size overflow when computing total_bytes"</span><span class="p">);</span>

        <span class="k">let</span> <span class="n">align</span> <span class="o">=</span> <span class="mi">64usize</span><span class="nf">.max</span><span class="p">(</span><span class="nn">mem</span><span class="p">::</span><span class="nn">align_of</span><span class="p">::</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span><span class="p">());</span>
        <span class="k">let</span> <span class="n">layout</span> <span class="o">=</span> <span class="nn">Layout</span><span class="p">::</span><span class="nf">from_size_align</span><span class="p">(</span><span class="n">total_bytes</span><span class="p">,</span> <span class="n">align</span><span class="p">)</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"invalid layout"</span><span class="p">);</span>

        <span class="k">let</span> <span class="n">ptr</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">alloc_zeroed</span><span class="p">(</span><span class="n">layout</span><span class="p">)</span> <span class="p">};</span>
        <span class="k">if</span> <span class="n">ptr</span><span class="nf">.is_null</span><span class="p">()</span> <span class="p">{</span>
            <span class="nd">panic!</span><span class="p">(</span><span class="s">"memory allocation failed"</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="c1">// Optionally advise the kernel about access pattern and lock into RAM to avoid swapping</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="nf">madvise</span><span class="p">(</span><span class="n">ptr</span> <span class="k">as</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">c_void</span><span class="p">,</span> <span class="n">total_bytes</span><span class="p">,</span> <span class="n">MADV_RANDOM</span><span class="p">);</span>
            <span class="k">if</span> <span class="nf">mlock</span><span class="p">(</span><span class="n">ptr</span> <span class="k">as</span> <span class="o">*</span><span class="k">const</span> <span class="nb">c_void</span><span class="p">,</span> <span class="n">total_bytes</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span>
                <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"Warning: mlock failed (continuing anyway). Consider running with privileges or ulimit adjustments."</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">num_elements</span> <span class="p">{</span>
                <span class="k">let</span> <span class="n">current_node_ptr</span> <span class="o">=</span> <span class="n">ptr</span><span class="nf">.add</span><span class="p">(</span><span class="n">i</span> <span class="o">*</span> <span class="n">element_size</span><span class="p">)</span> <span class="k">as</span> <span class="o">*</span><span class="k">mut</span> <span class="n">Node</span><span class="p">;</span>
                <span class="k">let</span> <span class="n">next_idx</span> <span class="o">=</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">num_elements</span><span class="p">;</span>
                <span class="k">let</span> <span class="n">next_node_ptr</span> <span class="o">=</span> <span class="n">ptr</span><span class="nf">.add</span><span class="p">(</span><span class="n">next_idx</span> <span class="o">*</span> <span class="n">element_size</span><span class="p">)</span> <span class="k">as</span> <span class="o">*</span><span class="k">mut</span> <span class="n">Node</span><span class="p">;</span>
                <span class="nn">ptr</span><span class="p">::</span><span class="nf">write</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="p">(</span><span class="o">*</span><span class="n">current_node_ptr</span><span class="p">)</span><span class="py">.next</span><span class="p">,</span> <span class="n">next_node_ptr</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">let</span> <span class="n">head</span> <span class="o">=</span> <span class="n">ptr</span> <span class="k">as</span> <span class="o">*</span><span class="k">mut</span> <span class="n">Node</span><span class="p">;</span>

        <span class="k">Self</span> <span class="p">{</span>
            <span class="n">ptr</span><span class="p">,</span>
            <span class="n">layout</span><span class="p">,</span>
            <span class="n">head</span><span class="p">,</span>
            <span class="n">num_elements</span><span class="p">,</span>
            <span class="n">total_bytes</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">run_benchmark</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">warmup_iterations</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">iterations</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">f64</span> <span class="p">{</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">cur</span> <span class="o">=</span> <span class="k">self</span><span class="py">.head</span><span class="p">;</span>
            <span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="p">(</span><span class="n">warmup_iterations</span> <span class="o">*</span> <span class="k">self</span><span class="py">.num_elements</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">cur</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="nf">black_box</span><span class="p">(</span><span class="n">cur</span><span class="p">))</span><span class="py">.next</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="k">let</span> <span class="n">start</span> <span class="o">=</span> <span class="nn">Instant</span><span class="p">::</span><span class="nf">now</span><span class="p">();</span>
            <span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="p">(</span><span class="n">iterations</span> <span class="o">*</span> <span class="k">self</span><span class="py">.num_elements</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">cur</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="nf">black_box</span><span class="p">(</span><span class="n">cur</span><span class="p">))</span><span class="py">.next</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">let</span> <span class="n">elapsed</span> <span class="o">=</span> <span class="n">start</span><span class="nf">.elapsed</span><span class="p">();</span>
            <span class="c1">// prevent compiler from optimizing away the traversal</span>
            <span class="nf">black_box</span><span class="p">(</span><span class="n">cur</span><span class="p">);</span>

            <span class="k">let</span> <span class="n">total_steps</span> <span class="o">=</span> <span class="p">(</span><span class="n">iterations</span> <span class="o">*</span> <span class="k">self</span><span class="py">.num_elements</span><span class="p">)</span> <span class="k">as</span> <span class="nb">f64</span><span class="p">;</span>
            <span class="n">elapsed</span><span class="nf">.as_nanos</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">/</span> <span class="n">total_steps</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="nb">Drop</span> <span class="k">for</span> <span class="n">LinkedListArena</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="nf">munlock</span><span class="p">(</span><span class="k">self</span><span class="py">.ptr</span> <span class="k">as</span> <span class="o">*</span><span class="k">const</span> <span class="nb">c_void</span><span class="p">,</span> <span class="k">self</span><span class="py">.total_bytes</span><span class="p">);</span>
            <span class="nf">dealloc</span><span class="p">(</span><span class="k">self</span><span class="py">.ptr</span><span class="p">,</span> <span class="k">self</span><span class="py">.layout</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">TOTAL_STEPS</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">10_000_000</span><span class="p">;</span>
    <span class="k">const</span> <span class="n">WARMUP_ITERATIONS</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>

    <span class="k">let</span> <span class="n">args</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">env</span><span class="p">::</span><span class="nf">args</span><span class="p">()</span><span class="nf">.collect</span><span class="p">();</span>
    <span class="k">if</span> <span class="n">args</span><span class="nf">.len</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">3</span> <span class="p">{</span>
        <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"Usage: {} working_set_2_pow npad"</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
        <span class="nn">std</span><span class="p">::</span><span class="nn">process</span><span class="p">::</span><span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="n">working_set</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="nf">.parse</span><span class="p">()</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"working_set_kb must be an integer"</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">npad</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="nf">.parse</span><span class="p">()</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"NPAD must be an integer"</span><span class="p">);</span>

    <span class="k">let</span> <span class="n">working_set_bytes</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">working_set</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">element_size</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nn">size_of</span><span class="p">::</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span><span class="p">()</span> <span class="o">+</span> <span class="n">npad</span> <span class="o">*</span> <span class="nn">std</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nn">size_of</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">num_elements</span> <span class="o">=</span> <span class="n">working_set_bytes</span> <span class="o">/</span> <span class="n">element_size</span><span class="p">;</span>

    <span class="k">if</span> <span class="n">num_elements</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="p">{</span>
        <span class="nd">panic!</span><span class="p">(</span><span class="s">"Too few elements (num_elements &lt; 2). Try larger working_set_kb or smaller element_stride."</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="n">iterations</span> <span class="o">=</span> <span class="p">(</span><span class="n">TOTAL_STEPS</span> <span class="o">+</span> <span class="n">num_elements</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="n">num_elements</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">arena</span> <span class="o">=</span> <span class="nn">LinkedListArena</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">num_elements</span><span class="p">,</span> <span class="n">element_size</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">avg_ns</span> <span class="o">=</span> <span class="n">arena</span><span class="nf">.run_benchmark</span><span class="p">(</span><span class="n">WARMUP_ITERATIONS</span><span class="p">,</span> <span class="n">iterations</span><span class="p">);</span>

    <span class="k">let</span> <span class="n">working_set_kb</span> <span class="o">=</span> <span class="n">working_set_bytes</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">;</span>
    <span class="nd">println!</span><span class="p">(</span>
        <span class="s">"working_set {}kb, npad:{}: {}ns"</span><span class="p">,</span>
        <span class="n">working_set_kb</span><span class="p">,</span> <span class="n">npad</span><span class="p">,</span> <span class="n">avg_ns</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Andreev Semen</name></author><category term="Books" /><category term="book" /><summary type="html"><![CDATA[Что каждый программист должен знать о памяти [2007] Ulrich Drepper]]></summary></entry></feed>