Bölüm 19:Son Söz
CPU'ya “Sen”i Katmak yazısının parçası: bilgisayarının programları nasıl çalıştırdığına doğru inen uzun bir teknik tavşan deliği.
Tüm bölümler
- Giriş
- Başlamadan Önce
- Temeller
- Kernel, User Mode ve Syscall
- Mimariler: x86, ARM ve Diğerleri
- Bellek Hiyerarşisi
- Cache Nasıl Çalışır
- Zamanı Dilimle
- İşlemciyi Hızlandıran Hileler
- Tahmin, Spekülasyon ve Spectre
- Bir Program Nasıl Çalıştırılır?
- Shell'den Kernel'e
- Bir ELF Ustasına Dönüşmek
- Bellek Aslında Sanal
- Adres Çevirisi ve TLB
- Bellek Güvenliği ve Sertleştirme
- Fork'lar ve COW'lar Hakkında Konuşalım
- Dosya Sistemi ve I/O
- Sanal Makineler ve Container'lar
- Son Söz
Tebrikler. Artık “sen”i CPU’nun içine epey sağlam bir şekilde yerleştirmiş olduk. Umarım keyif almışsındır.
Kapanırken tekrar vurgulamak istediğim şey şu: Bu kitaptaki bilgiler gerçek ve yaşayan şeyler. Bir dahaki sefere bilgisayarının nasıl aynı anda birden fazla uygulama çalıştırabildiğini düşündüğünde, umarım timer chip’leri ve hardware interrupt’ları gözünün önüne gelir. Süslü bir programlama diliyle bir şey yazıp linker hatası aldığında, o linker’ın aslında ne yapmaya çalıştığını da biraz olsun hissedersin.
Bu bölümde neyi kapatıyoruz?
- Büyük resmi tekrar zihne yerleştiriyoruz.
- Buradan sonra nereye gidileceğini konuşuyoruz.
- Ana anlatıya sığmayan birkaç notu paylaşıyoruz.
Büyük Resim: Bilgisayarın Açılışından Program Yürütmeye
Şimdiye kadar öğrendiklerimizi tek bir zincirde özetleyelim. Bilgisayarını açtığında şu sırayla olur:
- Firmware (BIOS/UEFI) donanımı başlatır ve bootloader’ı RAM’e yükler.
- Bootloader (GRUB vb.) kernel’ı diskten bulur, RAM’e yükler ve çalıştırır.
- Kernel (kernel mode’da) kendi sayfa tablolarını kurar, interrupt handler’larını hazırlar, driver’ları yükler.
- Kernel init process’ini (PID 1) başlatır. Artık user space’teyiz.
- Init (systemd, OpenRC vb.) servisleri ve grafik ortamını başlatır.
- Bir program çalıştırmak istendiğinde çoğu Unix-benzeri akışta parent process
fork()/clone()ailesiyle child üretir; bellek eşlemeleri COW sayesinde baştan kopyalanmaz. - Child
execve()ile yeni programa dönüşür; kernel ELF dosyasını okur, programın kod ve veri parçalarını sanal belleğe yerleştirir ve program dinamik kütüphanelere ihtiyaç duyuyorsa dynamic loader’ı devreye sokar. - Programın gördüğü sanal adresler, MMU + sayfa tabloları + TLB üçlüsü tarafından fiziksel RAM adreslerine çevrilir; demand paging sayesinde bazı sayfalar ancak ilk erişildiklerinde RAM’e gelir.
- CPU, user mode’da talimatları fetch-execute cycle ile yürütür; cache, pipeline, branch prediction ve çok çekirdek gibi teknikler bu yürütmeyi hızlandırır.
- Sistem çağrısı gerektiğinde syscall giriş yolu ile kernel mode’a geçilir, iş bitince user mode’a dönülür. Program bir dosya okuduğunda istek VFS üzerinden ilgili dosya sistemine iner ve çoğu zaman diske hiç gitmeden page cache’ten karşılanır; ağa yazdığındaysa istek socket katmanından ağ yığınına, oradan da ağ kartının sürücüsüne geçer.
- Birden fazla program aynı anda çalışıyorsa timer interrupt ve scheduler başka process/thread’e geçer (context switch).
- Bütün bu resim bir kez daha katlanabilir: hypervisor aynı donanımın üstünde birden fazla kernel çalıştırır, container’lar ise tek bir kernel’ın üstüne namespace, cgroup, seccomp ve capability katmanları ekleyerek izolasyon kurar.
- Tüm bu akış boyunca NX, stack canary, ASLR, PIE ve RELRO arka planda çalışır; bir bellek hatası olduğunda onu sömürmeyi zorlaştıran katmanlar bunlardır.
Bu zincirin her halkasını artık biliyorsun. Kendi kendine “Peki ya…?” diye sormaya devam et; işin en güzel yanı her cevabın yeni bir soru getirmesi.
Buradan Sonra Nereye?
Bu kitap bir haritaydı; her kutunun içi kendi başına bir alan. Merakının çektiği yöne göre birkaç somut yol:
- Ölçmeye devam et. Bu kitapta öğrendiğin her mekanizmanın kendi makinende bir izi var.
stracebir programın yaptığı syscall’ları,perfişlemcinin donanım sayaçlarını,/procise kernel’ın o process hakkında bildiği her şeyi gösterir. Bir web sunucusunustrace -cile izlemek, çoğu teoriden daha öğreticidir. - Kernel kaynağını aç. Yazı boyunca bağlantı verdiğimiz dosyalar gerçek:
fs/exec.ciçindekido_execveat_common,kernel/fork.ciçindekicopy_process. Artık bu fonksiyonların ne yaptığını biliyorsun, okumak sandığından kolay. - Küçük bir şey yaz. Kendi shell’ini (
fork+execve+waitpid), kendimalloc’unu ya daLD_PRELOADile bir fonksiyonu araya giren küçük bir kütüphane. Yüz satırlık bir deneme, bin satırlık okumadan fazlasını öğretir. - Diğer tarafa bak. Bu anlatı Unix dünyasına ait. Windows’un NT çekirdeği, macOS’un XNU’su ve WebAssembly gibi yeni yürütme modelleri aynı soruları farklı cevaplarla karşılıyor.
Klasik kaynaklar için: Kerrisk’in The Linux Programming Interface’i user space tarafının başvuru kitabı, Bovet ve Cesati’nin Understanding the Linux Kernel’i kernel tarafının klasiği, Gregg’in Systems Performance’ı ise ölçme ve performans konusunda alanın standardıdır.
Bu kitabın aslı Lexi Mattick ve Hack Club’ın eseri; Türkçe adaptasyonu ve güncel notları ben yazdım. İçerikte bir hata, bir eksik ya da daha iyi anlatabileceğim bir yer görürsen Türkçe adaptasyon reposunda issue veya PR aç, istersen doğrudan bana yaz. Özgün metnin kendisiyle ilgili geri bildirimleri ise kaynak projenin kendi iletişim kanallarına iletmen daha doğru olur.

… ama dur, daha bitmedi.
Bonus: Küçük Notlar
Şimdi de ana anlatının akışına sığmayan, ama bilmene değecek birkaç not. Bunları kasten sona sakladım.
Linux kullanıcılarının çoğunun yeterince ilginç bir hayatı vardır; page table’ların kernel içinde nasıl temsil edildiğini hayal etmeye fazla zaman ayırmazlar.
Hardware interrupt’lar için alternatif bir görselleştirme:

Bazı syscall’lar kernel space’e hiç sıçramaz. Bunun adı vDSO (virtual dynamic shared object) ve kitabın syscall bölümünü okuduktan sonra mantığı çok net.
Kernel, her process’in adres alanına küçük bir paylaşımlı kütüphane eşler. İçinde clock_gettime ve gettimeofday gibi birkaç fonksiyon vardır ve bu fonksiyonların okuduğu veri — mesela o anki zaman — kernel’ın sürekli güncellediği paylaşımlı bir sayfada durur. Yani program saati sorduğunda kernel’a girmez; kendi adres alanındaki bir sayfadan okur ve çıkar.
Kazanç, 2. bölümde konuştuğumuz mode geçişinin tamamen ortadan kalkması. Saniyede milyonlarca kez zaman soran bir programda bu fark ölçülebilir bir hızlanmadır — nitekim vDSO’nun var olma sebebi de tam olarak böyle programlardır.
Ayrıntısına girmek istersen: Wikipedia özeti, vdso(7) man sayfası ve linux-insides bölümü.
Unix’e özgü detaylar konusunda son bir dürüstlük notu: anlatının önemli bir kısmı Unix dünyasına ait. macOS ya da Linux kullanıyorsan bu gayet iyi, ama aynı CPU mimarisi üzerinde dursa bile Windows’un programları nasıl yürüttüğü ya da syscall’ları nasıl yönettiği hakkında sana doğrudan aynı resmi vermez. İleride bu işin Windows tarafını anlatan ayrı bir yazı yazmayı çok isterim.