CPU'ya“Sen”iKatmak 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.
Bölüm 2:
Zamanı Dilimle
GitHub'da düzenle
Diyelim ki bir işletim sistemi yazıyorsun ve kullanıcıların aynı anda birden fazla program çalıştırabilmesini istiyorsun. Ama elinde çok çekirdekli havalı bir işlemci yok; dolayısıyla CPU aynı anda yalnızca tek bir talimat yürütebiliyor.
Neyse ki akıllı bir işletim sistemi geliştiricisisin. CPU’yu işlemler arasında sırayla paylaştırarak sahte paralellik yaratabileceğini fark ediyorsun. İşlemler arasında hızlı hızlı geçiş yapar ve her birine az miktarda çalışma süresi verirsen, CPU’yu tek bir işlem işgal etmeden sistem duyarlı kalabilir.
Peki program kodu çalışırken kontrolü geri nasıl alacaksın? Biraz araştırınca, çoğu bilgisayarda timer chip’ler bulunduğunu keşfediyorsun. Belirli bir süre geçince işletim sisteminin interrupt handler’ına geçişi tetikleyecek şekilde bu timer chip’leri programlayabiliyorsun.
Donanım Interrupt’ları
Bir önceki bölümde, kontrolü user-space programından işletim sistemine aktarmak için software interrupt’ların nasıl kullanıldığını görmüştük. Bunlara “software” interrupt denmesinin nedeni, bir program tarafından gönüllü olarak tetiklenmeleridir; yani normal fetch-execute cycle içinde çalışan makine kodu, CPU’ya kontrolü kernel’a bırakmasını söyler.

İşletim sistemi scheduler’ları, PIT gibi timer chip‘leri kullanarak multitasking için hardware interrupt üretir:
- Program koduna geçmeden önce işletim sistemi, timer chip’i belirli bir süre sonra interrupt tetikleyecek şekilde ayarlar.
- İşletim sistemi user mode’a geçer ve programın bir sonraki talimatına atlar.
- Süre dolunca timer chip, kernel mode’a geçişi ve işletim sistemi kodunun devreye girmesini sağlayan bir hardware interrupt üretir.
- İşletim sistemi artık mevcut programın durumunu kaydedebilir, başka bir programı yükleyebilir ve aynı döngüyü sürdürebilir.
Buna preemptive multitasking denir; bir işlemin zorla kesilmesine de preemption denir. Bu makaleyi tarayıcıda okurken aynı anda müzik de dinliyorsan, bilgisayarın muhtemelen bu döngüyü saniyede binlerce kez yapıyordur.
Timeslice Hesabı
Timeslice, scheduler’ın bir işlemin kesilmeden önce çalışmasına izin verdiği süredir. Timeslice seçmenin en basit yolu, her işleme aynı süreyi vermek ve sırayla hepsi arasında dönmektir. Buna fixed-timeslice round-robin scheduling denir.
Küçük bir jargon notu
Timeslice için bazen “quantum” dendiğini biliyor muydun? Artık biliyorsun ve bunu uygun bir ortamda kullanarak teknik arkadaşlarını etkileyebilirsin. Bu makale boyunca her cümlede kuantum kelimesini kullanmadığım için takdir bekliyorum.
Linux kernel geliştiricileri ayrıca sabit frekanslı timer tick’lerini saymak için jiffy adlı zaman birimini kullanır. Timeslice uzunlukları da dâhil olmak üzere birçok şey bununla ölçülür. Linux’ta jiffy frekansı tipik olarak 1000 Hz’dir, ama kernel derlenirken değiştirilebilir.
Sabit timeslice yaklaşımının küçük ama önemli bir geliştirmesi, target latency değerini belirlemektir. Target latency, makul sayıda işlem varken bir işlemin preempt edildikten sonra tekrar CPU görmesi için geçmesini istediğin ideal en uzun süredir. Bunu kafada canlandırmak biraz zor; birazdan diyagramla daha net olacak.
Timeslice süresi, target latency’nin toplam görev sayısına bölünmesiyle hesaplanabilir. Bu, sabit süre vermekten daha iyidir; çünkü çalışan işlem sayısı az olduğunda gereksiz context switch’leri azaltır. Örneğin target latency 15 ms ve toplam 10 işlem varsa, her işlem yaklaşık 1,5 ms çalışır. Yalnızca 3 işlem varsa her biri 5 ms’lik daha uzun bir timeslice alır ve yine aynı target latency içinde sıraya girmiş olur.
Context switch pahalıdır; çünkü mevcut programın tüm durumunu kaydetmeyi ve başka bir programın durumunu geri yüklemeyi gerektirir. Çok küçük timeslice’lar, işlemler gereğinden sık değiştiği için performansı düşürebilir. Bu yüzden scheduler’larda genelde bir minimum granularity sınırı bulunur. Bu sınır devreye girdiğinde target latency aşılabilir, ama sistem aşırı sık context switch yapmaktan kurtulur.
Bu makale yazıldığında Linux scheduler’ı 6 ms target latency ve 0,75 ms minimum granularity kullanıyordu.

Bu temel timeslice hesabıyla çalışan round-robin scheduling, günümüz makinelerinin yaptıklarına fena halde yakındır. Yine de biraz naiftir; çoğu işletim sistemi işlem önceliklerini, deadline’ları ve başka sinyalleri de dikkate alan daha karmaşık scheduler’lar kullanır. Linux, 2007’den beri Completely Fair Scheduler kullanıyor. CFS, görevleri önceliklendirmek ve CPU süresini bölüştürmek için daha sofistike tekniklere başvurur.
İşletim sistemi bir işlemi her preempt ettiğinde, yeni programın kayıtlı yürütme bağlamını da yüklemesi gerekir. Buna bellek bağlamı da dâhildir. Bu, CPU’ya sanal adresleri fiziksel adreslere çevirirken farklı bir page table kullanmasını söyleyerek yapılır. Programların birbirlerinin belleğine erişmesini engelleyen şey de budur; bu tavşan deliğine Bölüm 5 ve Bölüm 6‘da gireceğiz.
Not #1: Kernel’in Preempt Edilebilirliği
Şu ana kadar yalnızca user-space işlemlerin preempt edilmesinden söz ettik. Oysa bir syscall’ın işlenmesi ya da driver kodunun yürütülmesi çok uzun sürerse, kernel kodu da programları yavaşlatabilir.
Linux dâhil modern kernel’lar preemptive kernel yaklaşımını benimser. Yani gerektiğinde kernel kodunun kendisi de kesilebilir ve scheduler tarafından başka bir işe geçilebilir.
Kernel ya da driver yazmıyorsan bunu ayrıntılı bilmen şart değil, ama okuduğum neredeyse her kaynak buna değiniyordu; ben de zinciri bozmamak istedim. Fazladan bağlam nadiren zarardır.
Not #2: Kısa Bir Tarih Dersi
Eski işletim sistemleri, buna klasik Mac OS ve NT öncesi bazı Windows sürümleri de dâhil, preemptive multitasking yerine onun öncülü olan başka bir modeli kullanıyordu. İşletim sisteminin “şimdi seni keseceğim” demesi yerine, programların kendisi gönüllü olarak kontrolü bırakıyordu. Yani bir software interrupt tetikleyip “istersen şimdi başka bir programı çalıştırabilirsin” diyordu. İşletim sisteminin kontrolü geri alıp başka sürece geçmesinin tek yolu buydu.
Buna cooperative multitasking denir. Büyük kusurları vardır: kötü niyetli ya da kötü yazılmış programlar tüm sistemi kolayca dondurabilir ve zaman hassasiyetinin önemli olduğu işlerde kararlı davranış elde etmek neredeyse imkânsızdır. Bu yüzden teknoloji dünyası uzun zaman önce preemptive multitasking’e geçti ve geriye dönüp bakmadı.
3. bölüme devam et: Bir Program Nasıl Çalıştırılır?