<?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://sonsuzus.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sonsuzus.github.io/" rel="alternate" type="text/html" /><updated>2026-07-21T07:13:25+00:00</updated><id>https://sonsuzus.github.io/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">Hexedit ile Ext4/Btrfs İmajından Silinmiş Dosya Kurtarma: Inode Dedektifliği</title><link href="https://sonsuzus.github.io/posts/hexedit-ile-ext4btrfs-imajindan-silinmis-dosya-kurtarma-inode-dedektifligi/" rel="alternate" type="text/html" title="Hexedit ile Ext4/Btrfs İmajından Silinmiş Dosya Kurtarma: Inode Dedektifliği" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/hexedit-ile-ext4btrfs-imajindan-silinmis-dosya-kurtarma-inode-dedektifligi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/hexedit-ile-ext4btrfs-imajindan-silinmis-dosya-kurtarma-inode-dedektifligi/"><![CDATA[<p>Bir dosya silindiğinde çoğu zaman verinin kendisi anında buharlaşmaz; dosya sisteminin “bu alan artık boş” dediği muhasebe kayıtları değişir. İşte ham disk imajını <code class="language-plaintext highlighter-rouge">hexedit</code> ile açıp inode ve blok adreslerini elle okumak, biraz arkeoloji biraz dedektiflik gibidir: kumların altından byte byte hikâye çıkarırız.
``</p>

<blockquote>
  <p>Not: Bu yazı adli analiz ve kendi verinizi kurtarma senaryoları içindir. Gerçek diskte değil, <strong>imaj kopyası</strong> üzerinde çalışın. Yazma işlemi, kurtarılabilir veriyi ezebilir.</p>
</blockquote>

<h2 id="silinme-ne-demek">Silinme Ne Demek?</h2>

<p>Dosya sistemi bir dosyayı genellikle iki parçayla takip eder: <strong>metadata</strong> ve <strong>data</strong>. Metadata; isim, izin, zaman damgası, inode numarası ve verinin hangi bloklarda durduğunu söyler. Data ise dosyanın gerçek içeriğidir.</p>

<p>Basitleştirilmiş denklem şöyle düşünülebilir:</p>

\[\text{Dosya} = \text{İsim Kaydı} + \text{Inode} + \text{Veri Blokları}\]

<p>Silme sırasında dizin girdisi kaldırılır, inode’un bağlantı sayısı düşer ve bloklar “boş” kabul edilir. Ama bloklar üzerine yeni veri yazılmadıysa hâlâ oradadır.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Ext4</th>
      <th>Btrfs</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Metadata modeli</td>
      <td>Inode tablosu ve blok grupları</td>
      <td>B-tree tabanlı, COW mimarisi</td>
    </tr>
    <tr>
      <td>Elle yorumlama</td>
      <td>Daha öngörülebilir</td>
      <td>Daha karmaşık</td>
    </tr>
    <tr>
      <td>Silinmiş veri şansı</td>
      <td>Üzerine yazılmadıysa iyi</td>
      <td>Snapshot/COW varsa bazen çok iyi</td>
    </tr>
    <tr>
      <td>Zorluk seviyesi</td>
      <td>Orta</td>
      <td>İleri</td>
    </tr>
  </tbody>
</table>

<p>Bu yazıda ana örneği Ext4 üzerinden kuracağız; Btrfs için de mantığı karşılaştıracağız.</p>

<h2 id="güvenli-laboratuvar-hazırlığı">Güvenli Laboratuvar Hazırlığı</h2>

<p>Önce diskten ya da bölümden imaj alın:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo dd </span><span class="k">if</span><span class="o">=</span>/dev/sdb1 <span class="nv">of</span><span class="o">=</span>disk.img <span class="nv">bs</span><span class="o">=</span>4M <span class="nv">status</span><span class="o">=</span>progress <span class="nv">conv</span><span class="o">=</span>noerror,sync
<span class="nb">cp </span>disk.img lab.img
hexedit lab.img
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">hexedit</code> içinde gezinirken unutmayın: sayılar çoğu zaman <strong>little-endian</strong> saklanır. Yani ekranda <code class="language-plaintext highlighter-rouge">00 10 00 00</code> görüyorsanız bu değer <code class="language-plaintext highlighter-rouge">0x00001000</code>, yani 4096 olabilir.</p>

<h2 id="ext4-inode-adresini-hesaplamak">Ext4 Inode Adresini Hesaplamak</h2>

<p>Ext4’te süperblok genelde bölüm başlangıcından 1024 byte sonra başlar. Buradan blok boyutu, inode boyutu, grup başına inode sayısı gibi bilgiler okunur.</p>

<p>Blok boyutu formülü:</p>

\[\text{block\_size} = 1024 \times 2^{s\_log\_block\_size}\]

<p>Bir inode’un disk üzerindeki konumu için:</p>

\[\text{group} = \left\lfloor \frac{inode - 1}{inodes\_per\_group} \right\rfloor\]

\[\text{index} = (inode - 1) \bmod inodes\_per\_group\]

\[\text{inode\_offset} = inode\_table\_block \times block\_size + index \times inode\_size\]

<p>Örneğin inode numarası 131083, blok boyutu 4096, inode boyutu 256 ve grup başına inode sayısı 8192 ise ilgili blok grubunu ve inode tablosundaki yerini bu formüllerle buluruz.</p>

<h2 id="inode-i̇çindeki-i̇puçları">Inode İçindeki İpuçları</h2>

<p>Ext4 inode yapısında önemli alanlar şunlardır:</p>

<table>
  <thead>
    <tr>
      <th>Alan</th>
      <th style="text-align: right">Tipik offset</th>
      <th>Anlamı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">i_mode</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">0x00</code></td>
      <td>Dosya türü ve izinler</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">i_size_lo</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">0x04</code></td>
      <td>Dosya boyutu düşük 32 bit</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">i_links_count</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">0x1A</code></td>
      <td>Bağlantı sayısı</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">i_blocks_lo</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">0x1C</code></td>
      <td>Ayrılmış blok bilgisi</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">i_block</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">0x28</code></td>
      <td>Extent veya blok işaretçileri</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">i_dtime</code></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">0x14</code></td>
      <td>Silinme zamanı</td>
    </tr>
  </tbody>
</table>

<p>Silinmiş dosyada <code class="language-plaintext highlighter-rouge">i_links_count</code> sıfır olabilir ve <code class="language-plaintext highlighter-rouge">i_dtime</code> doludur. Asıl hazine ise <code class="language-plaintext highlighter-rouge">i_block</code> alanındadır. Modern Ext4 çoğunlukla extent kullanır. <code class="language-plaintext highlighter-rouge">i_block</code> alanında <code class="language-plaintext highlighter-rouge">0a f3</code> görürseniz, bu little-endian <code class="language-plaintext highlighter-rouge">0xf30a</code> extent sihirli değeridir.</p>

<p>Extent başlığı kabaca şöyledir:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct ext4_extent_header {
  __le16 eh_magic;    // 0xf30a
  __le16 eh_entries;  // kaç kayıt var
  __le16 eh_max;
  __le16 eh_depth;    // 0 ise yaprak
  __le32 eh_generation;
}
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">eh_depth = 0</code> ise hemen ardından gerçek extent kayıtları gelir:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct ext4_extent {
  __le32 ee_block;     // dosya içi mantıksal blok
  __le16 ee_len;       // uzunluk
  __le16 ee_start_hi;  // fiziksel blok üst bitler
  __le32 ee_start_lo;  // fiziksel blok alt bitler
}
</code></pre></div></div>

<p>Fiziksel blok hesabı:</p>

\[\text{physical} = (ee\_start\_hi \ll 32) + ee\_start\_lo\]

<p>Byte offset ise:</p>

\[\text{byte\_offset} = \text{physical} \times \text{block\_size}\]

<h2 id="veriyi-çekmek">Veriyi Çekmek</h2>

<p>Diyelim extent bize fiziksel blok <code class="language-plaintext highlighter-rouge">250000</code>, uzunluk <code class="language-plaintext highlighter-rouge">12</code>, blok boyutu <code class="language-plaintext highlighter-rouge">4096</code> dedi. Dosyayı şöyle çıkarabiliriz:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">dd </span><span class="k">if</span><span class="o">=</span>lab.img <span class="nv">of</span><span class="o">=</span>recovered.bin <span class="nv">bs</span><span class="o">=</span>4096 <span class="nv">skip</span><span class="o">=</span>250000 <span class="nv">count</span><span class="o">=</span>12 <span class="nv">status</span><span class="o">=</span>progress
</code></pre></div></div>

<p>Bu komut imajdan 12 blok okur ve <code class="language-plaintext highlighter-rouge">recovered.bin</code> dosyasına yazar. Dosya parçalıysa her extent ayrı çıkarılır, sonra mantıksal blok sırasına göre birleştirilir.</p>

<h2 id="btrfs-neden-daha-çetrefilli">Btrfs Neden Daha Çetrefilli?</h2>

<p>Btrfs’te klasik “inode tablosu” beklentisi boşa düşer. Metadata B-tree yapılarında tutulur; ayrıca Copy-on-Write sayesinde eski sürümlerin izleri kalabilir. Bu harika bir kurtarma şansı sunar ama elle hex ile çözümlemek Ext4’e göre daha zahmetlidir.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th>Ext4 karşılığı</th>
      <th>Btrfs karşılığı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Inode yeri</td>
      <td>Hesaplanabilir tablo</td>
      <td>B-tree item</td>
    </tr>
    <tr>
      <td>Veri adresi</td>
      <td>Extent alanı</td>
      <td>File extent item</td>
    </tr>
    <tr>
      <td>Eski veri</td>
      <td>Üzerine yazılmadıysa</td>
      <td>COW/snapshot ile kalabilir</td>
    </tr>
    <tr>
      <td>Elle kurtarma</td>
      <td>Formülle mümkün</td>
      <td>Ağaç yapısı takibi gerekir</td>
    </tr>
  </tbody>
</table>

<h2 id="sonuç">Sonuç</h2>

<p>Ext4’te manuel kurtarma; süperbloktan parametreleri okumak, inode adresini hesaplamak, extent kayıtlarını yorumlamak ve fiziksel blokları <code class="language-plaintext highlighter-rouge">dd</code> ile çekmekten ibarettir. Btrfs ise daha modern ve güçlüdür ama elle analizde daha fazla ağaç gezmeyi gerektirir. Kısacası: Ext4 düz bir harita, Btrfs ise üç boyutlu labirenttir. İkisi de çözülebilir; yeter ki byte’lar hâlâ yerinde olsun.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="ext4" /><category term="btrfs" /><category term="forensics" /><category term="hexedit" /><category term="inode" /><summary type="html"><![CDATA[Bir dosya silindiğinde çoğu zaman verinin kendisi anında buharlaşmaz; dosya sisteminin “bu alan artık boş” dediği muhasebe kayıtları değişir. İşte ham disk imajını hexedit ile açıp inode ve blok adreslerini elle okumak, biraz arkeoloji biraz dedektiflik gibidir: kumların altından byte byte hikâye çıkarırız.]]></summary></entry><entry><title type="html">Kabuk Betikleriyle Mini Konteyner Motoru: Namespace ve Cgroup Atölyesi</title><link href="https://sonsuzus.github.io/posts/kabuk-betikleriyle-mini-konteyner-motoru-namespace-ve-cgroup-atolyesi/" rel="alternate" type="text/html" title="Kabuk Betikleriyle Mini Konteyner Motoru: Namespace ve Cgroup Atölyesi" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/kabuk-betikleriyle-mini-konteyner-motoru-namespace-ve-cgroup-atolyesi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/kabuk-betikleriyle-mini-konteyner-motoru-namespace-ve-cgroup-atolyesi/"><![CDATA[<p>Konteyner denince akla hemen Docker gelir; ama sahnenin arkasında sihirli bir ejderha değil, Linux çekirdeğinin çok ciddi iki mekanizması çalışır: ad alanları ve kontrol grupları. Bu yazıda hazır bir konteyner motoru kullanmadan, kabuk betikleriyle küçük ama öğretici bir motor kuracağız. Amaç üretim ortamına rakip çıkarmak değil; kaputun altındaki pistonları tek tek görmek.
``</p>

<p>Önce teoriyi netleştirelim. Linux namespace, bir sürece gerçek sistemin yalnızca belirli bir görünümünü verir. Yani süreç kendini ayrı bir makinedeymiş gibi hisseder. Cgroup ise o sürecin ne kadar CPU, bellek veya süreç sayısı kullanabileceğini sınırlar. Basit denklemle: konteyner hissi = izolasyon + kaynak limiti + ayrı dosya sistemi. Bunu şöyle yazabiliriz: $K = N + C + R$, burada $N$ namespace, $C$ cgroup, $R$ rootfs anlamına gelir.</p>

<table>
  <thead>
    <tr>
      <th>Mekanizma</th>
      <th>Ne yapar?</th>
      <th>Günlük benzetme</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>PID namespace</td>
      <td>Süreç numaralarını izole eder</td>
      <td>Her sınıfta ayrı yoklama listesi</td>
    </tr>
    <tr>
      <td>Mount namespace</td>
      <td>Bağlama noktalarını ayırır</td>
      <td>Her odanın kendi raf düzeni</td>
    </tr>
    <tr>
      <td>UTS namespace</td>
      <td>Hostname bilgisini ayırır</td>
      <td>Her evin farklı kapı tabelası</td>
    </tr>
    <tr>
      <td>Network namespace</td>
      <td>Ağ arayüzlerini ayırır</td>
      <td>Ayrı mahalle yolları</td>
    </tr>
    <tr>
      <td>Cgroup v2</td>
      <td>Kaynak tüketimini sınırlar</td>
      <td>Elektrik, su, internet kotası</td>
    </tr>
  </tbody>
</table>

<p>Bir konteynerin kök dosya sistemine ihtiyacı vardır. Debian tabanlı küçük bir rootfs hazırlamak için örnek:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>debootstrap <span class="nt">--variant</span><span class="o">=</span>minbase bookworm ./rootfs http://deb.debian.org/debian
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> ./rootfs/<span class="o">{</span>proc,sys,tmp<span class="o">}</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">rootfs</code>, konteynerin kendi dünyasıdır. <code class="language-plaintext highlighter-rouge">chroot</code> ile bir sürecin kök dizinini buraya çevirebiliriz; fakat tek başına <code class="language-plaintext highlighter-rouge">chroot</code> güvenlik sınırı değildir. Asıl izolasyonu <code class="language-plaintext highlighter-rouge">unshare</code> komutu ile namespace oluşturarak sağlarız.</p>

<p>Şimdi küçük motorumuzun iskeletini yazalım:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> <span class="o">&gt;</span> mini-run.sh <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">SH</span><span class="sh">'
#!/usr/bin/env bash
set -euo pipefail

ROOTFS=</span><span class="k">${</span><span class="nv">ROOTFS</span><span class="k">:-</span><span class="p">./rootfs</span><span class="k">}</span><span class="sh">
ID=</span><span class="k">${</span><span class="nv">1</span><span class="k">:-</span><span class="nv">demo</span><span class="k">}</span><span class="sh">
CG=/sys/fs/cgroup/mini-</span><span class="nv">$ID</span><span class="sh">

mkdir -p </span><span class="nv">$CG</span><span class="sh">
echo +cpu +memory +pids &gt; /sys/fs/cgroup/cgroup.subtree_control 2&gt;/dev/null || true
echo '50000 100000' &gt; </span><span class="nv">$CG</span><span class="sh">/cpu.max
echo </span><span class="k">$((</span><span class="m">128</span><span class="o">*</span><span class="m">1024</span><span class="o">*</span><span class="m">1024</span><span class="k">))</span><span class="sh"> &gt; </span><span class="nv">$CG</span><span class="sh">/memory.max
echo 64 &gt; </span><span class="nv">$CG</span><span class="sh">/pids.max

unshare --fork --pid --mount --uts --ipc --net --mount-proc </span><span class="se">\</span><span class="sh">
  chroot </span><span class="nv">$ROOTFS</span><span class="sh"> /bin/bash -lc 'hostname mini; mount -t proc proc /proc; exec /bin/bash' &amp;

CHILD=</span><span class="nv">$!</span><span class="sh">
echo </span><span class="nv">$CHILD</span><span class="sh"> &gt; </span><span class="nv">$CG</span><span class="sh">/cgroup.procs
wait </span><span class="nv">$CHILD</span><span class="sh">
rm -rf </span><span class="nv">$CG</span><span class="sh">
</span><span class="no">SH

</span><span class="nb">chmod</span> +x mini-run.sh
<span class="nb">sudo</span> ./mini-run.sh ilk-konteyner
</code></pre></div></div>

<p>Bu betik birkaç kritik şey yapar. Önce cgroup dizini açar, sonra CPU, bellek ve süreç sayısı sınırı koyar. <code class="language-plaintext highlighter-rouge">cpu.max</code> satırındaki <code class="language-plaintext highlighter-rouge">50000 100000</code>, her 100000 mikrosaniyelik periyotta 50000 mikrosaniye CPU hakkı demektir. Yani $oran = 50000 / 100000 = 0.5$; kabaca yarım CPU çekirdeği. Bellek sınırı ise $128 * 1024 * 1024$ bayt, yani 128 MB olur.</p>

<p><code class="language-plaintext highlighter-rouge">unshare</code> tarafı daha eğlenceli. <code class="language-plaintext highlighter-rouge">--pid</code> ile içerideki kabuk kendini yeni bir süreç ağacında görür. <code class="language-plaintext highlighter-rouge">--mount</code> sayesinde mount değişiklikleri dışarı sızmaz. <code class="language-plaintext highlighter-rouge">--uts</code> hostname değiştirmeye izin verir. <code class="language-plaintext highlighter-rouge">--net</code> yeni ve boş bir ağ namespace açar; bu yüzden konteyner ilk anda internetsizdir. Gerçek motorlar burada veth çifti, bridge ve NAT kurar.</p>

<table>
  <thead>
    <tr>
      <th>Eksik parça</th>
      <th>Bizde durum</th>
      <th>Docker gibi motorlarda</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Image katmanları</td>
      <td>Yok, tek rootfs</td>
      <td>OverlayFS katmanları</td>
    </tr>
    <tr>
      <td>Ağ</td>
      <td>Boş namespace</td>
      <td>Bridge, veth, iptables veya nftables</td>
    </tr>
    <tr>
      <td>Güvenlik</td>
      <td>Temel izolasyon</td>
      <td>Seccomp, AppArmor, capability azaltma</td>
    </tr>
    <tr>
      <td>Yaşam döngüsü</td>
      <td>Basit wait</td>
      <td>start, stop, logs, exec</td>
    </tr>
  </tbody>
</table>

<p>Ağ eklemek istersen fikir şudur: host tarafında bir bridge oluşturulur, konteyner namespace içine bir veth ucu taşınır, IP verilir ve NAT açılır. Bunu elle yapmak öğreticidir ama dağıtıma göre komutlar değişebilir.</p>

<p>Önemli uyarı: Bu betiği root yetkisiyle çalıştırırsın ve bu bir öğrenme aracıdır. Güvenli konteyner motoru yazmak; capability düşürme, salt okunur mount, kullanıcı namespace, seccomp profili ve dikkatli cleanup gerektirir.</p>

<p>Sonuç olarak konteyner, gizemli bir kutu değil; iyi düzenlenmiş bir Linux illüzyonudur. Namespace sürece ayrı bir evren gösterir, cgroup o evrende ne kadar kaynak yakabileceğini belirler, rootfs ise dekoru tamamlar. Docker veya Podman kullandığında artık sadece komut çalıştırmış olmayacaksın; çekirdeğin hangi düğmelerine basıldığını da zihninde göreceksin.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="linux" /><category term="container" /><category term="bash" /><category term="namespace" /><category term="cgroup" /><summary type="html"><![CDATA[Konteyner denince akla hemen Docker gelir; ama sahnenin arkasında sihirli bir ejderha değil, Linux çekirdeğinin çok ciddi iki mekanizması çalışır: ad alanları ve kontrol grupları. Bu yazıda hazır bir konteyner motoru kullanmadan, kabuk betikleriyle küçük ama öğretici bir motor kuracağız. Amaç üretim ortamına rakip çıkarmak değil; kaputun altındaki pistonları tek tek görmek.]]></summary></entry><entry><title type="html">Linux’ta /dev/uinput ile Sanal Fare ve Klavye Olayları Göndermek</title><link href="https://sonsuzus.github.io/posts/linuxta-devuinput-ile-sanal-fare-ve-klavye-olaylari-gondermek/" rel="alternate" type="text/html" title="Linux’ta /dev/uinput ile Sanal Fare ve Klavye Olayları Göndermek" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/linuxta-devuinput-ile-sanal-fare-ve-klavye-olaylari-gondermek</id><content type="html" xml:base="https://sonsuzus.github.io/posts/linuxta-devuinput-ile-sanal-fare-ve-klavye-olaylari-gondermek/"><![CDATA[<p>Bazen fiziksel bir fareye ya da klavyeye dokunmadan sisteme giriş olayı göndermek isteriz: test otomasyonu, erişilebilirlik araçları, kiosk sistemleri, oyun botu prototipleri veya uzaktan kontrol yazılımları buna örnektir. Linux tarafında bu işin en temiz yollarından biri <code class="language-plaintext highlighter-rouge">/dev/uinput</code> aygıtıdır. Kısaca uinput, kullanıcı alanındaki bir programın çekirdeğe “Ben bir input aygıtıyım” demesini ve gerçek donanım gibi olay üretmesini sağlar.
``
Linux input mimarisinde olaylar genellikle <code class="language-plaintext highlighter-rouge">/dev/input/eventX</code> dosyaları üzerinden okunur. Gerçek klavye, fare, gamepad gibi donanımlar çekirdeğe olay üretir; masaüstü ortamı da bunları tüketir. <code class="language-plaintext highlighter-rouge">/dev/uinput</code> ise bu akışı tersine çevirir: kullanıcı alanındaki program çekirdeğe sanal cihaz kaydeder ve olayları içeri enjekte eder.</p>

<p>Teorik olarak bir giriş olayı şu üçlüyle düşünülebilir:</p>

\[Olay = (tip, kod, değer)\]

<p>Örneğin klavyede A tuşuna basmak için tip <code class="language-plaintext highlighter-rouge">EV_KEY</code>, kod <code class="language-plaintext highlighter-rouge">KEY_A</code>, değer <code class="language-plaintext highlighter-rouge">1</code> gönderilir. Tuşu bırakmak için değer <code class="language-plaintext highlighter-rouge">0</code> kullanılır. Fare hareketi ise <code class="language-plaintext highlighter-rouge">EV_REL</code>, <code class="language-plaintext highlighter-rouge">REL_X</code>, <code class="language-plaintext highlighter-rouge">REL_Y</code> gibi göreli eksen olaylarıyla temsil edilir. Her olay grubundan sonra <code class="language-plaintext highlighter-rouge">EV_SYN</code> gönderilerek “bu paket tamamlandı” denir.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th>Anlamı</th>
      <th>Örnek</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">EV_KEY</code></td>
      <td>Tuş veya buton olayı</td>
      <td><code class="language-plaintext highlighter-rouge">KEY_ENTER</code>, <code class="language-plaintext highlighter-rouge">BTN_LEFT</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">EV_REL</code></td>
      <td>Göreli hareket</td>
      <td><code class="language-plaintext highlighter-rouge">REL_X</code>, <code class="language-plaintext highlighter-rouge">REL_Y</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">EV_ABS</code></td>
      <td>Mutlak koordinat</td>
      <td>Dokunmatik ekran ekseni</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">EV_SYN</code></td>
      <td>Olayları senkronize eder</td>
      <td><code class="language-plaintext highlighter-rouge">SYN_REPORT</code></td>
    </tr>
  </tbody>
</table>

<p>Bu yapı fiziksel donanımla sanal donanım arasında hoş bir soyutlama sağlar. Masaüstü ortamı çoğu zaman olayın gerçek fareden mi yoksa sanal aygıttan mı geldiğini umursamaz. Matematiksel olarak fare imlecinin göreli hareketini şöyle düşünebiliriz:</p>

\[x_{yeni} = x_{eski} + \Delta x, \quad y_{yeni} = y_{eski} + \Delta y\]

<p>Yani sanal fare <code class="language-plaintext highlighter-rouge">REL_X = 30</code>, <code class="language-plaintext highlighter-rouge">REL_Y = 10</code> gönderirse imleç mevcut konumdan sağa 30, aşağı 10 birim hareket eder.</p>

<p>Pratik tarafta Python için <code class="language-plaintext highlighter-rouge">evdev</code> kütüphanesi işleri oldukça kolaylaştırır. Önce kurulum yapalım:</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>python3-evdev
</code></pre></div></div>

<p>Ayrıca <code class="language-plaintext highlighter-rouge">/dev/uinput</code> erişimi gerekir. Çoğu sistemde root yetkisiyle çalıştırmak yeterlidir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>python3 virtual_input.py
</code></pre></div></div>

<p>Aşağıdaki örnek hem sanal klavye hem de sanal fare özellikleri olan bir cihaz oluşturur. Önce A tuşuna basıp bırakır, ardından fareyi hareket ettirip sol tık gönderir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">evdev</span> <span class="kn">import</span> <span class="n">UInput</span><span class="p">,</span> <span class="n">ecodes</span> <span class="k">as</span> <span class="n">e</span>
<span class="kn">import</span> <span class="n">time</span>

<span class="n">capabilities</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">e</span><span class="p">.</span><span class="n">EV_KEY</span><span class="p">:</span> <span class="p">[</span>
        <span class="n">e</span><span class="p">.</span><span class="n">KEY_A</span><span class="p">,</span>
        <span class="n">e</span><span class="p">.</span><span class="n">KEY_ENTER</span><span class="p">,</span>
        <span class="n">e</span><span class="p">.</span><span class="n">BTN_LEFT</span>
    <span class="p">],</span>
    <span class="n">e</span><span class="p">.</span><span class="n">EV_REL</span><span class="p">:</span> <span class="p">[</span>
        <span class="n">e</span><span class="p">.</span><span class="n">REL_X</span><span class="p">,</span>
        <span class="n">e</span><span class="p">.</span><span class="n">REL_Y</span>
    <span class="p">]</span>
<span class="p">}</span>

<span class="k">with</span> <span class="nc">UInput</span><span class="p">(</span><span class="n">capabilities</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">blog-virtual-keyboard-mouse</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">ui</span><span class="p">:</span>
    <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

    <span class="c1"># A tuşuna bas ve bırak
</span>    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_KEY</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">KEY_A</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_SYN</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">SYN_REPORT</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
    <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_KEY</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">KEY_A</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_SYN</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">SYN_REPORT</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>

    <span class="c1"># Fareyi sağa ve aşağı hareket ettir
</span>    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_REL</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">REL_X</span><span class="p">,</span> <span class="mi">80</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_REL</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">REL_Y</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_SYN</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">SYN_REPORT</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>

    <span class="c1"># Sol tık
</span>    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_KEY</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">BTN_LEFT</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_SYN</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">SYN_REPORT</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
    <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mf">0.05</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_KEY</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">BTN_LEFT</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">EV_SYN</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">SYN_REPORT</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>Buradaki <code class="language-plaintext highlighter-rouge">capabilities</code> sözlüğü sanal cihazın hangi olayları üretebileceğini bildirir. Eğer <code class="language-plaintext highlighter-rouge">KEY_A</code> tanımlamazsanız sonradan A tuşu göndermeye çalışmak anlamlı olmaz; çünkü çekirdek bu aygıtın böyle bir yeteneği olduğunu bilmez.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Avantaj</th>
      <th>Dezavantaj</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>X11 araçları</td>
      <td>Kolay kullanım</td>
      <td>Wayland ve konsolda sınırlı</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">/dev/uinput</code></td>
      <td>Çekirdek seviyesinde genel çözüm</td>
      <td>Yetki ve aygıt tanımı gerekir</td>
    </tr>
    <tr>
      <td>Donanım emülatörü</td>
      <td>Çok gerçekçi</td>
      <td>Ek cihaz veya firmware ister</td>
    </tr>
  </tbody>
</table>

<p>Güvenlik tarafı önemlidir. Sanal klavye oluşturabilen bir program parola alanına da yazı yazabilir. Bu yüzden <code class="language-plaintext highlighter-rouge">/dev/uinput</code> erişimi herkese verilmemelidir. Geliştirme ortamında root kullanmak pratik olsa da üretimde özel grup, udev kuralı ve minimum yetki prensibi tercih edilmelidir.</p>

<p>Sonuç olarak <code class="language-plaintext highlighter-rouge">/dev/uinput</code>, Linux’ta fiziksel donanım olmadan giriş simülasyonu yapmak için güçlü ve esnek bir kapıdır. Mantığı kavradığınızda olay tipleri, kodlar ve değerlerden oluşan küçük paketlerle sisteme “gerçekmiş gibi” davranan sanal aygıtlar tanımlayabilirsiniz. Bu da test otomasyonundan erişilebilirlik çözümlerine kadar oldukça geniş ve eğlenceli bir oyun alanı açar.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="linux" /><category term="uinput" /><category term="input-simulation" /><category term="python" /><category term="evdev" /><summary type="html"><![CDATA[Bazen fiziksel bir fareye ya da klavyeye dokunmadan sisteme giriş olayı göndermek isteriz: test otomasyonu, erişilebilirlik araçları, kiosk sistemleri, oyun botu prototipleri veya uzaktan kontrol yazılımları buna örnektir. Linux tarafında bu işin en temiz yollarından biri /dev/uinput aygıtıdır. Kısaca uinput, kullanıcı alanındaki bir programın çekirdeğe “Ben bir input aygıtıyım” demesini ve gerçek donanım gibi olay üretmesini sağlar.]]></summary></entry><entry><title type="html">LUKS İçin Sahte Masaüstü Tuzak Sistemi: Savunmacı Aldatma Tasarımı</title><link href="https://sonsuzus.github.io/posts/luks-icin-sahte-masaustu-tuzak-sistemi-savunmaci-aldatma-tasarimi/" rel="alternate" type="text/html" title="LUKS İçin Sahte Masaüstü Tuzak Sistemi: Savunmacı Aldatma Tasarımı" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/luks-icin-sahte-masaustu-tuzak-sistemi-savunmaci-aldatma-tasarimi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/luks-icin-sahte-masaustu-tuzak-sistemi-savunmaci-aldatma-tasarimi/"><![CDATA[<p>Bir saldırganın cihazınızı ele geçirdiğini ve LUKS parolasını denediğini düşünün. Normalde yanlış parola yalnızca “erişim yok” demektir; fakat savunmacı aldatma yaklaşımında sistem, belirli sayıda hatadan sonra sanki parola kabul edilmiş gibi davranıp izole bir sahte masaüstü başlatabilir. Bu yazıda bunu kötüye kullanım değil, yalnızca kendi cihazınızda ve laboratuvar ortamında uygulanacak bir güvenlik tasarımı olarak ele alacağız.
``</p>

<p>Önce kritik gerçeği netleştirelim: LUKS yanlış parolayla gerçek diski açamaz. Kriptografik olarak “yanlış ama yine de açılmış gibi” yapmak mümkün değildir; çünkü anahtar türetme sonucu bambaşka olur. Basitçe:</p>

\[K = KDF(parola, salt)\]

<p>Doğru parola için $K = K_{gerçek}$ olurken, yanlış parola için pratikte rastgele bir $K’$ oluşur. Yani sahte masaüstü, gerçek veriyi açmak yerine ayrı ve izole bir “decoy” sistemden başlatılmalıdır.</p>

<h2 id="genel-mimari">Genel mimari</h2>

<p>Tasarlayacağımız sistem üç parçadan oluşur:</p>

<ol>
  <li><strong>Gerçek LUKS bölüm</strong>: Asıl işletim sistemi ve veriler burada durur.</li>
  <li><strong>Decoy işletim sistemi</strong>: Ayrı bir bölümde veya salt-okunur imajda bulunan sahte masaüstü.</li>
  <li><strong>Initramfs karar katmanı</strong>: Boot sırasında parola denemelerini yönetir; başarısızlık eşiği aşılırsa decoy sisteme geçer.</li>
</ol>

<table>
  <thead>
    <tr>
      <th>Bileşen</th>
      <th>Görev</th>
      <th>Güvenlik Notu</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>LUKS gerçek bölüm</td>
      <td>Asıl veriyi korur</td>
      <td>Yanlış parola ile asla açılmaz</td>
    </tr>
    <tr>
      <td>Decoy root</td>
      <td>Sahte masaüstü sağlar</td>
      <td>Gerçek verilere erişimi olmamalıdır</td>
    </tr>
    <tr>
      <td>Initramfs hook</td>
      <td>Karar mekanizmasıdır</td>
      <td>Parolaları kaydetmemelidir</td>
    </tr>
    <tr>
      <td>Audit kaydı</td>
      <td>Olay sayacı tutar</td>
      <td>Hassas bilgi içermemelidir</td>
    </tr>
  </tbody>
</table>

<p>Bu tasarımın en önemli ilkesi şudur: Decoy ortam “inandırıcı” olabilir, ama saldırgana zarar vermemeli, parola toplamamalı ve üçüncü taraf sistemlere saldırı yapmamalıdır. Amaç alarm üretmek ve gerçek veriyi korumaktır.</p>

<h2 id="eşik-mantığı">Eşik mantığı</h2>

<p>Sistem, örneğin üç başarısız denemeden sonra decoy ortamı açabilir. Yanlışlıkla kendi kendinizi decoy’a düşürme ihtimali de hesaplanmalıdır. Eğer her denemede doğru girme olasılığınız $q$ ise, $N$ başarısız denemeden sonra decoy’a düşme olasılığı yaklaşık:</p>

\[P_{decoy} = (1-q)^N\]

<p>Örneğin $q=0.9$ ve $N=3$ için:</p>

\[P_{decoy} = 0.1^3 = 0.001\]

<p>Yani binde bir. Bu yüzden eşik değeri güvenlik ile kullanılabilirlik arasında dengelenmelidir.</p>

<table>
  <thead>
    <tr>
      <th>Strateji</th>
      <th>Avantaj</th>
      <th>Dezavantaj</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tek hatada decoy</td>
      <td>Hızlı tepki</td>
      <td>Kullanıcı hatasında riskli</td>
    </tr>
    <tr>
      <td>3 hatada decoy</td>
      <td>Dengeli</td>
      <td>Saldırgana birkaç deneme verir</td>
    </tr>
    <tr>
      <td>Panik parolası</td>
      <td>Kontrollü tetikleme</td>
      <td>Parola yönetimi karmaşıklaşır</td>
    </tr>
    <tr>
      <td>Ayrı decoy disk</td>
      <td>Temiz izolasyon</td>
      <td>Kurulum maliyeti artar</td>
    </tr>
  </tbody>
</table>

<h2 id="karar-akışı">Karar akışı</h2>

<p>Aşağıdaki örnek gerçek sisteme doğrudan kopyalanacak üretim kodu değil, mantığı göstermek için yazılmış güvenli bir iskelettir. Gerçek uygulamada bunu <code class="language-plaintext highlighter-rouge">initramfs-tools</code> veya <code class="language-plaintext highlighter-rouge">dracut</code> hook yapısına uyarlamak gerekir.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">MAX_FAILS</span><span class="o">=</span>3
<span class="nv">fails</span><span class="o">=</span>0
<span class="nv">REAL_DEV</span><span class="o">=</span>/dev/nvme0n1p3
<span class="nv">DECOY_BOOT</span><span class="o">=</span>/dev/nvme0n1p4

<span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s1">'LUKS parolasını girin:'</span>

  <span class="k">if </span>cryptsetup open <span class="nt">--test-passphrase</span> <span class="nv">$REAL_DEV</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s1">'Doğrulama başarılı, gerçek sistem açılıyor.'</span>
    <span class="nb">exec</span> /sbin/boot-real-root
  <span class="k">fi

  </span><span class="nv">fails</span><span class="o">=</span><span class="k">$((</span>fails <span class="o">+</span> <span class="m">1</span><span class="k">))</span>
  <span class="nb">echo</span> <span class="s1">'Parola doğrulanamadı.'</span>

  <span class="k">if</span> <span class="o">[</span> <span class="nv">$fails</span> <span class="nt">-ge</span> <span class="nv">$MAX_FAILS</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s1">'Sistem hazırlanıyor...'</span>
    <span class="nb">exec</span> /sbin/boot-decoy-root <span class="nv">$DECOY_BOOT</span>
  <span class="k">fi

  </span><span class="nb">sleep </span>2
<span class="k">done</span>
</code></pre></div></div>

<p>Burada dikkat edilmesi gereken nokta, script’in parolayı dosyaya yazmamasıdır. Ayrıca decoy’a geçerken kullanılan bölüm gerçek LUKS bölümünden tamamen bağımsız olmalıdır.</p>

<h2 id="decoy-masaüstünü-inandırıcı-yapmak">Decoy masaüstünü inandırıcı yapmak</h2>

<p>Decoy sistemin amacı saldırganı oyalamak değil, gerçek veriye ulaştığını düşündürürken hiçbir kritik bilgi sunmamaktır. Örneğin:</p>

<ul>
  <li>Sahte belgeler ve örnek proje klasörleri bulunabilir.</li>
  <li>Tarayıcı geçmişi gerçek hesaplara bağlı olmamalıdır.</li>
  <li>Ağ erişimi kısıtlanmalı veya yalnızca güvenli bir izleme ağına açılmalıdır.</li>
  <li>Sistem salt-okunur imajdan başlatılıp her açılışta temizlenmelidir.</li>
</ul>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[decoy]</span><span class="w">
</span><span class="py">root_mode</span><span class="p">=</span><span class="s">readonly</span>
<span class="py">network</span><span class="p">=</span><span class="s">restricted</span>
<span class="py">audit</span><span class="p">=</span><span class="s">metadata_only</span>
<span class="py">reset_on_boot</span><span class="p">=</span><span class="s">true</span>
</code></pre></div></div>

<p>Bu yapılandırma fikri, decoy ortamın her açılışta temiz bir duruma dönmesini sağlar. Böylece saldırganın bıraktığı dosyalar kalıcı olmaz; yalnızca zaman, deneme sayısı ve cihaz durumu gibi zararsız metaveriler kaydedilir.</p>

<h2 id="son-söz">Son söz</h2>

<p>LUKS tabanlı bir sahte masaüstü tuzağı, kriptografiyi kırmaya çalışmaz; aksine kriptografinin sağlamlığını kabul edip aldatma katmanını boot sürecine ekler. En güvenli yaklaşım, gerçek veriyi hiçbir koşulda decoy ortama bağlamamak, parola toplamamak ve sistemi önce sanal makinede test etmektir. Doğru tasarlandığında bu yöntem, dizüstü bilgisayarınızı yalnızca kilitleyen değil, aynı zamanda saldırı davranışını görünür kılan akıllı bir savunma katmanına dönüştürür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Proje" /><category term="LUKS" /><category term="Linux Güvenliği" /><category term="Honeypot" /><summary type="html"><![CDATA[Bir saldırganın cihazınızı ele geçirdiğini ve LUKS parolasını denediğini düşünün. Normalde yanlış parola yalnızca “erişim yok” demektir; fakat savunmacı aldatma yaklaşımında sistem, belirli sayıda hatadan sonra sanki parola kabul edilmiş gibi davranıp izole bir sahte masaüstü başlatabilir. Bu yazıda bunu kötüye kullanım değil, yalnızca kendi cihazınızda ve laboratuvar ortamında uygulanacak bir güvenlik tasarımı olarak ele alacağız.]]></summary></entry><entry><title type="html">Satır Satır Fork: Child Process’lerle Paralel Boru Hattı Kurmak</title><link href="https://sonsuzus.github.io/posts/satir-satir-fork-child-processlerle-paralel-boru-hatti-kurmak/" rel="alternate" type="text/html" title="Satır Satır Fork: Child Process’lerle Paralel Boru Hattı Kurmak" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/satir-satir-fork-child-processlerle-paralel-boru-hatti-kurmak</id><content type="html" xml:base="https://sonsuzus.github.io/posts/satir-satir-fork-child-processlerle-paralel-boru-hatti-kurmak/"><![CDATA[<p>Bir metin dosyasındaki her satırı ayrı bir iş birimi gibi düşünelim: Her satır küçük bir paket, her child process de bu paketi işleyen minik bir işçi. Sistem çağrısı seviyesinde paralel boru hattı kurmanın amacı, bu işçileri <code class="language-plaintext highlighter-rouge">fork()</code>, <code class="language-plaintext highlighter-rouge">pipe()</code>, <code class="language-plaintext highlighter-rouge">dup2()</code>, <code class="language-plaintext highlighter-rouge">exec()</code> ve <code class="language-plaintext highlighter-rouge">wait()</code> gibi yapı taşlarıyla yönetip çıktıları tek bir satırda birleştirmektir. Bu yaklaşım, shell komutlarını taklit etmekten öte, işletim sisteminin süreç ve dosya tanımlayıcı mantığını gerçekten kavratır.
``</p>

<h2 id="temel-fikir-satır--görev-process--işçi">Temel fikir: satır = görev, process = işçi</h2>

<p>Klasik bir program dosyayı satır satır okur ve her satırı sırayla işler. Paralel modelde ise $n$ satır için en fazla $n$ child process üretilebilir. Her child kendi satırını işler, sonucunu pipe üzerinden parent process’e yollar. Parent da sonuçları okuyup tek satır hâline getirir.</p>

<p>Teorik olarak toplam süre şöyle düşünülebilir:</p>

<p>$T_{seri} = \sum_{i=1}^{n} t_i$</p>

<p>Paralel modelde ideal durumda:</p>

<p>$T_{paralel} \approx \max(t_i) + T_{fork} + T_{pipe} + T_{wait}$</p>

<p>Yani her şey bedava değildir. Process oluşturmanın, pipe açmanın ve context switch’in maliyeti vardır. Bu yüzden bu yöntem özellikle satır başına yapılan işlem yeterince “ağırsa” mantıklıdır.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Avantaj</th>
      <th>Dezavantaj</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Seri okuma</td>
      <td>Basit, az kaynak tüketir</td>
      <td>Yavaş olabilir</td>
    </tr>
    <tr>
      <td>Thread tabanlı</td>
      <td>Daha hafif paralellik</td>
      <td>Paylaşımlı bellek dikkat ister</td>
    </tr>
    <tr>
      <td>Process tabanlı</td>
      <td>İzolasyon güçlüdür</td>
      <td><code class="language-plaintext highlighter-rouge">fork()</code> ve IPC maliyetlidir</td>
    </tr>
    <tr>
      <td>Shell pipeline</td>
      <td>Hızlı prototip</td>
      <td>Kontrol daha sınırlıdır</td>
    </tr>
  </tbody>
</table>

<h2 id="kullanacağımız-sistem-çağrıları">Kullanacağımız sistem çağrıları</h2>

<table>
  <thead>
    <tr>
      <th>Çağrı</th>
      <th>Görevi</th>
      <th>Akılda kalıcı benzetme</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">fork()</code></td>
      <td>Child process üretir</td>
      <td>İşçiyi klonlamak</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">pipe()</code></td>
      <td>İki uçlu veri kanalı açar</td>
      <td>Konuşma borusu</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dup2()</code></td>
      <td>Dosya tanımlayıcı yönlendirir</td>
      <td>Mikrofonu başka kabloya takmak</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">exec()</code></td>
      <td>Process’in programını değiştirir</td>
      <td>İşçiye yeni meslek vermek</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">waitpid()</code></td>
      <td>Child bitişini bekler</td>
      <td>Mesai kontrolü</td>
    </tr>
  </tbody>
</table>

<p>Aşağıdaki örnekte her satırı ayrı child process’e veriyoruz. Child, satırı küçük bir işlemden geçiriyor: boşlukları <code class="language-plaintext highlighter-rouge">_</code> karakterine çeviriyor ve sonucu pipe ile parent’a gönderiyor. Gerçek hayatta bu bölüm <code class="language-plaintext highlighter-rouge">exec()</code> ile harici bir programa da devredilebilir.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/wait.h&gt;</span><span class="cp">
</span>
<span class="cp">#define MAX_LINE 256
#define MAX_CHILD 128
</span>
<span class="kt">void</span> <span class="nf">transform</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">s</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">s</span><span class="p">[</span><span class="n">i</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">if</span> <span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">' '</span> <span class="o">||</span> <span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\n'</span><span class="p">)</span> <span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'_'</span><span class="p">;</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">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Kullanim: %s dosya.txt</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s">"r"</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">fp</span><span class="p">)</span> <span class="p">{</span> <span class="n">perror</span><span class="p">(</span><span class="s">"fopen"</span><span class="p">);</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>

    <span class="kt">int</span> <span class="n">pipes</span><span class="p">[</span><span class="n">MAX_CHILD</span><span class="p">][</span><span class="mi">2</span><span class="p">];</span>
    <span class="n">pid_t</span> <span class="n">pids</span><span class="p">[</span><span class="n">MAX_CHILD</span><span class="p">];</span>
    <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">line</span><span class="p">[</span><span class="n">MAX_LINE</span><span class="p">];</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">line</span><span class="p">),</span> <span class="n">fp</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">count</span> <span class="o">&lt;</span> <span class="n">MAX_CHILD</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">pipe</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">count</span><span class="p">])</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="n">perror</span><span class="p">(</span><span class="s">"pipe"</span><span class="p">);</span> <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span>

        <span class="n">pid_t</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">fork</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">pid</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="n">perror</span><span class="p">(</span><span class="s">"fork"</span><span class="p">);</span> <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">close</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">count</span><span class="p">][</span><span class="mi">0</span><span class="p">]);</span>
            <span class="n">transform</span><span class="p">(</span><span class="n">line</span><span class="p">);</span>
            <span class="n">dprintf</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">count</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="s">"%s"</span><span class="p">,</span> <span class="n">line</span><span class="p">);</span>
            <span class="n">close</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">count</span><span class="p">][</span><span class="mi">1</span><span class="p">]);</span>
            <span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="n">close</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">count</span><span class="p">][</span><span class="mi">1</span><span class="p">]);</span>
        <span class="n">pids</span><span class="p">[</span><span class="n">count</span><span class="p">]</span> <span class="o">=</span> <span class="n">pid</span><span class="p">;</span>
        <span class="n">count</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">fclose</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Sonuc: "</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="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="n">MAX_LINE</span><span class="p">];</span>
        <span class="kt">ssize_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">buffer</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buffer</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">buffer</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
            <span class="n">printf</span><span class="p">(</span><span class="s">"%s "</span><span class="p">,</span> <span class="n">buffer</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="n">close</span><span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]);</span>
        <span class="n">waitpid</span><span class="p">(</span><span class="n">pids</span><span class="p">[</span><span class="n">i</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="p">}</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Kodun önemli noktası şudur: Child sadece yazma ucunu, parent ise sadece okuma ucunu kullanır. Kullanılmayan pipe uçlarını kapatmazsak EOF davranışı bozulabilir ve program “neden bitmiyor?” diye bizi gece 03:00’te kahveye mahkûm edebilir.</p>

<h2 id="exec-eklemek-istersek"><code class="language-plaintext highlighter-rouge">exec()</code> eklemek istersek</h2>

<p>Child içinde kendi fonksiyonumuzu çalıştırmak yerine harici bir komuta veri göndermek mümkündür. Bunun için genellikle iki pipe gerekir: biri child’ın stdin’ine, diğeri stdout’una bağlanır. Mantık şu şekildedir:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// child tarafında kavramsal akış</span>
<span class="c1">// dup2(inputPipe[0], STDIN_FILENO);</span>
<span class="c1">// dup2(outputPipe[1], STDOUT_FILENO);</span>
<span class="c1">// execlp("tr", "tr", "a-z", "A-Z", NULL);</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">tr</code> komutu stdin’den aldığı metni dönüştürür, stdout’a yazar. Parent da stdout pipe’ından sonucu toplar. Böylece her satır ayrı bir Unix filtresinden geçmiş olur.</p>

<h2 id="sıralama-ve-güvenlik-notları">Sıralama ve güvenlik notları</h2>

<p>Parent sonuçları <code class="language-plaintext highlighter-rouge">pids</code> dizisindeki sıraya göre okuduğu için çıktı dosyadaki satır sırasını korur. Ancak çok büyük dosyalarda her satıra process açmak tehlikelidir. Daha sağlıklı model, aynı anda en fazla $k$ child çalıştırmaktır. Bu durumda yaklaşık kaynak sınırı:</p>

<p>$aktif_process \leq k$</p>

<p>Sonuç olarak bu paralel boru hattı, process izolasyonunu, pipe tabanlı iletişimi ve Unix’in “her şey dosya tanımlayıcıdır” felsefesini aynı sahnede buluşturur. Küçük dosyalarda eğlenceli, ağır satır işlemlerinde güçlü, sınırsız process açıldığında ise tam bir kaynak canavarına dönüşebilir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Program" /><category term="linux" /><category term="c" /><category term="process" /><category term="pipe" /><category term="fork" /><summary type="html"><![CDATA[Bir metin dosyasındaki her satırı ayrı bir iş birimi gibi düşünelim: Her satır küçük bir paket, her child process de bu paketi işleyen minik bir işçi. Sistem çağrısı seviyesinde paralel boru hattı kurmanın amacı, bu işçileri fork(), pipe(), dup2(), exec() ve wait() gibi yapı taşlarıyla yönetip çıktıları tek bir satırda birleştirmektir. Bu yaklaşım, shell komutlarını taklit etmekten öte, işletim sisteminin süreç ve dosya tanımlayıcı mantığını gerçekten kavratır.]]></summary></entry><entry><title type="html">Ses Kartından Nota Tahminine: Fourier Tabanlı Akort Uygulaması</title><link href="https://sonsuzus.github.io/posts/ses-kartindan-nota-tahminine-fourier-tabanli-akort-uygulamasi/" rel="alternate" type="text/html" title="Ses Kartından Nota Tahminine: Fourier Tabanlı Akort Uygulaması" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/ses-kartindan-nota-tahminine-fourier-tabanli-akort-uygulamasi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/ses-kartindan-nota-tahminine-fourier-tabanli-akort-uygulamasi/"><![CDATA[<p>Bir akort uygulaması kulağa basit gelir: teli çal, ekranda notayı gör. Ama perdenin arkasında ses kartından gelen ham örnekler, gürültü, pencereleme, Fourier dönüşümü ve biraz müzik teorisi birlikte dans eder. Bu yazıda, mikrofon ya da ses kartı girişinden alınan ham sinyali frekans analizine sokup enstrümanın notasını tahmin eden orta seviye bir uygulamanın mantığını kuracağız.
``
Ses kartı aslında sürekli bir analog hava titreşimini sayılara çevirir. Bu işleme örnekleme denir. Saniyede alınan örnek sayısı $f_s$ ile gösterilir; örneğin 44100 Hz, saniyede 44100 ölçüm demektir. Bir buffer içinde $N$ adet örnek toplarsak, analiz edebileceğimiz frekans çözünürlüğü yaklaşık olarak şudur:</p>

\[\Delta f = \frac{f_s}{N}\]

<p>Yani $f_s=44100$ ve $N=4096$ ise çözünürlük yaklaşık 10.77 Hz olur. Bu, gitar akordu için çalışır ama çok hassas değildir. Buffer büyüdükçe frekans çözünürlüğü artar, fakat gecikme de artar. Akort uygulamasının temel tasarım savaşı budur: hızlı mı olsun, hassas mı?</p>

<table>
  <thead>
    <tr>
      <th>Parametre</th>
      <th style="text-align: right">Küçük Değer</th>
      <th style="text-align: right">Büyük Değer</th>
      <th>Akort Uygulamasına Etkisi</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Buffer boyutu $N$</td>
      <td style="text-align: right">Düşük gecikme</td>
      <td style="text-align: right">İyi frekans çözünürlüğü</td>
      <td>Denge gerekir</td>
    </tr>
    <tr>
      <td>Örnekleme oranı $f_s$</td>
      <td style="text-align: right">Daha az veri</td>
      <td style="text-align: right">Daha geniş bant</td>
      <td>44100 Hz genelde yeterli</td>
    </tr>
    <tr>
      <td>Pencere fonksiyonu</td>
      <td style="text-align: right">Basit analiz</td>
      <td style="text-align: right">Daha az sızıntı</td>
      <td>Hann iyi başlangıçtır</td>
    </tr>
  </tbody>
</table>

<p>Ham sinyal doğrudan Fourier dönüşümüne verildiğinde bazı sorunlar çıkar. Çünkü biz sonsuz uzunlukta değil, kısa bir ses parçası inceleriz. Kesilen sinyalin kenarları ani sıçrama gibi davranır ve spektral sızıntı oluşur. Bu yüzden buffer’a Hann penceresi uygularız. Ayrık Fourier dönüşümünün fikri şudur:</p>

\[X[k] = \sum_{n=0}^{N-1} x[n] e^{-j2\pi kn/N}\]

<p>Burada $X[k]$, sinyalin hangi frekans bileşenlerinde güçlü olduğunu söyler. En büyük genlikli frekans çoğu zaman temel frekanstır; ama enstrümanlarda harmonikler yüzünden bazen ikinci ya da üçüncü harmonik temel frekanstan daha güçlü olabilir. Bu yüzden gerçek uygulamalarda yalnızca en yüksek tepeye güvenmek yerine belirli aralıkta arama, parabolik interpolasyon veya YIN gibi algoritmalar da kullanılır.</p>

<p>Nota tahmini için frekansı en yakın müzikal notaya eşleriz. Batı müziğinde A4 notası 440 Hz kabul edilirse MIDI nota numarası şu formülle bulunabilir:</p>

\[m = 69 + 12\log_2\left(\frac{f}{440}\right)\]

<p>Sonra $m$ değerini en yakın tam sayıya yuvarlarız. Farkı cent cinsinden gösterirsek kullanıcıya telin pes mi tiz mi olduğunu anlatabiliriz:</p>

\[cent = 1200\log_2\left(\frac{f}{f_{nota}}\right)\]

<table>
  <thead>
    <tr>
      <th style="text-align: right">Cent Değeri</th>
      <th>Anlamı</th>
      <th>Kullanıcıya Mesaj</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">-20</td>
      <td>Pes</td>
      <td>Teli biraz sık</td>
    </tr>
    <tr>
      <td style="text-align: right">0</td>
      <td>Tam akort</td>
      <td>Harika, dokunma</td>
    </tr>
    <tr>
      <td style="text-align: right">+20</td>
      <td>Tiz</td>
      <td>Teli biraz gevşet</td>
    </tr>
  </tbody>
</table>

<p>Aşağıdaki Python örneği <code class="language-plaintext highlighter-rouge">sounddevice</code> ile ses kartından veri alır, NumPy ile FFT uygular ve en yakın notayı hesaplar. Kod öğretici amaçlıdır; sahnede kullanmadan önce gürültü eşiği ve daha sağlam temel frekans algılama eklemek iyi olur.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="n">sounddevice</span> <span class="k">as</span> <span class="n">sd</span>

<span class="n">FS</span> <span class="o">=</span> <span class="mi">44100</span>
<span class="n">N</span> <span class="o">=</span> <span class="mi">4096</span>
<span class="n">NOTE_NAMES</span> <span class="o">=</span> <span class="p">[</span><span class="sh">'</span><span class="s">C</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">C#</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">D</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">D#</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">E</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">F</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">F#</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">G</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">G#</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">A</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">A#</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">B</span><span class="sh">'</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">freq_to_note</span><span class="p">(</span><span class="n">freq</span><span class="p">):</span>
    <span class="n">midi</span> <span class="o">=</span> <span class="mi">69</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="nf">log2</span><span class="p">(</span><span class="n">freq</span> <span class="o">/</span> <span class="mf">440.0</span><span class="p">)</span>
    <span class="n">nearest</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="n">midi</span><span class="p">))</span>
    <span class="n">note_freq</span> <span class="o">=</span> <span class="mf">440.0</span> <span class="o">*</span> <span class="p">(</span><span class="mi">2</span> <span class="o">**</span> <span class="p">((</span><span class="n">nearest</span> <span class="o">-</span> <span class="mi">69</span><span class="p">)</span> <span class="o">/</span> <span class="mi">12</span><span class="p">))</span>
    <span class="n">cents</span> <span class="o">=</span> <span class="mi">1200</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="nf">log2</span><span class="p">(</span><span class="n">freq</span> <span class="o">/</span> <span class="n">note_freq</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">NOTE_NAMES</span><span class="p">[</span><span class="n">nearest</span> <span class="o">%</span> <span class="mi">12</span><span class="p">]</span> <span class="o">+</span> <span class="nf">str</span><span class="p">(</span><span class="n">nearest</span> <span class="o">//</span> <span class="mi">12</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">name</span><span class="p">,</span> <span class="n">cents</span><span class="p">,</span> <span class="n">note_freq</span>

<span class="k">def</span> <span class="nf">detect_pitch</span><span class="p">(</span><span class="n">samples</span><span class="p">):</span>
    <span class="n">samples</span> <span class="o">=</span> <span class="n">samples</span> <span class="o">-</span> <span class="n">np</span><span class="p">.</span><span class="nf">mean</span><span class="p">(</span><span class="n">samples</span><span class="p">)</span>
    <span class="n">windowed</span> <span class="o">=</span> <span class="n">samples</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="nf">hanning</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">samples</span><span class="p">))</span>
    <span class="n">spectrum</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="nf">rfft</span><span class="p">(</span><span class="n">windowed</span><span class="p">)</span>
    <span class="n">magnitudes</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">abs</span><span class="p">(</span><span class="n">spectrum</span><span class="p">)</span>
    <span class="n">freqs</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="nf">rfftfreq</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">samples</span><span class="p">),</span> <span class="mi">1</span> <span class="o">/</span> <span class="n">FS</span><span class="p">)</span>

    <span class="n">valid</span> <span class="o">=</span> <span class="p">(</span><span class="n">freqs</span> <span class="o">&gt;</span> <span class="mi">60</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">freqs</span> <span class="o">&lt;</span> <span class="mi">1200</span><span class="p">)</span>
    <span class="n">index</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">argmax</span><span class="p">(</span><span class="n">magnitudes</span><span class="p">[</span><span class="n">valid</span><span class="p">])</span>
    <span class="n">freq</span> <span class="o">=</span> <span class="n">freqs</span><span class="p">[</span><span class="n">valid</span><span class="p">][</span><span class="n">index</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">freq</span>

<span class="k">with</span> <span class="n">sd</span><span class="p">.</span><span class="nc">InputStream</span><span class="p">(</span><span class="n">channels</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">samplerate</span><span class="o">=</span><span class="n">FS</span><span class="p">,</span> <span class="n">blocksize</span><span class="o">=</span><span class="n">N</span><span class="p">)</span> <span class="k">as</span> <span class="n">stream</span><span class="p">:</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">data</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">stream</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">N</span><span class="p">)</span>
        <span class="n">samples</span> <span class="o">=</span> <span class="n">data</span><span class="p">[:,</span> <span class="mi">0</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">np</span><span class="p">.</span><span class="nf">max</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nf">abs</span><span class="p">(</span><span class="n">samples</span><span class="p">))</span> <span class="o">&lt;</span> <span class="mf">0.01</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">freq</span> <span class="o">=</span> <span class="nf">detect_pitch</span><span class="p">(</span><span class="n">samples</span><span class="p">)</span>
        <span class="n">note</span><span class="p">,</span> <span class="n">cents</span><span class="p">,</span> <span class="n">target</span> <span class="o">=</span> <span class="nf">freq_to_note</span><span class="p">(</span><span class="n">freq</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="si">{</span><span class="n">freq</span><span class="si">:</span><span class="mf">7.2</span><span class="n">f</span><span class="si">}</span><span class="s"> Hz -&gt; </span><span class="si">{</span><span class="n">note</span><span class="si">}</span><span class="s">, hedef </span><span class="si">{</span><span class="n">target</span><span class="si">:</span><span class="mf">7.2</span><span class="n">f</span><span class="si">}</span><span class="s"> Hz, sapma </span><span class="si">{</span><span class="n">cents</span><span class="si">:</span><span class="o">+</span><span class="mf">5.1</span><span class="n">f</span><span class="si">}</span><span class="s"> cent</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>

<p>Kodda önce DC bileşenini temizlemek için ortalamayı çıkarıyoruz. Ardından Hann penceresi uygulayıp <code class="language-plaintext highlighter-rouge">rfft</code> ile yalnızca pozitif frekansları hesaplıyoruz. 60-1200 Hz aralığı gitar, bağlama, ukulele ve birçok melodik enstrüman için pratik bir başlangıçtır. En büyük spektral tepe bulunuyor, sonra bu frekans nota adına çevriliyor.</p>

<p>Daha profesyonel bir akort uygulaması için üç geliştirme önerisi çok işe yarar: tepe çevresinde parabolik interpolasyonla frekansı inceltmek, harmonikleri kontrol ederek temel frekansı doğrulamak ve ekranda cent sapmasını ibre ya da renkli göstergeyle sunmak. Sonuçta iyi bir tuner sadece matematik yapmaz; müzisyene sakin, okunabilir ve güvenilir geri bildirim verir. Fourier burada büyülü değnek değil, sesin içindeki düzeni görünür kılan güçlü bir büyüteçtir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Proje" /><category term="ses işleme" /><category term="fourier dönüşümü" /><category term="python" /><category term="akort" /><category term="dsp" /><summary type="html"><![CDATA[Bir akort uygulaması kulağa basit gelir: teli çal, ekranda notayı gör. Ama perdenin arkasında ses kartından gelen ham örnekler, gürültü, pencereleme, Fourier dönüşümü ve biraz müzik teorisi birlikte dans eder. Bu yazıda, mikrofon ya da ses kartı girişinden alınan ham sinyali frekans analizine sokup enstrümanın notasını tahmin eden orta seviye bir uygulamanın mantığını kuracağız.]]></summary></entry><entry><title type="html">SSH, Mikrofon ve Hoparlör Üçgeninde Etik Akustik Geri Besleme</title><link href="https://sonsuzus.github.io/posts/ssh-mikrofon-ve-hoparlor-ucgeninde-etik-akustik-geri-besleme/" rel="alternate" type="text/html" title="SSH, Mikrofon ve Hoparlör Üçgeninde Etik Akustik Geri Besleme" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/ssh-mikrofon-ve-hoparlor-ucgeninde-etik-akustik-geri-besleme</id><content type="html" xml:base="https://sonsuzus.github.io/posts/ssh-mikrofon-ve-hoparlor-ucgeninde-etik-akustik-geri-besleme/"><![CDATA[<p>Uzaktaki bir makinenin mikrofonunu açıp hoparlöre yönlendirmek teknik olarak ses yönlendirme, gecikme ve kazanç yönetimi konularına dokunur; fakat aynı zamanda çok ciddi mahremiyet riski taşır. Bu nedenle bu yazı, bir odayı gizlice dinleten bir düzenek kurma rehberi değil; yalnızca yetkili laboratuvar, konferans odası testi veya kendi cihazında yapılan kapalı devre akustik doğrulama için teorik ve güvenli bir çerçevedir.
``</p>

<h2 id="önce-etik-sınır-ssh-anahtarın-varsa-her-şeyi-yapabilir-misin">Önce etik sınır: SSH anahtarın varsa her şeyi yapabilir misin?</h2>

<p>Hayır. SSH erişimi teknik yetki verir, hukuki ve etik izin vermez. Mikrofon, kamera gibi algılayıcılar kişisel veri toplar. Bir makinede yönetici olsan bile ortamda bulunan herkesin açık rızası olmadan mikrofonu etkinleştirip odadan ses almak kabul edilemez. Güvenli yaklaşım şudur: cihaz sahibi, ortam kullanıcıları ve test zamanı önceden belli olmalı; kayıt yapılmamalı; test göstergesi görünür olmalı.</p>

<table>
  <thead>
    <tr>
      <th>Senaryo</th>
      <th style="text-align: right">Kabul edilebilir mi?</th>
      <th>Not</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kendi dizüstünde hoparlör-mikrofon gecikmesi ölçmek</td>
      <td style="text-align: right">Evet</td>
      <td>Yerel ve kontrollü</td>
    </tr>
    <tr>
      <td>Toplantı odasında anons sistemi testi</td>
      <td style="text-align: right">Evet</td>
      <td>Katılımcılara haber verilirse</td>
    </tr>
    <tr>
      <td>SSH ile kimseye haber vermeden mikrofonu açmak</td>
      <td style="text-align: right">Hayır</td>
      <td>Mahremiyet ihlali</td>
    </tr>
    <tr>
      <td>Sanal ses aygıtında test sinyali döndürmek</td>
      <td style="text-align: right">Evet</td>
      <td>Gerçek ortam sesi toplanmaz</td>
    </tr>
  </tbody>
</table>

<h2 id="akustik-geri-beslemenin-mantığı">Akustik geri beslemenin mantığı</h2>

<p>Mikrofondan alınan sinyal hoparlörden tekrar ortama verildiğinde bir döngü oluşur. Basitleştirilmiş model:</p>

\[y(t)=G \cdot x(t-\tau)\]

<p>Burada $G$ toplam kazanç, $\tau$ ise gecikmedir. Eğer belirli bir frekansta döngü kazancı $G(f) \ge 1$ olur ve faz yaklaşık $2\pi k$ hizasına gelirse sistem ıslık, uğultu veya çınlama üretir. Yani sorun sadece sesin yüksekliği değil; mikrofon-hoparlör mesafesi, oda yansımaları, ekolayzır ve gecikmedir.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th>Düşük değer</th>
      <th>Yüksek değer</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kazanç $G$</td>
      <td>Daha stabil</td>
      <td>Feedback riski artar</td>
    </tr>
    <tr>
      <td>Gecikme $\tau$</td>
      <td>Doğal his</td>
      <td>Yankı belirginleşir</td>
    </tr>
    <tr>
      <td>Mikrofon-hoparlör mesafesi</td>
      <td>Kaçak artabilir</td>
      <td>Kaçak azalabilir</td>
    </tr>
    <tr>
      <td>Oda yansıması</td>
      <td>Kuru ses</td>
      <td>Rezonans ve uğultu</td>
    </tr>
  </tbody>
</table>

<h2 id="ssh-burada-ne-işe-yarar">SSH burada ne işe yarar?</h2>

<p>SSH, ses kablosu değil; uzaktan yönetim kanalıdır. Güvenli kullanımda SSH ile cihaz durumu okunabilir, test servisleri başlatılabilir veya sanal ses aygıtları denetlenebilir. Örneğin aşağıdaki komutlar yalnızca ses altyapısını listeler; mikrofonu canlı dinlemeye açmaz:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh admin@lab-makinesi <span class="s1">'hostname; pactl info; pactl list short sources; pactl list short sinks'</span>
</code></pre></div></div>

<p>Bu çıktılar PipeWire/PulseAudio üzerinde hangi giriş ve çıkışların bulunduğunu anlamaya yarar. Burada kritik nokta, gerçek mikrofon kaynağını hoparlöre bağlayan otomatik bir komut çalıştırmamaktır.</p>

<h2 id="güvenli-test-gerçek-oda-sesi-yerine-sanal-kaynak">Güvenli test: gerçek oda sesi yerine sanal kaynak</h2>

<p>Akustik zinciri öğrenmek için önce sanal bir çıkış oluşturmak daha güvenlidir. Böylece gerçek ortam sesi toplanmaz:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pactl load-module module-null-sink <span class="nv">sink_name</span><span class="o">=</span>lab_sink <span class="nv">sink_properties</span><span class="o">=</span>device.description<span class="o">=</span>Lab_Test_Sink
pactl list short sinks
</code></pre></div></div>

<p>Bu komut bir null sink oluşturur. Yani ses sistemi içinde hedef gibi görünen ama fiziksel hoparlöre gitmeyen bir test noktası üretir. Ardından kısa bir test tonu yerel makinede çalınabilir:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>speaker-test <span class="nt">-t</span> sine <span class="nt">-f</span> 440 <span class="nt">-l</span> 1
</code></pre></div></div>

<p>440 Hz tonu, zincirin çalışıp çalışmadığını anlamak için klasik ve zararsız bir referanstır. Ölçüm yaparken ses seviyesini düşük tutmak kulak sağlığı açısından önemlidir.</p>

<h2 id="basit-güvenlik-kapısı-fikri">Basit güvenlik kapısı fikri</h2>

<p>Yetkili laboratuvarlarda bile test komutlarının yanlışlıkla çalışmasını önlemek gerekir. Aşağıdaki örnek betik, açık rıza dosyası yoksa testi durdurur:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>
<span class="nb">set</span> <span class="nt">-euo</span> pipefail

<span class="nv">CONSENT_FILE</span><span class="o">=</span><span class="s1">'/tmp/audio-test-consent-ok'</span>

<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$CONSENT_FILE</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s1">'Onay dosyası yok. Ses testi başlatılmadı.'</span>
  <span class="nb">exit </span>1
<span class="k">fi

</span><span class="nb">echo</span> <span class="s1">'Onay bulundu. Sadece cihaz listesi okunuyor.'</span>
pactl list short sources
pactl list short sinks
</code></pre></div></div>

<p>Bu betik bir güvenlik çözümü değil, iyi alışkanlık örneğidir. Asıl güvenlik; süreç, bilgilendirme ve erişim kontrolünden gelir.</p>

<h2 id="sonuç">Sonuç</h2>

<p>Mikrofonu hoparlöre döndürmek, sinyal işleme açısından eğitici bir konudur: kazanç, faz, gecikme ve oda akustiği aynı anda devreye girer. Fakat SSH üzerinden uzaktaki bir ortamın sesini dinlemek, izinsiz yapıldığında teknik meraktan çıkar ve mahremiyet ihlaline dönüşür. En doğru yol; sanal aygıtlarla öğrenmek, fiziksel testlerde açık rıza almak ve gerçek mikrofon verisini yalnızca gerekli, görünür ve kayıt dışı koşullarda kullanmaktır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="ssh" /><category term="ses sistemleri" /><category term="linux" /><category term="etik güvenlik" /><summary type="html"><![CDATA[Uzaktaki bir makinenin mikrofonunu açıp hoparlöre yönlendirmek teknik olarak ses yönlendirme, gecikme ve kazanç yönetimi konularına dokunur; fakat aynı zamanda çok ciddi mahremiyet riski taşır. Bu nedenle bu yazı, bir odayı gizlice dinleten bir düzenek kurma rehberi değil; yalnızca yetkili laboratuvar, konferans odası testi veya kendi cihazında yapılan kapalı devre akustik doğrulama için teorik ve güvenli bir çerçevedir.]]></summary></entry><entry><title type="html">X11’de xinput ve xdotool ile Piksel Piksel İmleç Çizen Canlı Tuval</title><link href="https://sonsuzus.github.io/posts/x11de-xinput-ve-xdotool-ile-piksel-piksel-imlec-cizen-canli-tuval/" rel="alternate" type="text/html" title="X11’de xinput ve xdotool ile Piksel Piksel İmleç Çizen Canlı Tuval" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/x11de-xinput-ve-xdotool-ile-piksel-piksel-imlec-cizen-canli-tuval</id><content type="html" xml:base="https://sonsuzus.github.io/posts/x11de-xinput-ve-xdotool-ile-piksel-piksel-imlec-cizen-canli-tuval/"><![CDATA[<p>Fare imlecini ekranda minik bir robot kalem gibi gezdirip, geçtiği yolu canlı olarak çizen bir tuval uygulaması kulağa biraz büyücülük gibi gelebilir. Aslında X11 dünyasında bu işin arkasında çok net bir mantık var: imleç konumunu oku, konumu bir önceki noktayla birleştir, gerekiyorsa imleci programatik olarak bir piksel kaydır ve tekrar et.
``</p>

<p>X11, Linux masaüstlerinde uzun yıllardır kullanılan bir pencereleme sistemidir. Burada uygulamalar “client”, ekran, klavye ve fare gibi kaynakları yöneten yapı ise “server” gibi düşünülebilir. Fare imleci de bu sunucunun yönettiği ortak bir varlıktır. Biz <code class="language-plaintext highlighter-rouge">xinput</code> ile giriş aygıtlarını ve olaylarını gözlemleyebilir, <code class="language-plaintext highlighter-rouge">xdotool</code> ile imleci hareket ettirebilir veya konumunu okuyabiliriz.</p>

<p>Temel matematik oldukça sade. İmlecin ekrandaki konumunu şu vektörle temsil edelim: $p_t = (x_t, y_t)$. Bir sonraki örnekte elimizde $p_{t+1} = (x_{t+1}, y_{t+1})$ varsa, tuvale çizeceğimiz çizgi parçası bu iki noktanın birleşimidir. İmleci piksel piksel hareket ettirmek istediğimizde ise her adımda $\Delta x$ ve $\Delta y$ değerlerini genellikle $-1$, $0$ veya $1$ seçeriz. Böylece hareket, büyük sıçramalar yerine minik adımlar halinde gerçekleşir.</p>

<table>
  <thead>
    <tr>
      <th>Araç</th>
      <th>Görevi</th>
      <th>Bu projedeki rolü</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">xinput</code></td>
      <td>Giriş aygıtlarını ve olaylarını izler</td>
      <td>Fareyi tanımak, X11 olay mantığını görmek</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">xdotool</code></td>
      <td>X11 üzerinde klavye/fare otomasyonu yapar</td>
      <td>İmleci taşımak ve konum okumak</td>
    </tr>
    <tr>
      <td>Python Tkinter</td>
      <td>Basit GUI tuvali sağlar</td>
      <td>İmleç yolunu canlı çizmek</td>
    </tr>
  </tbody>
</table>

<p>Önce sistemde gerekli araçları kurarak başlayalım:</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>xdotool xinput python3-tk
</code></pre></div></div>

<p>Fare aygıtlarını görmek için:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xinput list
</code></pre></div></div>

<p>Daha detaylı olay akışını izlemek isterseniz şu komut faydalıdır:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xinput test-xi2 <span class="nt">--root</span>
</code></pre></div></div>

<p>Bu çıktı, X11’in fare hareketini nasıl olaylara dönüştürdüğünü anlamak için harikadır. Ancak canlı çizim uygulamasında pratiklik adına konumu <code class="language-plaintext highlighter-rouge">xdotool getmouselocation</code> ile okuyacağız.</p>

<p>Aşağıdaki Python kodu bir Tkinter penceresi açar, imlecin ekran koordinatlarını düzenli olarak okur ve önceki konumla yeni konum arasına çizgi çeker. Ayrıca küçük bir otomasyon fonksiyonu, imleci piksel piksel hareket ettirerek ekranda iz bırakmasını sağlar.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">tkinter</span> <span class="k">as</span> <span class="n">tk</span>
<span class="kn">import</span> <span class="n">subprocess</span>
<span class="kn">import</span> <span class="n">time</span>
<span class="kn">import</span> <span class="n">threading</span>

<span class="n">WIDTH</span><span class="p">,</span> <span class="n">HEIGHT</span> <span class="o">=</span> <span class="mi">900</span><span class="p">,</span> <span class="mi">600</span>
<span class="n">last_point</span> <span class="o">=</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">get_mouse_position</span><span class="p">():</span>
    <span class="n">out</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="nf">check_output</span><span class="p">([</span><span class="sh">'</span><span class="s">xdotool</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">getmouselocation</span><span class="sh">'</span><span class="p">]).</span><span class="nf">decode</span><span class="p">()</span>
    <span class="n">parts</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">:</span><span class="sh">'</span><span class="p">)</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">out</span><span class="p">.</span><span class="nf">split</span><span class="p">()</span> <span class="k">if</span> <span class="sh">'</span><span class="s">:</span><span class="sh">'</span> <span class="ow">in</span> <span class="n">item</span><span class="p">)</span>
    <span class="k">return</span> <span class="nf">int</span><span class="p">(</span><span class="n">parts</span><span class="p">[</span><span class="sh">'</span><span class="s">x</span><span class="sh">'</span><span class="p">]),</span> <span class="nf">int</span><span class="p">(</span><span class="n">parts</span><span class="p">[</span><span class="sh">'</span><span class="s">y</span><span class="sh">'</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">move_relative</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">):</span>
    <span class="n">subprocess</span><span class="p">.</span><span class="nf">call</span><span class="p">([</span><span class="sh">'</span><span class="s">xdotool</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">mousemove_relative</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">--sync</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="nf">str</span><span class="p">(</span><span class="n">dx</span><span class="p">),</span> <span class="nf">str</span><span class="p">(</span><span class="n">dy</span><span class="p">)])</span>

<span class="k">def</span> <span class="nf">draw_loop</span><span class="p">():</span>
    <span class="k">global</span> <span class="n">last_point</span>
    <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="nf">get_mouse_position</span><span class="p">()</span>

    <span class="c1"># Ekran koordinatlarını basitçe tuval sınırlarına sıkıştırıyoruz.
</span>    <span class="n">cx</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">min</span><span class="p">(</span><span class="n">WIDTH</span><span class="p">,</span> <span class="n">x</span><span class="p">))</span>
    <span class="n">cy</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">min</span><span class="p">(</span><span class="n">HEIGHT</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>

    <span class="k">if</span> <span class="n">last_point</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">canvas</span><span class="p">.</span><span class="nf">create_line</span><span class="p">(</span><span class="n">last_point</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">last_point</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">cx</span><span class="p">,</span> <span class="n">cy</span><span class="p">,</span> <span class="n">fill</span><span class="o">=</span><span class="sh">'</span><span class="s">lime</span><span class="sh">'</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>

    <span class="n">last_point</span> <span class="o">=</span> <span class="p">(</span><span class="n">cx</span><span class="p">,</span> <span class="n">cy</span><span class="p">)</span>
    <span class="n">root</span><span class="p">.</span><span class="nf">after</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="n">draw_loop</span><span class="p">)</span>  <span class="c1"># Yaklaşık 60 FPS örnekleme
</span>
<span class="k">def</span> <span class="nf">pixel_walk</span><span class="p">():</span>
    <span class="c1"># Küçük kare benzeri bir rota: sağ, aşağı, sol, yukarı
</span>    <span class="n">route</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)]</span> <span class="o">*</span> <span class="mi">120</span> <span class="o">+</span> <span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span> <span class="o">*</span> <span class="mi">80</span> <span class="o">+</span> <span class="p">[(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)]</span> <span class="o">*</span> <span class="mi">120</span> <span class="o">+</span> <span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)]</span> <span class="o">*</span> <span class="mi">80</span>
    <span class="k">for</span> <span class="n">dx</span><span class="p">,</span> <span class="n">dy</span> <span class="ow">in</span> <span class="n">route</span><span class="p">:</span>
        <span class="nf">move_relative</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">)</span>
        <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mf">0.01</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">start_walk</span><span class="p">():</span>
    <span class="n">threading</span><span class="p">.</span><span class="nc">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">pixel_walk</span><span class="p">,</span> <span class="n">daemon</span><span class="o">=</span><span class="bp">True</span><span class="p">).</span><span class="nf">start</span><span class="p">()</span>

<span class="n">root</span> <span class="o">=</span> <span class="n">tk</span><span class="p">.</span><span class="nc">Tk</span><span class="p">()</span>
<span class="n">root</span><span class="p">.</span><span class="nf">title</span><span class="p">(</span><span class="sh">'</span><span class="s">X11 Canlı İmleç Tuvali</span><span class="sh">'</span><span class="p">)</span>
<span class="n">canvas</span> <span class="o">=</span> <span class="n">tk</span><span class="p">.</span><span class="nc">Canvas</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="n">WIDTH</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="n">HEIGHT</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="sh">'</span><span class="s">black</span><span class="sh">'</span><span class="p">)</span>
<span class="n">canvas</span><span class="p">.</span><span class="nf">pack</span><span class="p">()</span>

<span class="n">button</span> <span class="o">=</span> <span class="n">tk</span><span class="p">.</span><span class="nc">Button</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="sh">'</span><span class="s">Piksel Piksel Yürüt</span><span class="sh">'</span><span class="p">,</span> <span class="n">command</span><span class="o">=</span><span class="n">start_walk</span><span class="p">)</span>
<span class="n">button</span><span class="p">.</span><span class="nf">pack</span><span class="p">(</span><span class="n">fill</span><span class="o">=</span><span class="sh">'</span><span class="s">x</span><span class="sh">'</span><span class="p">)</span>

<span class="nf">draw_loop</span><span class="p">()</span>
<span class="n">root</span><span class="p">.</span><span class="nf">mainloop</span><span class="p">()</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">draw_loop</code> fonksiyonu kalp atışı gibidir. Her 16 milisaniyede bir imlecin nerede olduğunu sorar. Eğer önceki nokta biliniyorsa, iki nokta arasına çizgi çeker. Bu yüzden imleç hızlı hareket etse bile yol, kesik noktalar yerine çizgi segmentleri olarak görünür. <code class="language-plaintext highlighter-rouge">pixel_walk</code> ise imleci küçük adımlarla gezdirir; yani bizim minik robot ressamımızdır.</p>

<p>Koordinat dönüşümü önemlidir. Örnekte ekran koordinatlarını doğrudan tuval sınırlarına sıkıştırdık. Daha doğru bir yaklaşım için ekran boyutu $S_w \times S_h$, tuval boyutu $C_w \times C_h$ ise dönüşüm şöyle olabilir: $c_x = x \cdot C_w / S_w$, $c_y = y \cdot C_h / S_h$.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Avantaj</th>
      <th>Dezavantaj</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Doğrudan koordinat kullanımı</td>
      <td>Basit ve hızlı</td>
      <td>Büyük ekranlarda taşma olabilir</td>
    </tr>
    <tr>
      <td>Ölçekli dönüşüm</td>
      <td>Her ekrana uyumlu</td>
      <td>Biraz daha hesaplama ister</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">xinput</code> olay ayrıştırma</td>
      <td>Daha gerçek zamanlı</td>
      <td>Çıktı parse etmek zahmetli</td>
    </tr>
  </tbody>
</table>

<p>Bu proje X11’in ne kadar esnek olduğunu gösteren eğlenceli bir örnektir. Wayland oturumlarında güvenlik nedeniyle bu komutların bazıları çalışmayabilir; bu yüzden denemeyi X11 oturumunda yapmalısınız. Sonuçta birkaç komut satırı aracı, biraz Python ve basit vektör mantığıyla imleci hem hareket ettiren hem de izini çizen canlı bir dijital eskiz defteri elde etmiş olduk.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Proje" /><category term="x11" /><category term="xdotool" /><category term="xinput" /><category term="linux" /><category term="python" /><summary type="html"><![CDATA[Fare imlecini ekranda minik bir robot kalem gibi gezdirip, geçtiği yolu canlı olarak çizen bir tuval uygulaması kulağa biraz büyücülük gibi gelebilir. Aslında X11 dünyasında bu işin arkasında çok net bir mantık var: imleç konumunu oku, konumu bir önceki noktayla birleştir, gerekiyorsa imleci programatik olarak bir piksel kaydır ve tekrar et.]]></summary></entry><entry><title type="html">Definite Clause Grammars (DCG): Mantığın Dilbilgisiyle Dansı</title><link href="https://sonsuzus.github.io/posts/definite-clause-grammars-dcg-mantigin-dilbilgisiyle-dansi/" rel="alternate" type="text/html" title="Definite Clause Grammars (DCG): Mantığın Dilbilgisiyle Dansı" /><published>2026-07-20T00:00:00+00:00</published><updated>2026-07-20T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/definite-clause-grammars-dcg-mantigin-dilbilgisiyle-dansi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/definite-clause-grammars-dcg-mantigin-dilbilgisiyle-dansi/"><![CDATA[<p>Bir cümleyi okurken beynimiz kelimeleri tek tek saymaz; özne nerede, yüklem ne söylüyor, hangi sözcük hangi görevi üstlenmiş gibi yapısal ilişkileri hızla kurar. Definite Clause Grammars, yani DCG, tam da bu sezgisel süreci mantıksal kurallara dökmenin zarif bir yoludur. Özellikle Prolog dünyasında DCG, insan dilini parçalamak, cümleleri çözümlemek ve sembolik doğal dil işleme sistemleri kurmak için küçük ama güçlü bir laboratuvar gibidir.
``
DCG fikrinin temeli şudur: Bir dil, rastgele kelime dizilerinden değil, kurallı yapılardan oluşur. Örneğin Türkçede basit bir cümle için kabaca $Cümle \rightarrow Özne\ Yüklem$ diyebiliriz. İngilizce örnekte ise klasik gösterim $S \rightarrow NP\ VP$ şeklindedir. Burada S cümleyi, NP isim öbeğini, VP fiil öbeğini temsil eder. DCG bu üretim kurallarını Prolog cümleciklerine çevirir ve böylece dilbilgisi, sorgulanabilir bir mantık programına dönüşür.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th>Klasik Gramer</th>
      <th>DCG Yaklaşımı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kural</td>
      <td>$S \rightarrow NP\ VP$</td>
      <td><code class="language-plaintext highlighter-rouge">s --&gt; np, vp.</code></td>
    </tr>
    <tr>
      <td>Girdi</td>
      <td>Kelime dizisi</td>
      <td>Liste: <code class="language-plaintext highlighter-rouge">[the, cat, sleeps]</code></td>
    </tr>
    <tr>
      <td>Çıktı</td>
      <td>Ayrıştırma ağacı</td>
      <td>Mantıksal başarı veya yapı</td>
    </tr>
    <tr>
      <td>Çalışma biçimi</td>
      <td>Teorik üretim</td>
      <td>Prolog ile arama ve eşleme</td>
    </tr>
  </tbody>
</table>

<p>DCG, görünüşte basit bir sözdizimi sunar. Aşağıdaki mini gramer, İngilizce küçük cümleleri tanır:</p>

<div class="language-prolog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="ss">sentence</span> <span class="p">--&gt;</span> <span class="ss">noun_phrase</span><span class="p">,</span> <span class="ss">verb_phrase</span><span class="p">.</span>

<span class="ss">noun_phrase</span> <span class="p">--&gt;</span> <span class="ss">determiner</span><span class="p">,</span> <span class="ss">noun</span><span class="p">.</span>
<span class="ss">verb_phrase</span> <span class="p">--&gt;</span> <span class="ss">verb</span><span class="p">.</span>

<span class="ss">determiner</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">the</span><span class="p">].</span>
<span class="ss">determiner</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">a</span><span class="p">].</span>

<span class="ss">noun</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">cat</span><span class="p">].</span>
<span class="ss">noun</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">dog</span><span class="p">].</span>

<span class="ss">verb</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">sleeps</span><span class="p">].</span>
<span class="ss">verb</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">runs</span><span class="p">].</span>
</code></pre></div></div>

<p>Bu kodda <code class="language-plaintext highlighter-rouge">sentence --&gt; noun_phrase, verb_phrase.</code> kuralı, bir cümlenin önce isim öbeği sonra fiil öbeğinden oluştuğunu söyler. Köşeli parantez içindeki ifadeler ise doğrudan tüketilecek kelimelerdir. Prolog tarafında şu sorgu çalıştırılabilir:</p>

<div class="language-prolog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">?-</span> <span class="ss">phrase</span><span class="p">(</span><span class="ss">sentence</span><span class="p">,</span> <span class="p">[</span><span class="ss">the</span><span class="p">,</span> <span class="ss">cat</span><span class="p">,</span> <span class="ss">sleeps</span><span class="p">]).</span>
<span class="ss">true</span><span class="p">.</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">phrase/2</code>, verilen kelime listesinin gramer tarafından tamamen ayrıştırılıp ayrıştırılamadığını kontrol eder. Eğer liste kurallara uyuyorsa sonuç <code class="language-plaintext highlighter-rouge">true</code> olur. Uymuyorsa Prolog başka kural yollarını dener; bulamazsa başarısız olur. Bu noktada DCG’nin güzelliği ortaya çıkar: Ayrıştırma, aslında mantıksal çıkarımdır.</p>

<p>İşin teorik tarafında DCG, bağlamdan bağımsız gramerlere oldukça yakındır; fakat Prolog’un değişkenleri ve hedefleri sayesinde daha esnektir. Örneğin yalnızca cümleyi tanımak değil, aynı zamanda bir ayrıştırma ağacı üretmek de mümkündür:</p>

<div class="language-prolog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="ss">sentence</span><span class="p">(</span><span class="ss">s</span><span class="p">(</span><span class="nv">NP</span><span class="p">,</span> <span class="nv">VP</span><span class="p">))</span> <span class="p">--&gt;</span> <span class="ss">noun_phrase</span><span class="p">(</span><span class="nv">NP</span><span class="p">),</span> <span class="ss">verb_phrase</span><span class="p">(</span><span class="nv">VP</span><span class="p">).</span>

<span class="ss">noun_phrase</span><span class="p">(</span><span class="ss">np</span><span class="p">(</span><span class="nv">Det</span><span class="p">,</span> <span class="nv">Noun</span><span class="p">))</span> <span class="p">--&gt;</span> <span class="ss">determiner</span><span class="p">(</span><span class="nv">Det</span><span class="p">),</span> <span class="ss">noun</span><span class="p">(</span><span class="nv">Noun</span><span class="p">).</span>
<span class="ss">verb_phrase</span><span class="p">(</span><span class="ss">vp</span><span class="p">(</span><span class="nv">Verb</span><span class="p">))</span> <span class="p">--&gt;</span> <span class="ss">verb</span><span class="p">(</span><span class="nv">Verb</span><span class="p">).</span>

<span class="ss">determiner</span><span class="p">(</span><span class="ss">det</span><span class="p">(</span><span class="ss">the</span><span class="p">))</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">the</span><span class="p">].</span>
<span class="ss">noun</span><span class="p">(</span><span class="ss">n</span><span class="p">(</span><span class="ss">cat</span><span class="p">))</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">cat</span><span class="p">].</span>
<span class="ss">verb</span><span class="p">(</span><span class="ss">v</span><span class="p">(</span><span class="ss">sleeps</span><span class="p">))</span> <span class="p">--&gt;</span> <span class="p">[</span><span class="ss">sleeps</span><span class="p">].</span>
</code></pre></div></div>

<p>Sorgu şöyle olur:</p>

<div class="language-prolog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">?-</span> <span class="ss">phrase</span><span class="p">(</span><span class="ss">sentence</span><span class="p">(</span><span class="nv">Tree</span><span class="p">),</span> <span class="p">[</span><span class="ss">the</span><span class="p">,</span> <span class="ss">cat</span><span class="p">,</span> <span class="ss">sleeps</span><span class="p">]).</span>
<span class="nv">Tree</span> <span class="o">=</span> <span class="ss">s</span><span class="p">(</span><span class="ss">np</span><span class="p">(</span><span class="ss">det</span><span class="p">(</span><span class="ss">the</span><span class="p">),</span> <span class="ss">n</span><span class="p">(</span><span class="ss">cat</span><span class="p">)),</span> <span class="ss">vp</span><span class="p">(</span><span class="ss">v</span><span class="p">(</span><span class="ss">sleeps</span><span class="p">))).</span>
</code></pre></div></div>

<p>Bu ağaç, cümlenin yalnızca geçerli olduğunu değil, hangi parçalarının hangi dilbilgisel rolleri üstlendiğini de gösterir. Yani DCG, kelimeleri boncuk gibi dizmekten çok, onlardan anlamlı bir iskelet çıkarır.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Basit Regex</th>
      <th>DCG</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>İç içe yapı</td>
      <td>Zayıf</td>
      <td>Güçlü</td>
    </tr>
    <tr>
      <td>Dilbilgisel rol</td>
      <td>Genellikle yok</td>
      <td>Açıkça modellenir</td>
    </tr>
    <tr>
      <td>Geri izleme</td>
      <td>Sınırlı</td>
      <td>Prolog ile doğal</td>
    </tr>
    <tr>
      <td>Anlam bilgisi ekleme</td>
      <td>Zor</td>
      <td>Değişkenlerle kolay</td>
    </tr>
  </tbody>
</table>

<p>DCG’nin perde arkasında fark listeleri bulunur. Prolog, <code class="language-plaintext highlighter-rouge">s --&gt; np, vp.</code> gibi bir kuralı aslında giriş ve kalan listeyi taşıyan bir yapıya çevirir. Mantıksal olarak amaç şuna benzer: $phrase(Gramer, Girdi, Kalan)$. Eğer <code class="language-plaintext highlighter-rouge">Kalan = []</code> ise tüm cümle başarıyla tüketilmiştir. Bu sayede ayrıştırma verimli ve deklaratif kalır.</p>

<p>Elbette insan dili karmaşıktır: ekler, belirsizlikler, devrik cümleler, çok anlamlılık ve bağlam gibi canavarlar pusudadır. Yine de DCG, bu canavarlarla savaşmak için harika bir başlangıç kılıcıdır. Küçük bir sözlük, birkaç kural ve Prolog’un arama mekanizmasıyla kendi mini ayrıştırıcınızı yazabilirsiniz. Sonuçta DCG bize şunu öğretir: Dil yalnızca kelimelerden değil, kurallardan; programlama ise yalnızca komutlardan değil, ilişkilerden oluşur.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="prolog" /><category term="dcg" /><category term="doğal dil işleme" /><category term="mantıksal programlama" /><summary type="html"><![CDATA[Bir cümleyi okurken beynimiz kelimeleri tek tek saymaz; özne nerede, yüklem ne söylüyor, hangi sözcük hangi görevi üstlenmiş gibi yapısal ilişkileri hızla kurar. Definite Clause Grammars, yani DCG, tam da bu sezgisel süreci mantıksal kurallara dökmenin zarif bir yoludur. Özellikle Prolog dünyasında DCG, insan dilini parçalamak, cümleleri çözümlemek ve sembolik doğal dil işleme sistemleri kurmak için küçük ama güçlü bir laboratuvar gibidir.]]></summary></entry><entry><title type="html">Dinamik Veri Tabanı Yönetimi: Assert ve Retract ile Yaşayan Kurallar</title><link href="https://sonsuzus.github.io/posts/dinamik-veri-tabani-yonetimi-assert-ve-retract-ile-yasayan-kurallar/" rel="alternate" type="text/html" title="Dinamik Veri Tabanı Yönetimi: Assert ve Retract ile Yaşayan Kurallar" /><published>2026-07-20T00:00:00+00:00</published><updated>2026-07-20T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/dinamik-veri-tabani-yonetimi-assert-ve-retract-ile-yasayan-kurallar</id><content type="html" xml:base="https://sonsuzus.github.io/posts/dinamik-veri-tabani-yonetimi-assert-ve-retract-ile-yasayan-kurallar/"><![CDATA[<p>Bir programın çalışırken fikrini değiştirebilmesi kulağa biraz bilim kurgu gibi gelir, değil mi? Dinamik veri tabanı yönetimi tam olarak bunu sağlar: sistem, bellekte tuttuğu gerçekleri ve kuralları çalışma anında ekler, siler ve yeni duruma göre farklı sonuçlar üretir. Özellikle Prolog gibi mantıksal programlama dillerinde <code class="language-plaintext highlighter-rouge">assert</code> ve <code class="language-plaintext highlighter-rouge">retract</code>, programı sabit bir tarif defteri olmaktan çıkarıp mutfakta karar değiştirebilen bir şefe dönüştürür.
``
Klasik programlama yaklaşımında kod ve veri çoğu zaman ayrı düşünülür. Veritabanı değişir, ama programın kuralları genellikle sabittir. Mantıksal programlamada ise bilgi tabanı, yani knowledge base, gerçeklerden ve kurallardan oluşur. Basitçe şöyle yazabiliriz: $KB_t = F_t \cup R_t$. Burada $F_t$ belirli bir andaki gerçekler kümesini, $R_t$ ise kurallar kümesini temsil eder. Dinamik yönetimde amaç, $t$ anındaki bu yapıyı $t+1$ anında güncelleyebilmektir: $KB_{t+1} = KB_t + \Delta_{ekle} - \Delta_{sil}$.</p>

<p>Bu fikir özellikle uzman sistemlerde, oyun yapay zekasında, kural motorlarında ve interaktif asistanlarda çok değerlidir. Çünkü dünya değişir. Kullanıcı yeni bir bilgi verir, sensör farklı bir değer okur, oyunda karakterin canı azalır ya da bir stok ürünü tükenir. Sistem bu değişikliği yalnızca kaydetmekle kalmaz, çıkarım mekanizmasına da hemen yansıtır.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Veri/Kural Durumu</th>
      <th>Avantaj</th>
      <th>Risk</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Statik bilgi tabanı</td>
      <td>Program başında tanımlanır</td>
      <td>Basit ve tahmin edilebilir</td>
      <td>Değişen dünyaya uyum zayıf</td>
    </tr>
    <tr>
      <td>Dinamik bilgi tabanı</td>
      <td>Çalışma anında değişir</td>
      <td>Esnek ve etkileşimli</td>
      <td>Tutarsızlık yönetimi gerekir</td>
    </tr>
    <tr>
      <td>Harici veritabanı</td>
      <td>Program dışı saklanır</td>
      <td>Kalıcı ve büyük ölçekli</td>
      <td>Mantıksal çıkarımla entegrasyon zor olabilir</td>
    </tr>
  </tbody>
</table>

<p>Prolog tarafında <code class="language-plaintext highlighter-rouge">assert</code> yeni bir gerçeği veya kuralı belleğe ekler. <code class="language-plaintext highlighter-rouge">retract</code> ise eşleşen bir bilgiyi siler. Mini bir örnekle görelim:</p>

<div class="language-prolog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">:-</span> <span class="ss">dynamic</span> <span class="ss">hasta</span><span class="o">/</span><span class="m">2</span><span class="p">.</span>
<span class="p">:-</span> <span class="ss">dynamic</span> <span class="ss">riskli</span><span class="o">/</span><span class="m">1</span><span class="p">.</span>

<span class="c1">% Yeni bir hasta bilgisi ekler</span>
<span class="ss">hasta_ekle</span><span class="p">(</span><span class="nv">Isim</span><span class="p">,</span> <span class="nv">Yas</span><span class="p">)</span> <span class="p">:-</span>
    <span class="ss">assert</span><span class="p">(</span><span class="ss">hasta</span><span class="p">(</span><span class="nv">Isim</span><span class="p">,</span> <span class="nv">Yas</span><span class="p">)).</span>

<span class="c1">% Yaşı 65 üstü olanları riskli olarak işaretler</span>
<span class="ss">risk_degerlendir</span><span class="p">(</span><span class="nv">Isim</span><span class="p">)</span> <span class="p">:-</span>
    <span class="ss">hasta</span><span class="p">(</span><span class="nv">Isim</span><span class="p">,</span> <span class="nv">Yas</span><span class="p">),</span>
    <span class="nv">Yas</span> <span class="o">&gt;=</span> <span class="m">65</span><span class="p">,</span>
    <span class="ss">assert</span><span class="p">(</span><span class="ss">riskli</span><span class="p">(</span><span class="nv">Isim</span><span class="p">)).</span>

<span class="c1">% Hasta kaydı silinir</span>
<span class="ss">hasta_sil</span><span class="p">(</span><span class="nv">Isim</span><span class="p">)</span> <span class="p">:-</span>
    <span class="ss">retract</span><span class="p">(</span><span class="ss">hasta</span><span class="p">(</span><span class="nv">Isim</span><span class="p">,</span> <span class="nv">_</span><span class="p">)),</span>
    <span class="ss">retractall</span><span class="p">(</span><span class="ss">riskli</span><span class="p">(</span><span class="nv">Isim</span><span class="p">)).</span>
</code></pre></div></div>

<p>Bu kodda <code class="language-plaintext highlighter-rouge">:- dynamic hasta/2.</code> bildirimi önemlidir. Prolog’a bu ilişkinin çalışma zamanında değiştirilebileceğini söyler. <code class="language-plaintext highlighter-rouge">hasta_ekle(ayse, 70).</code> çağrıldığında belleğe yeni bir gerçek eklenir. Ardından <code class="language-plaintext highlighter-rouge">risk_degerlendir(ayse).</code> çalıştırılırsa sistem Ayşe için <code class="language-plaintext highlighter-rouge">riskli(ayse).</code> bilgisini üretir. Yani program yalnızca veri saklamaz; yeni veriye göre kendi bilgi tabanını genişletir.</p>

<p>Ancak burada tatlı bir tehlike var: Kontrolsüz <code class="language-plaintext highlighter-rouge">assert</code> kullanımı bilgi tabanını çorba yapabilir. Aynı gerçeği defalarca eklemek, eski bilgiyi silmeden yenisini yazmak veya çelişkili kuralları birlikte tutmak çıkarımları bozabilir. Örneğin hem <code class="language-plaintext highlighter-rouge">aktif(kullanici1)</code> hem de <code class="language-plaintext highlighter-rouge">pasif(kullanici1)</code> varsa sistemin ne yapacağı tasarıma bağlıdır. Bu yüzden dinamik sistemlerde tutarlılık kuralları gerekir. Mantıksal olarak bir çelişkiyi şöyle düşünebiliriz: $aktif(x) \land pasif(x) \rightarrow \bot$. Buradaki $\bot$, kabul edilemez durumu temsil eder.</p>

<table>
  <thead>
    <tr>
      <th>Komut</th>
      <th>Görev</th>
      <th>Tipik Kullanım</th>
      <th>Dikkat Edilecek Nokta</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">assert/1</code></td>
      <td>Bilgi ekler</td>
      <td>Yeni olay, kullanıcı tercihi</td>
      <td>Tekrarlı kayıt oluşabilir</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">retract/1</code></td>
      <td>İlk eşleşen bilgiyi siler</td>
      <td>Geçersizleşen durum</td>
      <td>Yanlış eşleşme silinebilir</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">retractall/1</code></td>
      <td>Tüm eşleşmeleri siler</td>
      <td>Temizlik, sıfırlama</td>
      <td>Fazla geniş desen risklidir</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dynamic/1</code></td>
      <td>Değişebilir ilişki tanımlar</td>
      <td>Dinamik predikatlar</td>
      <td>Bildirilmezse hata alınabilir</td>
    </tr>
  </tbody>
</table>

<p>Daha güvenli bir güncelleme için önce eski bilgiyi temizleyip sonra yenisini eklemek yaygın bir taktiktir:</p>

<div class="language-prolog highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">:-</span> <span class="ss">dynamic</span> <span class="ss">konum</span><span class="o">/</span><span class="m">2</span><span class="p">.</span>

<span class="c1">% Bir varlığın konumunu günceller</span>
<span class="ss">konum_guncelle</span><span class="p">(</span><span class="nv">Nesne</span><span class="p">,</span> <span class="nv">YeniKonum</span><span class="p">)</span> <span class="p">:-</span>
    <span class="ss">retractall</span><span class="p">(</span><span class="ss">konum</span><span class="p">(</span><span class="nv">Nesne</span><span class="p">,</span> <span class="nv">_</span><span class="p">)),</span>
    <span class="ss">assert</span><span class="p">(</span><span class="ss">konum</span><span class="p">(</span><span class="nv">Nesne</span><span class="p">,</span> <span class="nv">YeniKonum</span><span class="p">)).</span>

<span class="c1">% Örnek sorgu:</span>
<span class="c1">% konum_guncelle(robot1, depo).</span>
<span class="c1">% konum(robot1, Nerede).</span>
</code></pre></div></div>

<p>Burada amaç robotun aynı anda hem mutfakta hem depoda görünmesini engellemektir. Önce robotun eski konumu silinir, ardından güncel konumu eklenir. Bu desen, oyun karakterleri, IoT cihazları veya oturum yönetimi gibi birçok alanda işe yarar.</p>

<p>Sonuç olarak dinamik veri tabanı yönetimi, programa hafıza ve refleks kazandırır. <code class="language-plaintext highlighter-rouge">assert</code> ile öğrenen, <code class="language-plaintext highlighter-rouge">retract</code> ile unutan sistemler kurabiliriz. Ama her güçlü araç gibi bunun da disiplini vardır: değiştirilebilir predikatları açıkça tanımla, tutarsızlıkları engelle, eski bilgiyi temizle ve güncelleme kurallarını sade tut. Böylece programın sadece çalışan değil, yaşayan bir bilgi sistemine dönüşür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="prolog" /><category term="yapay-zeka" /><category term="veritabani-yonetimi" /><category term="mantiksal-programlama" /><summary type="html"><![CDATA[Bir programın çalışırken fikrini değiştirebilmesi kulağa biraz bilim kurgu gibi gelir, değil mi? Dinamik veri tabanı yönetimi tam olarak bunu sağlar: sistem, bellekte tuttuğu gerçekleri ve kuralları çalışma anında ekler, siler ve yeni duruma göre farklı sonuçlar üretir. Özellikle Prolog gibi mantıksal programlama dillerinde assert ve retract, programı sabit bir tarif defteri olmaktan çıkarıp mutfakta karar değiştirebilen bir şefe dönüştürür.]]></summary></entry></feed>