Ana içeriğe geç

Bölüm 19:Son Söz

5 dakikalık okumaGüncelleme:

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:

  1. Firmware (BIOS/UEFI) donanımı başlatır ve bootloader’ı RAM’e yükler.
  2. Bootloader (GRUB vb.) kernel’ı diskten bulur, RAM’e yükler ve çalıştırır.
  3. Kernel (kernel mode’da) kendi sayfa tablolarını kurar, interrupt handler’larını hazırlar, driver’ları yükler.
  4. Kernel init process’ini (PID 1) başlatır. Artık user space’teyiz.
  5. Init (systemd, OpenRC vb.) servisleri ve grafik ortamını başlatır.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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.
  11. Birden fazla program aynı anda çalışıyorsa timer interrupt ve scheduler başka process/thread’e geçer (context switch).
  12. 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.
  13. 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:

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.

Son.

… 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.

Jonathan Corbet, LWN

Hardware interrupt’lar için alternatif bir görselleştirme:

Assembly talimatlarını konuşma balonlarıyla gösteren dört panelli bir meme çizimi. Dışarıdan gelen başka bir ses normal akışı bozuyor ve son panelde küçük kuş hiç memnun görünmüyor.

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.