<?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://program.sonsuz.us/feed.xml" rel="self" type="application/atom+xml" /><link href="https://program.sonsuz.us/" rel="alternate" type="text/html" /><updated>2026-09-28T00:05:42+00:00</updated><id>https://program.sonsuz.us/feed.xml</id><title type="html">SonsuzUs</title><subtitle>Programlama ve Yazılım</subtitle><author><name>Sonsuz Us</name></author><entry><title type="html">APIC Sahne Arkası: Çok Çekirdekli Sistemlerde Kesme Dağıtımı</title><link href="https://program.sonsuz.us/posts/apic-sahne-arkasi-cok-cekirdekli-sistemlerde-kesme-dagitimi/" rel="alternate" type="text/html" title="APIC Sahne Arkası: Çok Çekirdekli Sistemlerde Kesme Dağıtımı" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/apic-sahne-arkasi-cok-cekirdekli-sistemlerde-kesme-dagitimi</id><content type="html" xml:base="https://program.sonsuz.us/posts/apic-sahne-arkasi-cok-cekirdekli-sistemlerde-kesme-dagitimi/"><![CDATA[<p>Fare düğmesine bastığınızda işlemcilerin hep bir ağızdan “Ben bakarım!” diye bağırdığını düşünebilirsiniz. Gerçekte donanım, işletim sistemi ve <strong>Gelişmiş Programlanabilir Kesme Denetleyicisi (APIC)</strong> birlikte çalışarak sinyalin hangi çekirdeğe gönderileceğini belirler. Bu mekanizma yalnızca kullanıcı girişlerini değil; ağ paketlerini, disk işlemlerini ve zamanlayıcı olaylarını da düzenleyen görünmez bir trafik polisidir.
``</p>
<h2 id="kesme-neden-dağıtılmalı">Kesme neden dağıtılmalı?</h2>

<p>Bir aygıt işlemcinin ilgilenmesini istediğinde bir <strong>kesme isteği (IRQ)</strong> üretir. Tek çekirdekli bir makinede hedef bellidir. Çok çekirdekli sistemde ise bütün kesmeleri aynı çekirdeğe göndermek darboğaz yaratır.</p>

<p>Bir çekirdeğin kesmeler için harcadığı yaklaşık işlem yükünü şöyle gösterebiliriz:</p>

\[L_i = \sum_{k=1}^{n} r_k \times c_k\]

<p>Burada $L_i$, $i$ çekirdeğinin yükü; $r_k$, bir kesme kaynağının saniyedeki olay sayısı; $c_k$ ise olayı işleme maliyetidir. Amaç, yükü çekirdekler arasında dengelerken önbellek yerelliğini ve uygulama gecikmesini de korumaktır.</p>

<h2 id="apic-ailesinin-oyuncuları">APIC ailesinin oyuncuları</h2>

<p>Klasik PIC yalnızca az sayıda IRQ hattını yönetebiliyordu. APIC mimarisi daha fazla kesme vektörü, çoklu işlemci desteği ve esnek hedef seçimi sundu.</p>

<table>
  <thead>
    <tr>
      <th>Bileşen</th>
      <th>Konum</th>
      <th>Görevi</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Local APIC</td>
      <td>Her mantıksal işlemcide</td>
      <td>Kesme kabul eder, önceliklendirir ve EOI işler</td>
    </tr>
    <tr>
      <td>I/O APIC</td>
      <td>Anakart/yonga seti tarafında</td>
      <td>Fiziksel IRQ hatlarını APIC hedeflerine yönlendirir</td>
    </tr>
    <tr>
      <td>MSI/MSI-X</td>
      <td>PCIe aygıt mekanizması</td>
      <td>Kesme yerine özel bir bellek yazma işlemi gönderir</td>
    </tr>
    <tr>
      <td>Interrupt Remapping</td>
      <td>IOMMU tarafında</td>
      <td>Kesme hedeflerini doğrular ve güvenli biçimde yeniden eşler</td>
    </tr>
  </tbody>
</table>

<p><strong>xAPIC</strong>, hedefleri APIC kimlikleriyle belirlerken kayıtlarına bellek eşlemeli erişim kullanır. Daha yeni <strong>x2APIC</strong> ise genişletilmiş kimlik alanı ve MSR tabanlı erişim sayesinde çok daha fazla mantıksal işlemciyi destekler.</p>

<h2 id="hedef-çekirdek-nasıl-seçiliyor">Hedef çekirdek nasıl seçiliyor?</h2>

<p>I/O APIC yönlendirme tablosundaki her kayıt; kesme vektörünü, hedefi, tetikleme biçimini ve teslim modunu içerir. <strong>Fixed</strong> modunda kesme belirli bir APIC kimliğine gider. <strong>Lowest Priority</strong> modunda ise uygun hedef kümesi içinden öncelik durumu elverişli olan işlemci seçilebilir.</p>

<p>Modern ağ kartları çoğunlukla <strong>MSI-X</strong> kullanır. Kart, birden fazla kesme vektörü oluşturabilir; işletim sistemi de her alım-gönderim kuyruğunu farklı çekirdeğe bağlayabilir:</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Avantaj</th>
      <th>Dezavantaj</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tek çekirdeğe sabitleme</td>
      <td>İyi önbellek yerelliği</td>
      <td>Yoğun trafikte darboğaz</td>
    </tr>
    <tr>
      <td>Çekirdeklere dağıtma</td>
      <td>Yüksek paralellik</td>
      <td>Önbellek ve senkronizasyon maliyeti</td>
    </tr>
    <tr>
      <td>NUMA uyumlu dağıtma</td>
      <td>Bellek erişim gecikmesi düşer</td>
      <td>Yapılandırması daha karmaşıktır</td>
    </tr>
  </tbody>
</table>

<p>Örneğin ağ kuyruğu 0 çekirdek 2’ye, kuyruk 1 çekirdek 3’e atanabilir. Paket işleme uygulaması da aynı çekirdeklerde çalıştırılırsa veriler önbellekte sıcak kalır.</p>

<h2 id="linux-üzerinde-kesme-yakınlığı">Linux üzerinde kesme yakınlığı</h2>

<p>Linux, her IRQ için bir çekirdek maskesi sunar. Aşağıdaki komutlar önce kesmeleri listeler, ardından 44 numaralı IRQ’yu maske değeri <code class="language-plaintext highlighter-rouge">4</code>, yani çekirdek 2 ile sınırlar:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># IRQ sayaçlarını ve aygıt adlarını görüntüle</span>
<span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'CPU|eth|xhci'</span> /proc/interrupts

<span class="c"># 0b0100 maskesi: kesmeyi yalnızca CPU 2 alır</span>
<span class="nb">echo </span>4 | <span class="nb">sudo tee</span> /proc/irq/44/smp_affinity

<span class="c"># Çekirdek listesini daha okunabilir biçimde doğrula</span>
<span class="nb">cat</span> /proc/irq/44/smp_affinity_list
</code></pre></div></div>

<p>Gerçek IRQ numarası sistemden sisteme değişir. Ayrıca <code class="language-plaintext highlighter-rouge">irqbalance</code> hizmeti yükü otomatik dağıttığından elle yapılan ayarı daha sonra değiştirebilir.</p>

<h2 id="fare-tıklamasından-ağ-paketine">Fare tıklamasından ağ paketine</h2>

<p>USB fare tıklaması önce USB denetleyicisine ulaşır. Denetleyici bir kesme üretir; I/O APIC veya MSI mekanizması bunu seçilen Local APIC’e yollar. İşlemci mevcut işini uygun noktada durdurur, kesme işleyicisini çalıştırır ve sonunda <strong>EOI</strong> bildirerek işlemi tamamlar.</p>

<p>Ağ kartında süreç benzerdir ancak saniyede milyonlarca olay oluşabilir. Bu yüzden MSI-X kuyrukları, kesme birleştirme ve çekirdek yakınlığı kritik hâle gelir. Kısacası APIC yalnızca “hangi çekirdek?” sorusunu yanıtlamaz; sistemin gecikme, verim ve ölçeklenebilirlik dengesini de belirler.</p>

<p><img src="/img/apic-sahne-arkasi-38.svg" alt="apic-sahne-arkasi-38" /></p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="apic" /><category term="kesmeler" /><category term="çok çekirdek" /><category term="işletim sistemleri" /><category term="donanım" /><category term="linux" /><summary type="html"><![CDATA[Fare düğmesine bastığınızda işlemcilerin hep bir ağızdan “Ben bakarım!” diye bağırdığını düşünebilirsiniz. Gerçekte donanım, işletim sistemi ve Gelişmiş Programlanabilir Kesme Denetleyicisi (APIC) birlikte çalışarak sinyalin hangi çekirdeğe gönderileceğini belirler. Bu mekanizma yalnızca kullanıcı girişlerini değil; ağ paketlerini, disk işlemlerini ve zamanlayıcı olaylarını da düzenleyen görünmez bir trafik polisidir.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/apic-sahne-arkasi-38.png" /><media:content medium="image" url="https://program.sonsuz.us/img/apic-sahne-arkasi-38.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Çekirdek Modülü Geliştirmeye Giriş: Kendi Linux Cihaz Sürücünü Yaz</title><link href="https://program.sonsuz.us/posts/cekirdek-modulu-gelistirmeye-giris-kendi-linux-cihaz-surucunu-yaz/" rel="alternate" type="text/html" title="Çekirdek Modülü Geliştirmeye Giriş: Kendi Linux Cihaz Sürücünü Yaz" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/cekirdek-modulu-gelistirmeye-giris-kendi-linux-cihaz-surucunu-yaz</id><content type="html" xml:base="https://program.sonsuz.us/posts/cekirdek-modulu-gelistirmeye-giris-kendi-linux-cihaz-surucunu-yaz/"><![CDATA[<p>Linux çekirdeği devasa bir monolitik yapı olsa da her özelliği kalıcı olarak çekirdeğe gömmek zorunda değildir. Yüklenebilir Çekirdek Modülleri (Loadable Kernel Modules veya LKM), çalışan sistemi yeniden başlatmadan sürücü, dosya sistemi ya da ağ özelliği eklememizi sağlar. Bu yazıda küçük bir karakter cihazı geliştirerek modül mimarisini güvenli biçimde keşfedeceğiz.</p>

<p>``</p>

<h2 id="modül-mimarisi-nasıl-çalışır">Modül mimarisi nasıl çalışır?</h2>

<p>Bir çekirdek modülü, kullanıcı alanındaki sıradan uygulamalardan farklı olarak <strong>kernel space</strong> içinde çalışır. İşletim sisteminin belleğine ve donanım kaynaklarına doğrudan erişebildiği için küçük bir hata bile tüm sistemi durdurabilir. Bu nedenle geliştirmeyi sanal makinede yapmak akıllıcadır.</p>

<p>Modül çekirdeğe yüklendiğinde sembolleri bağlanır, başlangıç fonksiyonu çalıştırılır ve gerekli kaynaklar kaydedilir. Kaldırılırken kaynakların ters sırada serbest bırakılması gerekir. Basitleştirilmiş yaşam döngüsü şöyledir:</p>

\[Kaynak\ Durumu = Tahsis\ Edilenler - Serbest\ Bırakılanlar\]

<p>Sağlıklı bir kaldırma işleminde sonucun $0$ olması beklenir. Aksi durumda bellek sızıntıları, kullanımda kalan cihazlar veya çökmeler ortaya çıkabilir.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Kullanıcı alanı programı</th>
      <th>Çekirdek modülü</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Çalışma alanı</td>
      <td>User space</td>
      <td>Kernel space</td>
    </tr>
    <tr>
      <td>Standart kütüphane</td>
      <td>Genellikle kullanılabilir</td>
      <td>Kullanılamaz</td>
    </tr>
    <tr>
      <td>Hata sonucu</td>
      <td>Süreç kapanabilir</td>
      <td>Sistem çökebilir</td>
    </tr>
    <tr>
      <td>Çıktı yöntemi</td>
      <td><code class="language-plaintext highlighter-rouge">printf()</code></td>
      <td><code class="language-plaintext highlighter-rouge">pr_info()</code> / <code class="language-plaintext highlighter-rouge">printk()</code></td>
    </tr>
    <tr>
      <td>Yükleme</td>
      <td>Programı çalıştırma</td>
      <td><code class="language-plaintext highlighter-rouge">insmod</code> veya <code class="language-plaintext highlighter-rouge">modprobe</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/img/cekirdek-modulu-gelistirmeye-69.svg" alt="cekirdek-modulu-gelistirmeye-69" /></p>

<h2 id="basit-bir-karakter-cihazı">Basit bir karakter cihazı</h2>

<p>Aşağıdaki modül, <code class="language-plaintext highlighter-rouge">/dev/minidriver</code> adında bir karakter cihazı oluşturur. Kullanıcı cihazı okuduğunda çekirdekten kısa bir mesaj alır. <code class="language-plaintext highlighter-rouge">miscdevice</code> arabirimi, ana cihaz numarasını otomatik seçerek örneği sade tutar.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;linux/module.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/miscdevice.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/fs.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/uaccess.h&gt;</span><span class="cp">
</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">message</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"Merhaba, çekirdek!</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>

<span class="k">static</span> <span class="kt">ssize_t</span> <span class="nf">mini_read</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">file</span><span class="p">,</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="n">buffer</span><span class="p">,</span>
                         <span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="n">loff_t</span> <span class="o">*</span><span class="n">offset</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="n">simple_read_from_buffer</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">length</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span>
                                   <span class="n">message</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">file_operations</span> <span class="n">mini_fops</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">.</span><span class="n">owner</span> <span class="o">=</span> <span class="n">THIS_MODULE</span><span class="p">,</span>
    <span class="p">.</span><span class="n">read</span> <span class="o">=</span> <span class="n">mini_read</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">static</span> <span class="k">struct</span> <span class="n">miscdevice</span> <span class="n">mini_device</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">.</span><span class="n">minor</span> <span class="o">=</span> <span class="n">MISC_DYNAMIC_MINOR</span><span class="p">,</span>
    <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">"minidriver"</span><span class="p">,</span>
    <span class="p">.</span><span class="n">fops</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">mini_fops</span><span class="p">,</span>
    <span class="p">.</span><span class="n">mode</span> <span class="o">=</span> <span class="mo">0444</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="nf">mini_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">misc_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mini_device</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span>
        <span class="n">pr_err</span><span class="p">(</span><span class="s">"minidriver kaydedilemedi: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>
    <span class="k">else</span>
        <span class="n">pr_info</span><span class="p">(</span><span class="s">"minidriver yüklendi</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="n">__exit</span> <span class="nf">mini_exit</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">misc_deregister</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mini_device</span><span class="p">);</span>
    <span class="n">pr_info</span><span class="p">(</span><span class="s">"minidriver kaldırıldı</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">module_init</span><span class="p">(</span><span class="n">mini_init</span><span class="p">);</span>
<span class="n">module_exit</span><span class="p">(</span><span class="n">mini_exit</span><span class="p">);</span>

<span class="n">MODULE_LICENSE</span><span class="p">(</span><span class="s">"GPL"</span><span class="p">);</span>
<span class="n">MODULE_AUTHOR</span><span class="p">(</span><span class="s">"Örnek Geliştirici"</span><span class="p">);</span>
<span class="n">MODULE_DESCRIPTION</span><span class="p">(</span><span class="s">"Basit karakter cihazı modülü"</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">file_operations</code>, sistem çağrıları ile sürücü fonksiyonları arasındaki köprüdür. Kullanıcı <code class="language-plaintext highlighter-rouge">read()</code> çağırdığında çekirdek <code class="language-plaintext highlighter-rouge">mini_read()</code> fonksiyonuna yönelir. <code class="language-plaintext highlighter-rouge">simple_read_from_buffer()</code> ise çekirdek belleğindeki veriyi kullanıcı belleğine kontrollü şekilde aktarır; doğrudan işaretçi kopyalamaktan daha güvenlidir.</p>

<h2 id="derleme-ve-deneme">Derleme ve deneme</h2>

<p>Modülü derlemek için aynı dizine şu <code class="language-plaintext highlighter-rouge">Makefile</code> dosyasını ekleyin:</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">obj-m</span> <span class="o">+=</span> minidriver.o

<span class="nl">all</span><span class="o">:</span>
	<span class="p">$(</span>MAKE<span class="p">)</span> <span class="nt">-C</span> /lib/modules/<span class="p">$(</span>shell <span class="nb">uname</span> <span class="nt">-r</span><span class="p">)</span>/build <span class="nv">M</span><span class="o">=</span><span class="p">$(</span>PWD<span class="p">)</span> modules

<span class="nl">clean</span><span class="o">:</span>
	<span class="p">$(</span>MAKE<span class="p">)</span> <span class="nt">-C</span> /lib/modules/<span class="p">$(</span>shell <span class="nb">uname</span> <span class="nt">-r</span><span class="p">)</span>/build <span class="nv">M</span><span class="o">=</span><span class="p">$(</span>PWD<span class="p">)</span> clean
</code></pre></div></div>

<p>Ardından geliştirme başlıklarının kurulu olduğundan emin olarak komutları çalıştırın:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make
<span class="nb">sudo </span>insmod minidriver.ko
<span class="nb">cat</span> /dev/minidriver
<span class="nb">sudo </span>rmmod minidriver
dmesg | <span class="nb">tail</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">insmod</code> modülü doğrudan yüklerken <code class="language-plaintext highlighter-rouge">modprobe</code> bağımlılıkları da çözer. Üretim sistemlerinde Secure Boot imzasız modülleri reddedebilir; ayrıca çekirdek API’leri sürümler arasında değişebilir.</p>

<h2 id="güvenli-geliştirme-alışkanlıkları">Güvenli geliştirme alışkanlıkları</h2>

<p>Her tahsisin karşılığında bir temizleme işlemi yazın, kullanıcıdan gelen uzunluk ve adreslere güvenmeyin, kilit gerektiren ortak verileri koruyun ve günlükleri <code class="language-plaintext highlighter-rouge">dmesg</code> üzerinden izleyin. Gerçek donanım sürücülerinde kesmeler, DMA, aygıt ağacı ve platform veri yolları devreye girer. Ancak temel ritim değişmez: <strong>kaydet, isteği işle, kaynakları eksiksiz bırak ve modülü kaldır</strong>.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="linux" /><category term="kernel" /><category term="çekirdek-modülü" /><category term="cihaz-sürücüsü" /><category term="c" /><category term="sistem-programlama" /><summary type="html"><![CDATA[Linux çekirdeği devasa bir monolitik yapı olsa da her özelliği kalıcı olarak çekirdeğe gömmek zorunda değildir. Yüklenebilir Çekirdek Modülleri (Loadable Kernel Modules veya LKM), çalışan sistemi yeniden başlatmadan sürücü, dosya sistemi ya da ağ özelliği eklememizi sağlar. Bu yazıda küçük bir karakter cihazı geliştirerek modül mimarisini güvenli biçimde keşfedeceğiz.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/cekirdek-modulu-gelistirmeye-69.png" /><media:content medium="image" url="https://program.sonsuz.us/img/cekirdek-modulu-gelistirmeye-69.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Context Switch Optimizasyonları: Thread Değiştirmenin Görünmez Maliyeti</title><link href="https://program.sonsuz.us/posts/context-switch-optimizasyonlari-thread-degistirmenin-gorunmez-maliyeti/" rel="alternate" type="text/html" title="Context Switch Optimizasyonları: Thread Değiştirmenin Görünmez Maliyeti" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/context-switch-optimizasyonlari-thread-degistirmenin-gorunmez-maliyeti</id><content type="html" xml:base="https://program.sonsuz.us/posts/context-switch-optimizasyonlari-thread-degistirmenin-gorunmez-maliyeti/"><![CDATA[<p><img src="/img/context-switch-optimizasyonlari-19.svg" alt="context-switch-optimizasyonlari-19" /></p>

<p>Bir işlemci çekirdeği bir thread’i durdurup diğerini çalıştırdığında ekranda hiçbir şey kıpırdamaz; ancak donanım ve işletim sistemi küçük bir bavul toplama töreni gerçekleştirir. Yazmaçlar saklanır, yeni yürütme bağlamı yüklenir, adres çevirileri gözden geçirilir ve önbellek düzeni sarsılabilir. Context switch, yararlı iş üretmeyen fakat çoklu görev için kaçınılmaz olan bu sürecin adıdır.</p>

<p>``</p>

<h2 id="context-switch-sırasında-ne-saklanır">Context switch sırasında ne saklanır?</h2>

<p>Her thread’in program sayacı, yığın işaretçisi ve genel amaçlı yazmaçları gibi kendine ait bir işlemci durumu bulunur. Zamanlayıcı çalışan thread’i değiştirdiğinde çekirdeğe geçilir; mevcut durum thread’in kontrol bloğuna kaydedilir ve sıradaki thread’in durumu geri yüklenir.</p>

<p>Tipik olarak şu bilgiler işleme katılır:</p>

<ul>
  <li>Program sayacı ve stack pointer</li>
  <li>Genel amaçlı yazmaçlar</li>
  <li>Bayrak ve durum yazmaçları</li>
  <li>SIMD/FPU durumu</li>
  <li>Zamanlama ve muhasebe bilgileri</li>
  <li>Gerekirse sayfa tablosu bağlamı</li>
</ul>

<p>Buradaki “kopyalama”, çoğunlukla yazmaçların çekirdek belleğindeki yapılara yazılmasıdır. Modern sistemler, büyük SIMD durumunu her geçişte körlemesine taşımak yerine tembel veya donanım destekli yöntemler kullanabilir.</p>

<p>Bir uygulamanın toplam çalışma süresini basitçe şöyle düşünebiliriz:</p>

\[T_{toplam}=T_{yararlı}+N_{cs}\times(T_{doğrudan}+T_{dolaylı})\]

<p>Burada $N_{cs}$ context switch sayısıdır. Doğrudan maliyet, çekirdek kodu ve durum kaydetme süresidir. Dolaylı maliyet ise soğuyan cache, bozulan branch predictor geçmişi ve TLB kayıplarıdır. Çoğu zaman asıl canavar ikinci tarafta saklanır.</p>

<h2 id="tlb-neden-önemlidir">TLB neden önemlidir?</h2>

<p>TLB, sanal adreslerin fiziksel adreslere çevrilmiş sonuçlarını tutan küçük ve hızlı bir önbellektir. Farklı süreçler farklı adres uzaylarına sahip olduğundan süreçler arası geçiş, eski çevirilerin geçersiz kılınmasını gerektirebilir. Bir sonraki bellek erişiminde TLB miss oluşursa işlemci sayfa tablolarını yürümek zorunda kalır.</p>

<p>Bununla birlikte “her context switch TLB’yi tamamen temizler” demek günümüzde doğru değildir. PCID ve ASID gibi etiketleme mekanizmaları, farklı adres uzaylarının TLB kayıtlarını birlikte koruyabilir. Aynı süreçteki thread’ler zaten adres uzayını paylaştığı için aralarındaki geçiş genellikle daha ucuzdur.</p>

<table>
  <thead>
    <tr>
      <th>Geçiş türü</th>
      <th>Adres uzayı</th>
      <th>TLB etkisi</th>
      <th>Tahmini maliyet</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Aynı thread, fonksiyon çağrısı</td>
      <td>Aynı</td>
      <td>Yok</td>
      <td>Çok düşük</td>
    </tr>
    <tr>
      <td>Aynı süreçte iki thread</td>
      <td>Aynı</td>
      <td>Genellikle sınırlı</td>
      <td>Orta</td>
    </tr>
    <tr>
      <td>Farklı süreçler</td>
      <td>Farklı</td>
      <td>PCID/ASID’ye bağlı</td>
      <td>Daha yüksek</td>
    </tr>
    <tr>
      <td>Farklı çekirdeğe göç</td>
      <td>Değişebilir</td>
      <td>Cache yerelliği de bozulur</td>
      <td>En yüksek olabilir</td>
    </tr>
  </tbody>
</table>

<h2 id="maliyeti-ölçmek">Maliyeti ölçmek</h2>

<p>Aşağıdaki Linux örneği iki thread arasında <code class="language-plaintext highlighter-rouge">eventfd</code> kullanarak kontrollü biçimde sinyal dolaştırır. Amaç gerçek uygulamayı taklit etmek değil, çok sayıda uyandırma ve zamanlama geçişinin toplam süresini gözlemlemektir.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;pthread.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/eventfd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">tekrar</span> <span class="o">=</span> <span class="mi">100000</span><span class="p">;</span>

<span class="kt">void</span> <span class="o">*</span><span class="nf">worker</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">x</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">tekrar</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">read</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">x</span><span class="p">));</span>
        <span class="n">write</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">x</span><span class="p">));</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">pthread_t</span> <span class="n">t</span><span class="p">;</span>
    <span class="n">a</span> <span class="o">=</span> <span class="n">eventfd</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">b</span> <span class="o">=</span> <span class="n">eventfd</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">pthread_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">t</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">worker</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">tekrar</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">write</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">x</span><span class="p">));</span>
        <span class="n">read</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">x</span><span class="p">));</span>
    <span class="p">}</span>
    <span class="n">pthread_join</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Program <code class="language-plaintext highlighter-rouge">perf stat -e context-switches,cycles,instructions,cache-misses ./a.out</code> ile çalıştırılabilir. Ölçüm sırasında CPU frekansı, çekirdek göçleri ve arka plan yükü sonuçları etkileyebileceğinden deneyi birkaç kez tekrarlamak gerekir.</p>

<h2 id="optimizasyon-stratejileri">Optimizasyon stratejileri</h2>

<p>İlk hedef thread sayısını körlemesine artırmak değil, çalıştırılabilir thread sayısını çekirdek sayısıyla uyumlu tutmaktır. Thread pool kullanmak, kısa işler için sürekli thread oluşturup yok etmeyi önler. Küçük görevleri gruplamak zamanlayıcıya yapılan başvuruları azaltır.</p>

<p>CPU affinity, aynı thread’i belirli çekirdeklere bağlayarak cache yerelliğini koruyabilir; fakat dengesiz yükte boş çekirdeklerden yararlanmayı zorlaştırabilir. Kilit rekabetini azaltmak, gereksiz uyandırmaları engellemek ve bloklayan I/O için asenkron modeller kullanmak da etkilidir.</p>

<p>Sonuç olarak context switch tek başına kötü değildir; gereksiz ve aşırı olduğunda pahalıdır. En iyi optimizasyon, nanosanileri kovalamadan önce <code class="language-plaintext highlighter-rouge">perf</code>, profiler ve zamanlayıcı metrikleriyle gerçek darboğazı ölçmektir. Görünmez maliyet ancak ölçüldüğünde görünür olur.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="context-switch" /><category term="thread" /><category term="işletim-sistemleri" /><category term="performans" /><category term="cpu" /><category term="tlb" /><category term="linux" /><summary type="html"><![CDATA[Bir işlemci çekirdeği bir thread’i durdurup diğerini çalıştırdığında ekranda hiçbir şey kıpırdamaz; ancak donanım ve işletim sistemi küçük bir bavul toplama töreni gerçekleştirir. Yazmaçlar saklanır, yeni yürütme bağlamı yüklenir, adres çevirileri gözden geçirilir ve önbellek düzeni sarsılabilir. Context switch, yararlı iş üretmeyen fakat çoklu görev için kaçınılmaz olan bu sürecin adıdır.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/context-switch-optimizasyonlari-19.png" /><media:content medium="image" url="https://program.sonsuz.us/img/context-switch-optimizasyonlari-19.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">eBPF ile Çekirdeği Yeniden Derlemeden Ağ Paketlerini Manipüle Etmek</title><link href="https://program.sonsuz.us/posts/ebpf-ile-cekirdegi-yeniden-derlemeden-ag-paketlerini-manipule-etmek/" rel="alternate" type="text/html" title="eBPF ile Çekirdeği Yeniden Derlemeden Ağ Paketlerini Manipüle Etmek" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/ebpf-ile-cekirdegi-yeniden-derlemeden-ag-paketlerini-manipule-etmek</id><content type="html" xml:base="https://program.sonsuz.us/posts/ebpf-ile-cekirdegi-yeniden-derlemeden-ag-paketlerini-manipule-etmek/"><![CDATA[<p>Linux ağ yığınında paketleri yakalamak, filtrelemek veya yönlendirmek eskiden çekirdek modülü geliştirmeyi ve bazen çekirdeği yeniden derlemeyi gerektiriyordu. eBPF sayesinde artık doğrulanmış küçük programları çekirdek içinde güvenli biçimde çalıştırabiliyoruz. Böylece DDoS filtrelerinden gecikme ölçümüne kadar pek çok işi, kaynak koduna neşter vurmadan gerçekleştirmek mümkün.</p>

<p>``</p>

<h2 id="ebpf-tam-olarak-nedir">eBPF tam olarak nedir?</h2>

<p>eBPF, kullanıcı alanında hazırlanan programların Linux çekirdeğindeki belirli kancalara yüklenmesini sağlayan bir çalışma ortamıdır. İsmindeki BPF, tarihsel olarak “Berkeley Packet Filter” anlamına gelse de teknoloji bugün ağ paketlerinin çok ötesine uzanır: sistem çağrıları, süreçler, dosya erişimleri ve performans olayları da izlenebilir.</p>

<p>“Çekirdeğe dokunmadan” ifadesi, kodun çekirdek dışında çalıştığı anlamına gelmez. eBPF programı çekirdek bağlamında çalışır; ancak çekirdek kaynak kodunu değiştirmeye veya özel bir modül yüklemeye gerek bırakmaz. Program yüklenmeden önce <strong>verifier</strong> tarafından incelenir. Geçersiz bellek erişimi, güvensiz işaretçi kullanımı veya sonlanmama riski varsa program reddedilir.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Çalışma noktası</th>
      <th style="text-align: right">Performans</th>
      <th>Risk ve esneklik</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kullanıcı alanı yakalama</td>
      <td>Ağ yığınından sonra</td>
      <td style="text-align: right">Orta</td>
      <td>Güvenli, kopyalama maliyetli</td>
    </tr>
    <tr>
      <td>Netfilter/iptables</td>
      <td>Çekirdek ağ yığını</td>
      <td style="text-align: right">Yüksek</td>
      <td>Kurallarla sınırlı</td>
    </tr>
    <tr>
      <td>Çekirdek modülü</td>
      <td>Çekirdek içinde</td>
      <td style="text-align: right">Çok yüksek</td>
      <td>Hata tüm sistemi etkileyebilir</td>
    </tr>
    <tr>
      <td>eBPF/XDP</td>
      <td>Sürücüye çok yakın</td>
      <td style="text-align: right">Çok yüksek</td>
      <td>Verifier ile denetimli</td>
    </tr>
  </tbody>
</table>

<h2 id="xdp-neden-bu-kadar-hızlı">XDP neden bu kadar hızlı?</h2>

<p>XDP, yani <strong>eXpress Data Path</strong>, paketi ağ sürücüsünden gelir gelmez işleyebilir. Paket henüz <code class="language-plaintext highlighter-rouge">sk_buff</code> yapısına dönüştürülmeden karar verildiği için ayırma, kopyalama ve ağ yığını maliyetleri azaltılır.</p>

<p>Bir paketin yaklaşık işlem maliyetini şöyle düşünebiliriz:</p>

\[T_{toplam} = T_{alma} + T_{ayrıştırma} + T_{karar} + T_{yönlendirme}\]

<p>XDP özellikle $T_{ayrıştırma}$ ve $T_{yönlendirme}$ bileşenlerini küçültür. Program paketi kabul etmek için <code class="language-plaintext highlighter-rouge">XDP_PASS</code>, düşürmek için <code class="language-plaintext highlighter-rouge">XDP_DROP</code>, başka arayüze göndermek için <code class="language-plaintext highlighter-rouge">XDP_REDIRECT</code> döndürebilir.</p>

<p>Aşağıdaki orta düzey örnek, IPv4 UDP paketlerini erken aşamada düşürür:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;linux/bpf.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/if_ether.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/ip.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/udp.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;bpf/bpf_helpers.h&gt;</span><span class="cp">
</span>
<span class="n">SEC</span><span class="p">(</span><span class="s">"xdp"</span><span class="p">)</span>
<span class="kt">int</span> <span class="nf">drop_udp</span><span class="p">(</span><span class="k">struct</span> <span class="n">xdp_md</span> <span class="o">*</span><span class="n">ctx</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="kt">long</span><span class="p">)</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">data_end</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="kt">long</span><span class="p">)</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">data_end</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">ethhdr</span> <span class="o">*</span><span class="n">eth</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">((</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="n">eth</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">data_end</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">XDP_ABORTED</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">eth</span><span class="o">-&gt;</span><span class="n">h_proto</span> <span class="o">!=</span> <span class="n">__constant_htons</span><span class="p">(</span><span class="n">ETH_P_IP</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">XDP_PASS</span><span class="p">;</span>

    <span class="k">struct</span> <span class="n">iphdr</span> <span class="o">*</span><span class="n">ip</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="n">eth</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">((</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="n">ip</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">data_end</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">XDP_ABORTED</span><span class="p">;</span>

    <span class="k">return</span> <span class="n">ip</span><span class="o">-&gt;</span><span class="n">protocol</span> <span class="o">==</span> <span class="n">IPPROTO_UDP</span> <span class="o">?</span> <span class="n">XDP_DROP</span> <span class="o">:</span> <span class="n">XDP_PASS</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">char</span> <span class="n">LICENSE</span><span class="p">[]</span> <span class="n">SEC</span><span class="p">(</span><span class="s">"license"</span><span class="p">)</span> <span class="o">=</span> <span class="s">"GPL"</span><span class="p">;</span>
</code></pre></div></div>

<p>Sınır kontrolleri yalnızca iyi programlama alışkanlığı değildir; verifier, <code class="language-plaintext highlighter-rouge">data_end</code> kontrolü bulunmayan paket erişimini kabul etmez. Kod Clang ile eBPF bytecode’una çevrilip bir arayüze bağlanabilir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-O2</span> <span class="nt">-g</span> <span class="nt">-target</span> bpf <span class="nt">-c</span> filter.c <span class="nt">-o</span> filter.o
<span class="nb">sudo </span>ip <span class="nb">link set </span>dev eth0 xdp obj filter.o sec xdp
<span class="nb">sudo </span>ip <span class="nb">link set </span>dev eth0 xdp off
</code></pre></div></div>

<p>İlk komut programı derler, ikincisi <code class="language-plaintext highlighter-rouge">eth0</code> arayüzüne yükler, sonuncusu ise bağlantıyı kaldırır. Uzaktan bağlı bir sunucuda bütün UDP trafiğini düşürmeden önce DNS ve VPN kullanımını düşünmek iyi fikirdir; aksi hâlde güvenlik demosu hızla bağlantı kesme demosuna dönüşebilir.</p>

<h2 id="haritalar-ve-gözlemlenebilirlik">Haritalar ve gözlemlenebilirlik</h2>

<p>eBPF haritaları, çekirdek ve kullanıcı alanı arasında paylaşılan anahtar-değer depolarıdır. IP başına paket sayacı tutmak, engelli adresleri dinamik olarak güncellemek veya CPU başına istatistik toplamak için kullanılabilirler. Üretimde C ile doğrudan uğraşmak yerine libbpf, BCC, bpftrace, Cilium ve Rust tabanlı Aya gibi araçlardan yararlanılabilir.</p>

<p>TC kancaları paket değişikliği ve trafik şekillendirme konusunda daha esnekken XDP mümkün olan en erken kararı hedefler. Sonuç olarak eBPF, klasik çekirdek modüllerinin bütün risklerini üstlenmeden çekirdek hızına yaklaşan, programlanabilir bir ağ güvenliği ve izleme katmanı sunar.</p>

<p><img src="/img/ebpf-ile-cekirdegi-78.svg" alt="ebpf-ile-cekirdegi-78" /></p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="ebpf" /><category term="linux" /><category term="xdp" /><category term="ağ güvenliği" /><category term="paket işleme" /><category term="gözlemlenebilirlik" /><summary type="html"><![CDATA[Linux ağ yığınında paketleri yakalamak, filtrelemek veya yönlendirmek eskiden çekirdek modülü geliştirmeyi ve bazen çekirdeği yeniden derlemeyi gerektiriyordu. eBPF sayesinde artık doğrulanmış küçük programları çekirdek içinde güvenli biçimde çalıştırabiliyoruz. Böylece DDoS filtrelerinden gecikme ölçümüne kadar pek çok işi, kaynak koduna neşter vurmadan gerçekleştirmek mümkün.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/ebpf-ile-cekirdegi-78.png" /><media:content medium="image" url="https://program.sonsuz.us/img/ebpf-ile-cekirdegi-78.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Epoll vs Kqueue: C10K Çağında Olay Dinleme Sanatı</title><link href="https://program.sonsuz.us/posts/epoll-vs-kqueue-c10k-caginda-olay-dinleme-sanati/" rel="alternate" type="text/html" title="Epoll vs Kqueue: C10K Çağında Olay Dinleme Sanatı" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/epoll-vs-kqueue-c10k-caginda-olay-dinleme-sanati</id><content type="html" xml:base="https://program.sonsuz.us/posts/epoll-vs-kqueue-c10k-caginda-olay-dinleme-sanati/"><![CDATA[<p>Bir web sunucusunun aynı anda on binlerce bağlantıya cevap vermesi, yalnızca hızlı bir işlemciye sahip olmakla çözülmez. Asıl mesele, çoğu zaman hiçbir şey yapmadan veri bekleyen bağlantıları düşük maliyetle yönetebilmektir. Linux dünyasındaki <strong>epoll</strong> ve BSD/macOS tarafındaki <strong>kqueue</strong>, işletim sistemine “Hazır olanı bana haber ver, diğerleriyle beni uğraştırma” diyerek yüksek eşzamanlılığın kapısını açar.</p>

<p><img src="/img/epoll-vs-kqueue-15.svg" alt="epoll-vs-kqueue-15" /></p>

<p>``</p>
<h2 id="c10k-problemi-neden-ortaya-çıktı">C10K problemi neden ortaya çıktı?</h2>

<p>Klasik yaklaşımda her bağlantı için ayrı süreç veya iş parçacığı oluşturulurdu. Bu model anlaşılır olsa da bağlantı sayısı yükseldiğinde bellek tüketimi, bağlam değiştirme ve zamanlayıcı yükü büyür. Yaklaşık maliyeti şöyle düşünebiliriz:</p>

\[M \approx N \times S\]

<p>Burada $N$ bağlantı sayısını, $S$ ise her iş parçacığının yığın ve yönetim maliyetini temsil eder. Her iş parçacığı yalnızca 1 MB yığın ayırsa 10.000 bağlantı teorik olarak 10 GB adres alanı isteyebilir. Üstelik işlemci, iş yapanlardan çok bekleyen iş parçacıkları arasında dolaşabilir.</p>

<p><code class="language-plaintext highlighter-rouge">select</code> ve <code class="language-plaintext highlighter-rouge">poll</code> daha az iş parçacığıyla çalışmayı mümkün kılsa da her çağrıda izlenen tanımlayıcıların taranması gerekir. Bu davranış kabaca $O(N)$ maliyetlidir. epoll ve kqueue ise yalnızca durumu değişen olayları döndürerek maliyeti pratikte hazır olay sayısına, yani $O(K)$ düzeyine yaklaştırır.</p>

<h2 id="epoll-ve-kqueue-karşılaştırması">epoll ve kqueue karşılaştırması</h2>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>epoll</th>
      <th>kqueue</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Platform</td>
      <td>Linux</td>
      <td>FreeBSD, OpenBSD, NetBSD, macOS</td>
    </tr>
    <tr>
      <td>Temel nesne</td>
      <td>Dosya tanımlayıcısı olayları</td>
      <td>Genel amaçlı kernel olayları</td>
    </tr>
    <tr>
      <td>Çalışma biçimi</td>
      <td>İlgi listesi ve hazır listesi</td>
      <td>Filtre tabanlı olay kuyruğu</td>
    </tr>
    <tr>
      <td>Ağ dışı olaylar</td>
      <td>Daha sınırlı, ek API gerekebilir</td>
      <td>Sinyal, süreç, zamanlayıcı ve dosya sistemi filtreleri</td>
    </tr>
    <tr>
      <td>Tetikleme</td>
      <td>Level veya edge-triggered</td>
      <td>Varsayılan level, <code class="language-plaintext highlighter-rouge">EV_CLEAR</code> ile edge benzeri</td>
    </tr>
    <tr>
      <td>Güncelleme</td>
      <td><code class="language-plaintext highlighter-rouge">epoll_ctl</code></td>
      <td><code class="language-plaintext highlighter-rouge">kevent</code> değişiklik listesi</td>
    </tr>
  </tbody>
</table>

<p><strong>Level-triggered</strong> kullanımda veri okunabilir kaldığı sürece bildirim tekrar gelir. Bu, unutkan programcılar için daha güvenlidir. <strong>Edge-triggered</strong> kullanımda ise yalnızca durum değişiminde bildirim alınır. Daha az uyarı üretir ancak soketin <code class="language-plaintext highlighter-rouge">EAGAIN</code> sonucuna kadar tamamen boşaltılması gerekir; aksi hâlde veri kuyrukta sessizce bekleyebilir.</p>

<h2 id="linux-üzerinde-epoll-akışı">Linux üzerinde epoll akışı</h2>

<p>Aşağıdaki C kodu, bir epoll örneği oluşturup dinleme soketini takip listesine ekler:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">epfd</span> <span class="o">=</span> <span class="n">epoll_create1</span><span class="p">(</span><span class="n">EPOLL_CLOEXEC</span><span class="p">);</span>

<span class="k">struct</span> <span class="n">epoll_event</span> <span class="n">ev</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
<span class="n">ev</span><span class="p">.</span><span class="n">events</span> <span class="o">=</span> <span class="n">EPOLLIN</span> <span class="o">|</span> <span class="n">EPOLLET</span><span class="p">;</span>
<span class="n">ev</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">fd</span> <span class="o">=</span> <span class="n">server_fd</span><span class="p">;</span>

<span class="n">epoll_ctl</span><span class="p">(</span><span class="n">epfd</span><span class="p">,</span> <span class="n">EPOLL_CTL_ADD</span><span class="p">,</span> <span class="n">server_fd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ev</span><span class="p">);</span>

<span class="k">struct</span> <span class="n">epoll_event</span> <span class="n">events</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">epoll_wait</span><span class="p">(</span><span class="n">epfd</span><span class="p">,</span> <span class="n">events</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">handle_event</span><span class="p">(</span><span class="n">events</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">data</span><span class="p">.</span><span class="n">fd</span><span class="p">,</span> <span class="n">events</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">events</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">EPOLLIN</code>, okunabilir veri bulunduğunu; <code class="language-plaintext highlighter-rouge">EPOLLET</code> ise edge-triggered çalışma istendiğini belirtir. Gerçek bir sunucuda soketler non-blocking yapılmalı, <code class="language-plaintext highlighter-rouge">accept</code> ve <code class="language-plaintext highlighter-rouge">read</code> işlemleri <code class="language-plaintext highlighter-rouge">EAGAIN</code> görülene kadar döngüyle sürdürülmelidir.</p>

<h2 id="bsd-tarafında-kqueue">BSD tarafında kqueue</h2>

<p>kqueue aynı fikri filtrelerle daha genel hâle getirir:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">kq</span> <span class="o">=</span> <span class="n">kqueue</span><span class="p">();</span>
<span class="k">struct</span> <span class="n">kevent</span> <span class="n">change</span><span class="p">;</span>

<span class="n">EV_SET</span><span class="p">(</span><span class="o">&amp;</span><span class="n">change</span><span class="p">,</span> <span class="n">server_fd</span><span class="p">,</span> <span class="n">EVFILT_READ</span><span class="p">,</span>
       <span class="n">EV_ADD</span> <span class="o">|</span> <span class="n">EV_ENABLE</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">kevent</span><span class="p">(</span><span class="n">kq</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">change</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>

<span class="k">struct</span> <span class="n">kevent</span> <span class="n">events</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">kevent</span><span class="p">(</span><span class="n">kq</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">events</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">handle_event</span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">events</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">ident</span><span class="p">,</span> <span class="n">events</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">filter</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">EVFILT_READ</code> okunabilirliği izler. Aynı mekanizma <code class="language-plaintext highlighter-rouge">EVFILT_TIMER</code> ile zamanlayıcıları, <code class="language-plaintext highlighter-rouge">EVFILT_SIGNAL</code> ile sinyalleri ve <code class="language-plaintext highlighter-rouge">EVFILT_PROC</code> ile süreç değişimlerini de takip edebilir. Bu bütünlük, kqueue’nun en şık tarafıdır.</p>

<h2 id="hangisini-seçmeli">Hangisini seçmeli?</h2>

<p>Seçim çoğunlukla performanstan önce platform tarafından yapılır: Linux’ta epoll, BSD ve macOS’ta kqueue doğal tercihtir. Nginx, libuv, Tokio ve benzeri sistemler bu ayrıntıları soyutlayabilir. Yine de sihirli değnek yoktur; yavaş istemciler, geri basınç, zaman aşımı, bağlantı başına bellek ve adil görev dağıtımı ayrıca tasarlanmalıdır.</p>

<p>Sonuçta C10K’yı aşmanın sırrı, her bağlantıya çalışan tahsis etmek değil, <strong>hazır olana çalışacak zaman vermektir</strong>. epoll ve kqueue farklı lehçelerde konuşsa da aynı cümleyi kurar: Boşta bekleme, olay gelince harekete geç!</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="epoll" /><category term="kqueue" /><category term="c10k" /><category term="asenkron" /><category term="linux" /><category term="bsd" /><category term="ağ-programlama" /><summary type="html"><![CDATA[Bir web sunucusunun aynı anda on binlerce bağlantıya cevap vermesi, yalnızca hızlı bir işlemciye sahip olmakla çözülmez. Asıl mesele, çoğu zaman hiçbir şey yapmadan veri bekleyen bağlantıları düşük maliyetle yönetebilmektir. Linux dünyasındaki epoll ve BSD/macOS tarafındaki kqueue, işletim sistemine “Hazır olanı bana haber ver, diğerleriyle beni uğraştırma” diyerek yüksek eşzamanlılığın kapısını açar.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/epoll-vs-kqueue-15.png" /><media:content medium="image" url="https://program.sonsuz.us/img/epoll-vs-kqueue-15.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">FUSE ile Python Kullanarak Kendi Sanal Dosya Sisteminizi Yazın</title><link href="https://program.sonsuz.us/posts/fuse-ile-python-kullanarak-kendi-sanal-dosya-sisteminizi-yazin/" rel="alternate" type="text/html" title="FUSE ile Python Kullanarak Kendi Sanal Dosya Sisteminizi Yazın" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/fuse-ile-python-kullanarak-kendi-sanal-dosya-sisteminizi-yazin</id><content type="html" xml:base="https://program.sonsuz.us/posts/fuse-ile-python-kullanarak-kendi-sanal-dosya-sisteminizi-yazin/"><![CDATA[<p>Dosya sistemi yazmak denildiğinde akla çekirdek kodu, korkutucu C işaretçileri ve bilgisayarı yeniden başlatma ritüelleri gelebilir. FUSE sayesinde bunların çoğuna gerek kalmadan Python veya Go ile dosya işlemlerine kendi kurallarımızı uygulayabiliriz. Örneğin içeriğini çalışma anında üreten, buluttaki verileri dosya gibi gösteren ya da tüm yazılanları otomatik şifreleyen bir sistem geliştirebiliriz.
``</p>
<h2 id="fuse-tam-olarak-nedir">FUSE tam olarak nedir?</h2>

<p>FUSE, yani <strong>Filesystem in Userspace</strong>, dosya sistemi mantığının kullanıcı alanında çalışan normal bir program tarafından uygulanmasını sağlayan Linux altyapısıdır. Uygulamalar yine <code class="language-plaintext highlighter-rouge">open</code>, <code class="language-plaintext highlighter-rouge">read</code>, <code class="language-plaintext highlighter-rouge">write</code> ve <code class="language-plaintext highlighter-rouge">stat</code> gibi standart sistem çağrılarını kullanır. Çekirdekteki FUSE sürücüsü bu istekleri bizim sürecimize iletir, cevabı alır ve uygulamaya döndürür.</p>

<p>Akış kabaca şöyledir:</p>

<ol>
  <li>Kullanıcı <code class="language-plaintext highlighter-rouge">/mnt/ozel/merhaba.txt</code> dosyasını okur.</li>
  <li>İşletim sistemi isteği FUSE sürücüsüne gönderir.</li>
  <li>FUSE, Python programımızdaki <code class="language-plaintext highlighter-rouge">read()</code> metodunu çağırır.</li>
  <li>Metodun ürettiği veri kullanıcıya teslim edilir.</li>
</ol>

<p>Bir işlemin toplam gecikmesini basitçe $T = T_{kernel} + T_{iletisim} + T_{uygulama}$ şeklinde düşünebiliriz. Kullanıcı alanına geçiş ek maliyet oluşturduğu için FUSE her senaryoda yerel dosya sistemleri kadar hızlı değildir; fakat geliştirme kolaylığı çoğu özel proje için daha değerlidir.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Çekirdek dosya sistemi</th>
      <th>FUSE dosya sistemi</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Geliştirme dili</td>
      <td>Genellikle C</td>
      <td>Python, Go, Rust, C++</td>
    </tr>
    <tr>
      <td>Hata etkisi</td>
      <td>Sistemi çökertebilir</td>
      <td>Genellikle süreç kapanır</td>
    </tr>
    <tr>
      <td>Performans</td>
      <td>Çok yüksek</td>
      <td>Ek iletişim maliyeti var</td>
    </tr>
    <tr>
      <td>Geliştirme kolaylığı</td>
      <td>Zor</td>
      <td>Görece kolay</td>
    </tr>
    <tr>
      <td>Kullanım alanı</td>
      <td>Genel amaçlı depolama</td>
      <td>Özel ve sanal sistemler</td>
    </tr>
  </tbody>
</table>

<h2 id="python-ile-çalışan-küçük-bir-örnek">Python ile çalışan küçük bir örnek</h2>

<p>Ubuntu tabanlı bir sistemde gerekli paketleri şu şekilde kurabiliriz:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>fuse3 libfuse3-dev
python3 <span class="nt">-m</span> pip <span class="nb">install </span>fusepy
<span class="nb">mkdir</span> <span class="nt">-p</span> /tmp/benimfs
</code></pre></div></div>

<p>Aşağıdaki dosya sistemi yalnızca <code class="language-plaintext highlighter-rouge">merhaba.txt</code> isimli sanal bir dosya sunar. Dosyanın fiziksel diskte bulunması gerekmez; içerik Python tarafından anlık üretilir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">fuse</span> <span class="kn">import</span> <span class="n">FUSE</span><span class="p">,</span> <span class="n">Operations</span>
<span class="kn">import</span> <span class="n">errno</span>
<span class="kn">import</span> <span class="n">os</span>

<span class="k">class</span> <span class="nc">BenimFS</span><span class="p">(</span><span class="n">Operations</span><span class="p">):</span>
    <span class="n">mesaj</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">"</span><span class="s">Merhaba, bu veri sanal dosya sisteminden geliyor!</span><span class="se">\n</span><span class="sh">"</span>

    <span class="k">def</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">fh</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">path</span> <span class="o">==</span> <span class="sh">"</span><span class="s">/</span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">{</span>
                <span class="sh">"</span><span class="s">st_mode</span><span class="sh">"</span><span class="p">:</span> <span class="mo">0o40555</span><span class="p">,</span>
                <span class="sh">"</span><span class="s">st_nlink</span><span class="sh">"</span><span class="p">:</span> <span class="mi">2</span>
            <span class="p">}</span>

        <span class="k">if</span> <span class="n">path</span> <span class="o">==</span> <span class="sh">"</span><span class="s">/merhaba.txt</span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">{</span>
                <span class="sh">"</span><span class="s">st_mode</span><span class="sh">"</span><span class="p">:</span> <span class="mo">0o100444</span><span class="p">,</span>
                <span class="sh">"</span><span class="s">st_nlink</span><span class="sh">"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
                <span class="sh">"</span><span class="s">st_size</span><span class="sh">"</span><span class="p">:</span> <span class="nf">len</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">mesaj</span><span class="p">)</span>
            <span class="p">}</span>

        <span class="k">raise</span> <span class="nc">OSError</span><span class="p">(</span><span class="n">errno</span><span class="p">.</span><span class="n">ENOENT</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="nf">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">.</span><span class="n">ENOENT</span><span class="p">))</span>

    <span class="k">def</span> <span class="nf">readdir</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">fh</span><span class="p">):</span>
        <span class="k">return</span> <span class="p">[</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">..</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">merhaba.txt</span><span class="sh">"</span><span class="p">]</span>

    <span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">flags</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">path</span> <span class="o">!=</span> <span class="sh">"</span><span class="s">/merhaba.txt</span><span class="sh">"</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nc">OSError</span><span class="p">(</span><span class="n">errno</span><span class="p">.</span><span class="n">ENOENT</span><span class="p">,</span> <span class="sh">"</span><span class="s">Dosya bulunamadı</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">flags</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">O_WRONLY</span> <span class="o">|</span> <span class="n">os</span><span class="p">.</span><span class="n">O_RDWR</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nc">OSError</span><span class="p">(</span><span class="n">errno</span><span class="p">.</span><span class="n">EACCES</span><span class="p">,</span> <span class="sh">"</span><span class="s">Dosya salt okunur</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span>

    <span class="k">def</span> <span class="nf">read</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">fh</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">mesaj</span><span class="p">[</span><span class="n">offset</span><span class="p">:</span><span class="n">offset</span> <span class="o">+</span> <span class="n">size</span><span class="p">]</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="nc">FUSE</span><span class="p">(</span><span class="nc">BenimFS</span><span class="p">(),</span> <span class="sh">"</span><span class="s">/tmp/benimfs</span><span class="sh">"</span><span class="p">,</span> <span class="n">foreground</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">getattr()</code> dosyanın türünü, izinlerini ve boyutunu bildirir. <code class="language-plaintext highlighter-rouge">readdir()</code> dizin listesini üretir. <code class="language-plaintext highlighter-rouge">open()</code> erişim kurallarını denetlerken <code class="language-plaintext highlighter-rouge">read()</code> istenen aralıktaki baytları döndürür. Programı <code class="language-plaintext highlighter-rouge">python3 benimfs.py</code> ile çalıştırdıktan sonra başka bir terminalde <code class="language-plaintext highlighter-rouge">cat /tmp/benimfs/merhaba.txt</code> komutu kullanılabilir. Ayırmak için <code class="language-plaintext highlighter-rouge">fusermount3 -u /tmp/benimfs</code> yeterlidir.</p>

<h2 id="tasarımda-dikkat-edilmesi-gerekenler">Tasarımda dikkat edilmesi gerekenler</h2>

<p>Gerçek bir projede eşzamanlı erişim, önbellekleme, dosya tanıtıcıları, kullanıcı kimlikleri ve hata kodları hesaba katılmalıdır. <code class="language-plaintext highlighter-rouge">read()</code> metodunun tüm içeriği değil, yalnızca <code class="language-plaintext highlighter-rouge">offset</code> ile başlayan <code class="language-plaintext highlighter-rouge">size</code> baytı döndürmesi özellikle önemlidir. Aksi hâlde büyük dosyalarda beklenmedik sonuçlar oluşabilir.</p>

<table>
  <thead>
    <tr>
      <th>Amaç</th>
      <th>Uygulanabilecek kural</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Şifreli kasa</td>
      <td><code class="language-plaintext highlighter-rouge">write()</code> sırasında şifreleme</td>
    </tr>
    <tr>
      <td>Bulut sürücüsü</td>
      <td><code class="language-plaintext highlighter-rouge">read()</code> sırasında API isteği</td>
    </tr>
    <tr>
      <td>Denetim sistemi</td>
      <td>Her erişimi günlüğe kaydetme</td>
    </tr>
    <tr>
      <td>Dinamik rapor</td>
      <td>Dosya açıldığında içerik üretme</td>
    </tr>
  </tbody>
</table>

<p>FUSE; prototipler, eğitim araçları, arşiv görüntüleyicileri ve uzak depolama istemcileri için güçlü bir oyun alanıdır. Çekirdeğe dalmadan işletim sisteminin dosya soyutlamasını kullanabilir, hatta bir REST API’yi klasör ve dosyalardan oluşan doğal bir arayüze dönüştürebilirsiniz.</p>

<p><img src="/img/fuse-ile-python-34.svg" alt="fuse-ile-python-34" /></p>]]></content><author><name>Sonsuz Us</name></author><category term="Proje" /><category term="fuse" /><category term="python" /><category term="dosya-sistemi" /><category term="linux" /><category term="sanal-dosya-sistemi" /><category term="sistem-programlama" /><summary type="html"><![CDATA[Dosya sistemi yazmak denildiğinde akla çekirdek kodu, korkutucu C işaretçileri ve bilgisayarı yeniden başlatma ritüelleri gelebilir. FUSE sayesinde bunların çoğuna gerek kalmadan Python veya Go ile dosya işlemlerine kendi kurallarımızı uygulayabiliriz. Örneğin içeriğini çalışma anında üreten, buluttaki verileri dosya gibi gösteren ya da tüm yazılanları otomatik şifreleyen bir sistem geliştirebiliriz.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/fuse-ile-python-34.png" /><media:content medium="image" url="https://program.sonsuz.us/img/fuse-ile-python-34.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">İşletim Sistemlerinde Buddy Bellek Yönetimi: RAM Nasıl Parsellenir?</title><link href="https://program.sonsuz.us/posts/isletim-sistemlerinde-buddy-bellek-yonetimi-ram-nasil-parsellenir/" rel="alternate" type="text/html" title="İşletim Sistemlerinde Buddy Bellek Yönetimi: RAM Nasıl Parsellenir?" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/isletim-sistemlerinde-buddy-bellek-yonetimi-ram-nasil-parsellenir</id><content type="html" xml:base="https://program.sonsuz.us/posts/isletim-sistemlerinde-buddy-bellek-yonetimi-ram-nasil-parsellenir/"><![CDATA[<p><img src="/img/isletim-sistemlerinde-buddy-91.svg" alt="isletim-sistemlerinde-buddy-91" /></p>

<p>Bir işletim sistemi çekirdeği için boş RAM, gelişigüzel dağıtılabilecek dev bir tarla değildir. Sürücüler, süreçler ve çekirdek veri yapıları sürekli farklı büyüklüklerde fiziksel bellek ister. Buddy bellek yönetimi, bu talepleri hızlı karşılamak için belleği ikinin kuvvetleri büyüklüğündeki bloklara ayırır; gerektiğinde blokları ikiye böler, serbest bırakıldıklarında ise yeniden birleştirir.
``</p>

<h2 id="buddy-sisteminin-temel-fikri">Buddy sisteminin temel fikri</h2>

<p>Buddy allocator, yönetilen bellek bölgesini genellikle büyüklüğü $2^N$ olan büyük bir blok olarak kabul eder. Bir istek geldiğinde, isteği karşılayabilecek en küçük ikinin kuvveti seçilir.</p>

<p>Örneğin 13 KB bellek isteniyorsa uygun blok büyüklüğü:</p>

\[2^{\lceil \log_2 13 \rceil} = 2^4 = 16\text{ KB}\]

<p>Sistemde yalnızca 64 KB’lık boş bir blok bulunduğunu düşünelim. Çekirdek bu bloğu sırasıyla böler:</p>

<ul>
  <li>64 KB → iki adet 32 KB</li>
  <li>Bir 32 KB → iki adet 16 KB</li>
  <li>16 KB bloklardan biri isteğe verilir</li>
  <li>Kullanılmayan bloklar boş listelerde tutulur</li>
</ul>

<p>Bir bloğun bölünmesiyle oluşan eşit büyüklükteki iki parçaya <strong>buddy</strong>, yani eş blok denir. Bu iki bloktan biri serbest bırakıldığında diğeri de boşsa tekrar birleşebilirler.</p>

<h2 id="siparişler-ve-boş-listeler">Siparişler ve boş listeler</h2>

<p>Her blok büyüklüğü bir <strong>order</strong> ile temsil edilir. Sayfa büyüklüğü 4 KB ise order 0 bir sayfa, order 1 iki sayfa taşır:</p>

\[\text{blok boyutu} = 2^{order} \times \text{sayfa boyutu}\]

<table>
  <thead>
    <tr>
      <th style="text-align: right">Order</th>
      <th style="text-align: right">Sayfa sayısı</th>
      <th style="text-align: right">Blok büyüklüğü</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">0</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">4 KB</td>
    </tr>
    <tr>
      <td style="text-align: right">1</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">8 KB</td>
    </tr>
    <tr>
      <td style="text-align: right">2</td>
      <td style="text-align: right">4</td>
      <td style="text-align: right">16 KB</td>
    </tr>
    <tr>
      <td style="text-align: right">3</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">32 KB</td>
    </tr>
    <tr>
      <td style="text-align: right">4</td>
      <td style="text-align: right">16</td>
      <td style="text-align: right">64 KB</td>
    </tr>
  </tbody>
</table>

<p>Çekirdek, her order için ayrı bir boş blok listesi tutar. İstenen order boşsa daha üst order’lardan blok aranır. Bulunan büyük blok, hedef boyuta ulaşılıncaya kadar parçalanır. Böylece tüm RAM’i taramak yerine birkaç liste üzerinde işlem yapılır.</p>

<h2 id="eş-blok-nasıl-bulunur">Eş blok nasıl bulunur?</h2>

<p>Buddy yaklaşımının en zarif numarası adres hesabıdır. Boyutu $2^k$ olan bir bloğun başlangıç adresi biliniyorsa buddy adresi şu şekilde hesaplanabilir:</p>

\[\text{buddy} = \text{adres} \oplus 2^k\]

<p>Buradaki $\oplus$ bit düzeyinde XOR işlemidir. İlgili boyut bitini değiştirmek, bloğun eşini doğrudan bulur. Böylece pahalı aramalar yerine hızlı bir adres hesabı yeterli olur.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">size_t</span> <span class="nf">buddy_address</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">address</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">order</span><span class="p">,</span>
                     <span class="kt">size_t</span> <span class="n">page_size</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">size_t</span> <span class="n">block_size</span> <span class="o">=</span> <span class="n">page_size</span> <span class="o">&lt;&lt;</span> <span class="n">order</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">address</span> <span class="o">^</span> <span class="n">block_size</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bu örnek, sayfa boyutunu <code class="language-plaintext highlighter-rouge">order</code> kadar sola kaydırarak blok büyüklüğünü hesaplar ve XOR ile eş bloğun adresini üretir. Gerçek çekirdeklerde fiziksel sayfa numaraları, bölgeler ve ek güvenlik kontrolleri de hesaba katılır.</p>

<h2 id="bölme-ve-birleştirme-döngüsü">Bölme ve birleştirme döngüsü</h2>

<p>Serbest bırakma sırasında allocator aynı order’daki buddy bloğu kontrol eder. Buddy boşsa iki blok birleşir ve bir üst order’a taşınır. Ardından yeni büyük bloğun buddy’si kontrol edilir. Bu işlem birleşme mümkün olmayana kadar sürer.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>free(block, order):
    while order &lt; MAX_ORDER:
        buddy = find_buddy(block, order)
        if buddy kullanımda ise:
            break
        buddy'yi boş listesinden çıkar
        block = adresi daha küçük olan blok
        order = order + 1
    block'u ilgili boş listeye ekle
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Buddy sistemi</th>
      <th>Değişken boyutlu liste</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tahsis hızı</td>
      <td>Yüksek</td>
      <td>Aramaya göre değişir</td>
    </tr>
    <tr>
      <td>Birleştirme</td>
      <td>XOR ile kolay</td>
      <td>Komşu aramak gerekebilir</td>
    </tr>
    <tr>
      <td>İç parçalanma</td>
      <td>Orta</td>
      <td>Genellikle daha düşük</td>
    </tr>
    <tr>
      <td>Dış parçalanma</td>
      <td>Kontrollü</td>
      <td>Daha belirgin olabilir</td>
    </tr>
  </tbody>
</table>

<p>Buddy sistemi parçalanmayı tamamen yok etmez. 17 KB’lık istek 32 KB blok kullanacağından 15 KB <strong>iç parçalanma</strong> oluşur. Buna karşılık standart blok boyutları ve hızlı birleşme, <strong>dış parçalanmayı</strong> azaltır. Linux gibi sistemler bu yöntemi küçük nesneler için kullanılan slab veya SLUB allocator’larla birlikte çalıştırır. Kısacası buddy allocator, RAM’i kusursuz değil; hızlı, öngörülebilir ve çekirdek için yönetilebilir biçimde parseller.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="işletim sistemleri" /><category term="bellek yönetimi" /><category term="buddy allocator" /><category term="ram" /><category term="çekirdek" /><category term="algoritma" /><summary type="html"><![CDATA[Bir işletim sistemi çekirdeği için boş RAM, gelişigüzel dağıtılabilecek dev bir tarla değildir. Sürücüler, süreçler ve çekirdek veri yapıları sürekli farklı büyüklüklerde fiziksel bellek ister. Buddy bellek yönetimi, bu talepleri hızlı karşılamak için belleği ikinin kuvvetleri büyüklüğündeki bloklara ayırır; gerektiğinde blokları ikiye böler, serbest bırakıldıklarında ise yeniden birleştirir.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/isletim-sistemlerinde-buddy-91.png" /><media:content medium="image" url="https://program.sonsuz.us/img/isletim-sistemlerinde-buddy-91.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">İşletim Sistemlerinde Page Cache: Disk Okumaları Neden Sandığımızdan Hızlıdır?</title><link href="https://program.sonsuz.us/posts/isletim-sistemlerinde-page-cache-disk-okumalari-neden-sandigimizdan-hizlidir/" rel="alternate" type="text/html" title="İşletim Sistemlerinde Page Cache: Disk Okumaları Neden Sandığımızdan Hızlıdır?" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/isletim-sistemlerinde-page-cache-disk-okumalari-neden-sandigimizdan-hizlidir</id><content type="html" xml:base="https://program.sonsuz.us/posts/isletim-sistemlerinde-page-cache-disk-okumalari-neden-sandigimizdan-hizlidir/"><![CDATA[<p>Bir dosyayı ilk kez açarken kısa bir bekleme yaşayıp ikinci açılışta şaşırtıcı bir hız görmüş olabilirsiniz. Dosya diskte durduğu hâlde bilgisayarınız nasıl bir anda hızlandı? Çoğu zaman cevap daha hızlı bir disk değil, işletim sisteminin boş RAM alanını akıllıca değerlendirdiği <strong>page cache</strong> mekanizmasıdır.</p>

<p>``</p>

<h2 id="page-cache-nedir">Page cache nedir?</h2>

<p>İşletim sistemi, depolama aygıtındaki dosyaları doğrudan uygulamaların yönetmesine izin vermez. Uygulama bir dosya istediğinde çekirdek, veriyi belirli büyüklükteki bellek sayfaları hâlinde okur. Okunan sayfalar yalnızca programa verilmez; aynı zamanda RAM içindeki page cache alanında tutulur.</p>

<p>Aynı dosya yeniden istendiğinde çekirdek önce önbelleği kontrol eder. Gerekli sayfalar RAM’deyse diske erişmeden sonucu döndürür. Buna <strong>cache hit</strong>, verinin bulunamayıp diskten okunmasına ise <strong>cache miss</strong> denir.</p>

<table>
  <thead>
    <tr>
      <th>Durum</th>
      <th>Veri kaynağı</th>
      <th style="text-align: right">Yaklaşık gecikme</th>
      <th>Sonuç</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Cache hit</td>
      <td>RAM</td>
      <td style="text-align: right">nanosaniye–mikrosaniye</td>
      <td>Çok hızlı okuma</td>
    </tr>
    <tr>
      <td>SSD okuması</td>
      <td>SSD</td>
      <td style="text-align: right">onlarca–yüzlerce mikrosaniye</td>
      <td>Hızlı ama RAM’den yavaş</td>
    </tr>
    <tr>
      <td>HDD okuması</td>
      <td>Mekanik disk</td>
      <td style="text-align: right">milisaniye</td>
      <td>Kafa hareketi nedeniyle yavaş</td>
    </tr>
  </tbody>
</table>

<p><img src="/img/isletim-sistemlerinde-page-33.svg" alt="isletim-sistemlerinde-page-33" /></p>

<p>Basitleştirilmiş toplam erişim süresi şöyle düşünülebilir:</p>

\[T_{ortalama} = H \cdot T_{RAM} + (1-H) \cdot T_{disk}\]

<p>Buradaki $H$, önbellek isabet oranıdır. $H$ değeri 1’e yaklaştıkça ortalama erişim süresi RAM hızına yaklaşır. Küçük görünen bir isabet oranı artışı bile yoğun dosya kullanan sunucularda büyük performans farkı yaratabilir.</p>

<h2 id="boş-ram-neden-gerçekten-boş-değildir">“Boş RAM” neden gerçekten boş değildir?</h2>

<p>Görev yöneticisinde RAM’in büyük bölümünün kullanıldığını görmek her zaman sorun anlamına gelmez. Modern işletim sistemleri kullanılmayan belleği boş bırakmak yerine dosya önbelleği olarak değerlendirir. Çünkü kullanılmayan RAM, performans açısından kaçırılmış bir fırsattır.</p>

<p>Bir uygulama daha fazla belleğe ihtiyaç duyduğunda önbellekteki temiz sayfalar kolayca bırakılabilir. Bu nedenle page cache, uygulamaların belleğini “çalmaz”; ihtiyaç ortaya çıkana kadar boş kapasiteyi ödünç alır.</p>

<p>Linux çıktılarındaki <code class="language-plaintext highlighter-rouge">free</code>, <code class="language-plaintext highlighter-rouge">buff/cache</code> ve özellikle <code class="language-plaintext highlighter-rouge">available</code> değerleri bu yüzden birlikte değerlendirilmelidir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Belleğin uygulamalar ve önbellek arasında nasıl dağıldığını gösterir.</span>
free <span class="nt">-h</span>

<span class="c"># Bir dosyayı okuyarak gerçek süreyi ölçer.</span>
<span class="nb">time cat </span>buyuk-dosya.bin <span class="o">&gt;</span> /dev/null

<span class="c"># Aynı komut ikinci kez çalıştırıldığında veri büyük olasılıkla cache'tedir.</span>
<span class="nb">time cat </span>buyuk-dosya.bin <span class="o">&gt;</span> /dev/null
</code></pre></div></div>

<p>İkinci çalıştırmanın daha hızlı olması, dosyanın değiştiği veya <code class="language-plaintext highlighter-rouge">cat</code> programının özel bir numara yaptığı anlamına gelmez. İlk komut dosya sayfalarını RAM’e taşımış, ikinci komut ise hazır bulunan sayfaları kullanmıştır. Dosya RAM kapasitesinden büyükse veya sistem yoğun bellek baskısı altındaysa fark azalabilir.</p>

<h2 id="yazma-işlemlerinde-de-görev-alır-mı">Yazma işlemlerinde de görev alır mı?</h2>

<p>Evet. Bir uygulama dosyaya yazdığında veri çoğu zaman önce bellekteki sayfalara aktarılır. Değiştirilmiş ve henüz diske gönderilmemiş sayfalara <strong>dirty page</strong> denir. Çekirdek bunları daha sonra toplu biçimde diske yazar. Böylece uygulama her küçük değişiklik için fiziksel disk işlemini beklemez.</p>

<table>
  <thead>
    <tr>
      <th>Sayfa türü</th>
      <th>Anlamı</th>
      <th>Bellekten hemen atılabilir mi?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Clean page</td>
      <td>Diskteki kopyayla aynı</td>
      <td>Evet</td>
    </tr>
    <tr>
      <td>Dirty page</td>
      <td>Henüz diske yazılmamış değişiklik içerir</td>
      <td>Önce yazılmalıdır</td>
    </tr>
  </tbody>
</table>

<p>Bu yaklaşım performansı artırsa da elektrik kesintisi gibi durumlarda henüz kalıcı depolamaya ulaşmamış veriler kaybolabilir. Veritabanları bu nedenle <code class="language-plaintext highlighter-rouge">fsync</code> benzeri çağrılarla kritik verinin diske ulaştığından emin olur.</p>

<h2 id="page-cache-her-şeyi-hızlandırır-mı">Page cache her şeyi hızlandırır mı?</h2>

<p>Page cache sihirli değildir. İlk okuma yine depolama aygıtının hızına bağlıdır. Çok büyük veri kümeleri önbelleğe sığmayabilir; başka uygulamalar da sık kullanılan sayfaları dışarı itebilir. Ayrıca bazı veritabanları ve özel uygulamalar, kendi önbelleklerini yönetmek için doğrudan I/O kullanabilir.</p>

<p>Yine de günlük kullanımda programların hızlı açılması, kaynak kodlarının çabuk derlenmesi ve aynı videonun tekrar akıcı oynatılması çoğunlukla bu görünmez yardımcı sayesinde gerçekleşir. Kısacası yüksek RAM kullanımı her zaman kötü değildir: İşletim sistemi belleği dosyalarla dolduruyorsa, RAM’iniz tembellik etmek yerine gelecekteki disk okumalarını hızlandırıyordur.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="işletim sistemi" /><category term="page cache" /><category term="ram" /><category term="disk" /><category term="linux" /><category term="performans" /><summary type="html"><![CDATA[Bir dosyayı ilk kez açarken kısa bir bekleme yaşayıp ikinci açılışta şaşırtıcı bir hız görmüş olabilirsiniz. Dosya diskte durduğu hâlde bilgisayarınız nasıl bir anda hızlandı? Çoğu zaman cevap daha hızlı bir disk değil, işletim sisteminin boş RAM alanını akıllıca değerlendirdiği page cache mekanizmasıdır.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/isletim-sistemlerinde-page-33.png" /><media:content medium="image" url="https://program.sonsuz.us/img/isletim-sistemlerinde-page-33.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">L1/L2 Cache Dostu Kod: Döngüler ve Veri Yapılarıyla Performansı Katlamak</title><link href="https://program.sonsuz.us/posts/l1l2-cache-dostu-kod-donguler-ve-veri-yapilariyla-performansi-katlamak/" rel="alternate" type="text/html" title="L1/L2 Cache Dostu Kod: Döngüler ve Veri Yapılarıyla Performansı Katlamak" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/l1l2-cache-dostu-kod-donguler-ve-veri-yapilariyla-performansi-katlamak</id><content type="html" xml:base="https://program.sonsuz.us/posts/l1l2-cache-dostu-kod-donguler-ve-veri-yapilariyla-performansi-katlamak/"><![CDATA[<p>Modern işlemciler son derece hızlıdır; ancak ihtiyaç duydukları veriyi ana bellekten beklemek zorunda kaldıklarında pahalı bir spor arabayı trafik ışığında bekletmeye dönüşürler. Döngülerimizi ve veri yapılarımızı işlemci önbelleğine uygun tasarlamak, daha yüksek saat hızına ihtiyaç duymadan programı birkaç kat hızlandırabilir.</p>

<p><img src="/img/l1l2-cache-dostu-54.svg" alt="l1l2-cache-dostu-54" /></p>

<p>``</p>

<h2 id="i̇şlemci-neden-önbelleğe-ihtiyaç-duyar">İşlemci neden önbelleğe ihtiyaç duyar?</h2>

<p>CPU ile RAM aynı hızda çalışmaz. İşlemci bir komutu birkaç çevrimde tamamlayabilirken RAM erişimi yüzlerce çevrim sürebilir. Bu farkı azaltmak için işlemci ile RAM arasına küçük fakat hızlı önbellek katmanları yerleştirilir.</p>

<table>
  <thead>
    <tr>
      <th>Katman</th>
      <th style="text-align: right">Yaklaşık kapasite</th>
      <th style="text-align: right">Göreli hız</th>
      <th>Özellik</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>L1 cache</td>
      <td style="text-align: right">32–128 KB</td>
      <td style="text-align: right">En hızlı</td>
      <td>Genellikle çekirdeğe özeldir</td>
    </tr>
    <tr>
      <td>L2 cache</td>
      <td style="text-align: right">256 KB–2 MB</td>
      <td style="text-align: right">Çok hızlı</td>
      <td>L1’den büyük, biraz daha yavaş</td>
    </tr>
    <tr>
      <td>L3 cache</td>
      <td style="text-align: right">Birkaç MB</td>
      <td style="text-align: right">Orta</td>
      <td>Çekirdekler arasında paylaşılabilir</td>
    </tr>
    <tr>
      <td>RAM</td>
      <td style="text-align: right">GB düzeyi</td>
      <td style="text-align: right">Yavaş</td>
      <td>Büyük veri deposudur</td>
    </tr>
  </tbody>
</table>

<p>Aranan veri önbellekte bulunursa <strong>cache hit</strong>, bulunamazsa <strong>cache miss</strong> oluşur. Basitleştirilmiş ortalama erişim süresi şöyle düşünülebilir:</p>

\[T_{ortalama} = h \cdot T_{cache} + (1-h) \cdot T_{RAM}\]

<p>Burada $h$ cache hit oranıdır. RAM çok daha yavaş olduğundan, $h$ değerindeki küçük bir artış bile toplam süreyi ciddi biçimde azaltabilir.</p>

<h2 id="mekânsal-ve-zamansal-yerellik">Mekânsal ve zamansal yerellik</h2>

<p>Önbellek RAM’den tek bir değişken getirmez; genellikle 64 baytlık bir <strong>cache line</strong> taşır. Bir elemana eriştiğimizde komşu elemanlar da gelir. Ardışık elemanları kullanmak <strong>mekânsal yerellik</strong>, aynı veriyi kısa süre içinde tekrar kullanmak ise <strong>zamansal yerellik</strong> sağlar.</p>

<p>C ve C++ gibi dillerde iki boyutlu diziler satır öncelikli saklanır. Bu nedenle döngü sırası önemlidir:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define N 2048
</span><span class="kt">int</span> <span class="n">matrix</span><span class="p">[</span><span class="n">N</span><span class="p">][</span><span class="n">N</span><span class="p">];</span>
<span class="kt">long</span> <span class="kt">long</span> <span class="n">toplam</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="c1">// Cache dostu: Bellekte ardışık elemanlar okunur.</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">toplam</span> <span class="o">+=</span> <span class="n">matrix</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>İç döngüde <code class="language-plaintext highlighter-rouge">j</code> değiştiği için <code class="language-plaintext highlighter-rouge">matrix[i][j]</code> elemanları art arda okunur. Döngüleri ters çevirmek ise her erişimde farklı bir satıra sıçrayabilir:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Cache açısından zayıf: Büyük adımlarla bellekte gezinir.</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">toplam</span> <span class="o">+=</span> <span class="n">matrix</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Her iki kod da aynı matematiksel sonucu üretir ve zaman karmaşıklıkları $O(N^2)$ olur. Buna rağmen gerçek çalışma süreleri dramatik biçimde farklılaşabilir. Big-O, bellek erişim maliyetini tek başına anlatmaz.</p>

<h2 id="veri-yapısının-dizilişi-aos-ve-soa">Veri yapısının dizilişi: AoS ve SoA</h2>

<p>Bir oyundaki parçacıkları düşünelim:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Parca</span> <span class="p">{</span>
    <span class="kt">float</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">;</span>
    <span class="kt">float</span> <span class="n">hiz</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">renk</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">Parca</span> <span class="n">parcalar</span><span class="p">[</span><span class="mi">1000000</span><span class="p">];</span>
</code></pre></div></div>

<p>Bu yaklaşım <strong>Array of Structures (AoS)</strong> olarak adlandırılır. Yalnızca konumları güncelliyorsak kullanılmayan <code class="language-plaintext highlighter-rouge">renk</code> gibi alanlar da cache line içine taşınır. Alternatif <strong>Structure of Arrays (SoA)</strong> düzeninde alanlar ayrılır:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Parcalar</span> <span class="p">{</span>
    <span class="kt">float</span> <span class="n">x</span><span class="p">[</span><span class="mi">1000000</span><span class="p">];</span>
    <span class="kt">float</span> <span class="n">y</span><span class="p">[</span><span class="mi">1000000</span><span class="p">];</span>
    <span class="kt">float</span> <span class="n">z</span><span class="p">[</span><span class="mi">1000000</span><span class="p">];</span>
    <span class="kt">float</span> <span class="n">hiz</span><span class="p">[</span><span class="mi">1000000</span><span class="p">];</span>
    <span class="kt">int</span> <span class="n">renk</span><span class="p">[</span><span class="mi">1000000</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Düzen</th>
      <th>Güçlü olduğu durum</th>
      <th>Zayıf olduğu durum</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AoS</td>
      <td>Bir nesnenin tüm alanları birlikte kullanılıyorsa</td>
      <td>Yalnızca birkaç alan işleniyorsa</td>
    </tr>
    <tr>
      <td>SoA</td>
      <td>Aynı alan topluca işleniyorsa, SIMD kullanılıyorsa</td>
      <td>Tek nesnenin tüm alanlarına sık erişiliyorsa</td>
    </tr>
  </tbody>
</table>

<h2 id="büyük-veriyi-bloklar-hâlinde-işlemek">Büyük veriyi bloklar hâlinde işlemek</h2>

<p>Matris çarpımı gibi algoritmalarda veri L1 veya L2’ye sığmayabilir. <strong>Blocking</strong> ya da <strong>tiling</strong>, problemi önbelleğe sığan küçük parçalara ayırır:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="kt">int</span> <span class="n">B</span> <span class="o">=</span> <span class="mi">32</span><span class="p">;</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">ii</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">ii</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">ii</span> <span class="o">+=</span> <span class="n">B</span><span class="p">)</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">kk</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">kk</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">kk</span> <span class="o">+=</span> <span class="n">B</span><span class="p">)</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">ii</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">ii</span> <span class="o">+</span> <span class="n">B</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="n">kk</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">kk</span> <span class="o">+</span> <span class="n">B</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span>
                <span class="n">sonuc</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">matrix</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">*</span> <span class="n">vektor</span><span class="p">[</span><span class="n">k</span><span class="p">];</span>
</code></pre></div></div>

<p>Blok boyutu donanıma ve veri tipine göre ölçülerek seçilmelidir. Gereğinden büyük bloklar önbellekten taşar; çok küçük bloklar ise döngü yönetimi maliyetini artırır.</p>

<p>Sonuç olarak ardışık erişim, tekrar kullanılan veriyi yakın tutma, uygun veri düzeni ve bloklama cache hit oranını yükseltir. En doğru optimizasyon için tahminde bulunmak yerine <code class="language-plaintext highlighter-rouge">perf</code>, Valgrind Cachegrind veya işlemci performans sayaçlarıyla cache miss değerlerini ölçmek gerekir. Çünkü hızlı kod yalnızca daha az işlem yapan değil, işlemciyi verisiz bırakmayan koddur.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="işlemci" /><category term="önbellek" /><category term="cache" /><category term="performans" /><category term="bellek" /><category term="c" /><category term="optimizasyon" /><summary type="html"><![CDATA[Modern işlemciler son derece hızlıdır; ancak ihtiyaç duydukları veriyi ana bellekten beklemek zorunda kaldıklarında pahalı bir spor arabayı trafik ışığında bekletmeye dönüşürler. Döngülerimizi ve veri yapılarımızı işlemci önbelleğine uygun tasarlamak, daha yüksek saat hızına ihtiyaç duymadan programı birkaç kat hızlandırabilir.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/l1l2-cache-dostu-54.png" /><media:content medium="image" url="https://program.sonsuz.us/img/l1l2-cache-dostu-54.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Linux Çekirdeğinde Cgroups v2: Kaynak İzolasyonunun Modern Mimarisi</title><link href="https://program.sonsuz.us/posts/linux-cekirdeginde-cgroups-v2-kaynak-izolasyonunun-modern-mimarisi/" rel="alternate" type="text/html" title="Linux Çekirdeğinde Cgroups v2: Kaynak İzolasyonunun Modern Mimarisi" /><published>2026-09-27T00:00:00+00:00</published><updated>2026-09-27T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/linux-cekirdeginde-cgroups-v2-kaynak-izolasyonunun-modern-mimarisi</id><content type="html" xml:base="https://program.sonsuz.us/posts/linux-cekirdeginde-cgroups-v2-kaynak-izolasyonunun-modern-mimarisi/"><![CDATA[<p>Bir konteynere “en fazla 512 MB bellek kullan” veya “işlemcinin yalnızca yarısını tüket” dediğimizde bu kuralları Docker’ın sihirli değneği değil, Linux çekirdeğinin <strong>control groups</strong> mekanizması uygular. Cgroups v2, ilk sürümün yıllar içinde karmaşıklaşan yapısını tek bir hiyerarşide birleştirerek kaynak yönetimini daha tutarlı, güvenli ve öngörülebilir hâle getirir.
``</p>
<h2 id="cgroups-neyi-çözer">Cgroups neyi çözer?</h2>

<p>Cgroups, süreçleri gruplandırır ve CPU, bellek, disk G/Ç ya da süreç sayısı gibi kaynakları bu gruplar üzerinden denetler. Namespace’ler bir konteynerin <em>neyi görebildiğini</em>, cgroups ise <em>ne kadar tüketebildiğini</em> belirler. Dolayısıyla ikisi tamamlayıcıdır: namespace izolasyon perdesiyken cgroups kaynak bütçesidir.</p>

<p>Bir grubun CPU kullanım oranı basitçe</p>

\[R = \frac{Q}{P}\]

<p>ile düşünülebilir. Burada $Q$ izin verilen CPU süresi, $P$ ise periyottur. Örneğin <code class="language-plaintext highlighter-rouge">cpu.max</code> dosyasına <code class="language-plaintext highlighter-rouge">50000 100000</code> yazılması, grubun her 100 milisaniyede en fazla 50 milisaniye CPU kullanabilmesi, yani yaklaşık $R=0.5$ CPU kapasitesi anlamına gelir.</p>

<h2 id="v1-neden-yorucuydu">v1 neden yorucuydu?</h2>

<p>Cgroups v1’de her denetleyici bağımsız bir hiyerarşiye bağlanabiliyordu. Aynı süreç CPU ağacında bir grupta, bellek ağacında başka bir grupta bulunabiliyordu. Esneklik gibi görünen bu model; Docker, systemd ve Kubernetes gibi araçlar aynı makinede çalıştığında yönetim karmaşasına, belirsiz yetki devrine ve tutarsız kaynak davranışlarına yol açıyordu.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Cgroups v1</th>
      <th>Cgroups v2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Hiyerarşi</td>
      <td>Denetleyici başına ayrı olabilir</td>
      <td>Tek ve birleşik</td>
    </tr>
    <tr>
      <td>Süreç organizasyonu</td>
      <td>Farklı ağaçlarda farklı üyelik</td>
      <td>Tek cgroup üyeliği</td>
    </tr>
    <tr>
      <td>Yetki devri</td>
      <td>Karmaşık ve riskli</td>
      <td>Kontrollü delegation</td>
    </tr>
    <tr>
      <td>Bellek denetimi</td>
      <td>Daha parçalı davranış</td>
      <td>Tutarlı limit ve basınç modeli</td>
    </tr>
    <tr>
      <td>Arayüz</td>
      <td>Denetleyiciler arasında düzensiz</td>
      <td>Standartlaştırılmış dosyalar</td>
    </tr>
  </tbody>
</table>

<h2 id="birleşik-hiyerarşi-nasıl-çalışır">Birleşik hiyerarşi nasıl çalışır?</h2>

<p>v2 çoğunlukla <code class="language-plaintext highlighter-rouge">/sys/fs/cgroup</code> altında tek bir ağaç sunar. Bir sürecin kimliği <code class="language-plaintext highlighter-rouge">cgroup.procs</code>, kullanılabilir denetleyiciler <code class="language-plaintext highlighter-rouge">cgroup.controllers</code>, alt gruplara aktarılacak denetleyiciler ise <code class="language-plaintext highlighter-rouge">cgroup.subtree_control</code> üzerinden yönetilir.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Sistemde etkin olan v2 denetleyicilerini gösterir.</span>
<span class="nb">cat</span> /sys/fs/cgroup/cgroup.controllers

<span class="c"># Yeni bir uygulama grubu oluşturur.</span>
<span class="nb">sudo mkdir</span> /sys/fs/cgroup/demo

<span class="c"># Kabuğun PID'sini gruba taşır.</span>
<span class="nb">echo</span> <span class="nv">$$</span> | <span class="nb">sudo tee</span> /sys/fs/cgroup/demo/cgroup.procs
</code></pre></div></div>

<p>v2’nin önemli kurallarından biri <strong>no internal process constraint</strong> ilkesidir. Kaynak denetleyicileri alt gruplara dağıtan bir cgroup, normalde aynı anda doğrudan süreç barındırmaz. Başka bir deyişle dallar organizasyon, yapraklar iş yükü içindir. Bu kural, ebeveyn ve çocuk gruplar arasında kaynak hesabının bulanıklaşmasını önler.</p>

<h2 id="bellek-cpu-ve-süreç-sınırları">Bellek, CPU ve süreç sınırları</h2>

<p>Bellek tarafında <code class="language-plaintext highlighter-rouge">memory.max</code> kesin tavanı, <code class="language-plaintext highlighter-rouge">memory.high</code> ise çekirdeğin agresif geri kazanım uygulamaya başlayacağı yumuşak eşiği ifade eder. Böylece uygulamayı anında öldürmeden önce baskı oluşturmak mümkündür. <code class="language-plaintext highlighter-rouge">memory.current</code> mevcut tüketimi gösterirken <code class="language-plaintext highlighter-rouge">memory.events</code>, OOM gibi olayları sayar.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Kesin bellek limitini 512 MiB yapar.</span>
<span class="nb">echo </span>536870912 | <span class="nb">sudo tee</span> /sys/fs/cgroup/demo/memory.max

<span class="c"># Yaklaşık yarım CPU ve en fazla 100 süreç tanımlar.</span>
<span class="nb">echo</span> <span class="s1">'50000 100000'</span> | <span class="nb">sudo tee</span> /sys/fs/cgroup/demo/cpu.max
<span class="nb">echo </span>100 | <span class="nb">sudo tee</span> /sys/fs/cgroup/demo/pids.max
</code></pre></div></div>

<p>CPU paylaşım önceliği için v1’deki <code class="language-plaintext highlighter-rouge">cpu.shares</code> yerine daha anlaşılır bir aralığa sahip <code class="language-plaintext highlighter-rouge">cpu.weight</code> kullanılır. G/Ç denetimi de <code class="language-plaintext highlighter-rouge">io.max</code> ve <code class="language-plaintext highlighter-rouge">io.weight</code> gibi tutarlı adlarla sunulur.</p>

<h2 id="docker-açısından-anlamı">Docker açısından anlamı</h2>

<p>Docker’daki <code class="language-plaintext highlighter-rouge">--memory</code>, <code class="language-plaintext highlighter-rouge">--cpus</code> ve <code class="language-plaintext highlighter-rouge">--pids-limit</code> seçenekleri sonuçta bu dosyalara dönüştürülür. Modern systemd sürümleri de servisleri cgroup ağacında doğal olarak düzenler. Tek hiyerarşi sayesinde orkestratör, çalışma zamanı ve init sistemi aynı kaynak ağacını paylaşabilir; “bu süreci aslında kim yönetiyor?” bilmecesi büyük ölçüde ortadan kalkar.</p>

<p>Cgroups v2 yalnızca temizlenmiş bir arayüz değildir. Tutarlı muhasebe, güvenli yetki devri, gelişmiş bellek baskısı ve sade hiyerarşi sayesinde konteyner izolasyonunu daha sağlam bir çekirdek sözleşmesine dönüştürür. Kısacası v1 kaynaklara ayrı ayrı kilit vuruyordu; v2 ise bütün binaya düzenli bir erişim planı çiziyor.</p>

<p><img src="/img/linux-cekirdeginde-cgroups-17.svg" alt="linux-cekirdeginde-cgroups-17" /></p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="linux" /><category term="cgroups" /><category term="docker" /><category term="konteyner" /><category term="çekirdek" /><category term="devops" /><summary type="html"><![CDATA[Bir konteynere “en fazla 512 MB bellek kullan” veya “işlemcinin yalnızca yarısını tüket” dediğimizde bu kuralları Docker’ın sihirli değneği değil, Linux çekirdeğinin control groups mekanizması uygular. Cgroups v2, ilk sürümün yıllar içinde karmaşıklaşan yapısını tek bir hiyerarşide birleştirerek kaynak yönetimini daha tutarlı, güvenli ve öngörülebilir hâle getirir.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/linux-cekirdeginde-cgroups-17.png" /><media:content medium="image" url="https://program.sonsuz.us/img/linux-cekirdeginde-cgroups-17.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>