Bölüm 15: İşletim Sistemi Tasarımı
Giriş
Öğrenme Çıktıları
İşletim sistemi tasarımını zorlaştıran etkenleri ve bir işletim sisteminin dört temel hedefini açıklamak.
Basitlik, bütünlük ve verimlilik ilkelerini; yürütme ve veri paradigmalarını sistem çağrısı örnekleriyle tartışmak.
Katmanlı, exokernel, mikroçekirdek tabanlı ve genişletilebilir yapıları ödünleşimleriyle karşılaştırmak.
Mekanizma/politika ayrımını, dikliği, iki düzeyli adlandırmayı, bağlama zamanını ve statik/dinamik yapıları uygulamak.
Dolaylılık, yeniden girişlilik, uzay-zaman takası, önbellek, ipucu ve yaygın durumu hızlandırma tekniklerini örneklerle kullanmak.
Brooks yasasını ve ekip yapısını yorumlamak; sanallaştırma, çok çekirdek, Rust, eBPF ve unikernel eğilimlerini değerlendirmek.
Giriş
İşletim Sistemi Tasarlamak Neden Zor?
Linux ≈ 40 milyon satır; kimse bütünü kavrayamaz.
Aygıtlar, kesmeler, iş parçacıkları aynı anda: yarış, kilitlenme.
Bazıları sistemi bozmak, veri çalmak ister; her girdi şüpheli.
Kullanıcılar ayrılmalı, ama seçtikleriyle bilgi paylaşabilmeli.
20-30 yıl yaşar; gelecekteki donanım ve uygulamalar öngörülmeli.
Nasıl kullanılacağı bilinmez: telefondan süper bilgisayara.
Farklı işlemci ve platformlar: x86-64, ARM64, RISC-V…
Eski programlar çalışmalı; yeni fikirler eski kısıtlarla sınırlanır.
Tanenbaum’ın sekiz etkeni. Uzun bakım-onarım ihtiyacı 5. ve 8. maddelerin doğal sonucudur.
Giriş · Adım adım
İşletim Sisteminin Hedefleri
1. Soyutlamalar tanımlamak (abstractions): süreç, adres uzayı, dosya, soket gibi kavramlar donanımın karmaşıklığını gizler. Tasarımın en zor kısmı doğru soyutlamayı bulmaktır.
2. İlkel işlemler sağlamak (primitives): bu soyutlamalar üzerinde çalışan işlemler, yani sistem çağrıları. Örn. dosya için open/read/write/close.
3. Yalıtım sağlamak (isolation): kullanıcılar ve süreçler birbirine karışamamalı; ama denetimli paylaşım (dosya izinleri, paylaşılan bellek) mümkün olmalı.
4. Donanımı yönetmek (hardware): kesmeler, zamanlayıcılar, MMU, aygıt denetleyicileri; düşük seviyeli işler çoğunlukla bu katmanda yapılır.
Arayüz Tasarımı
Arayüz Tasarımı İçin Yol Gösterici İlkeler
“Mükemmelliğe, eklenecek bir şey kalmadığında değil, çıkarılacak bir şey kalmadığında ulaşılır.” (A. de Saint-Exupéry). Az sayıda, iyi düşünülmüş kavram öğrenmesi, gerçekleştirmesi ve doğrulaması kolaydır.
“Her şey olabildiğince basit olmalı, ama daha basit değil.” (Einstein’a atfedilir). Arayüz, kullanıcıların ihtiyaç duyduğu her şeyi yapabilmelidir. Az mekanizma ile çok iş: MINIX 3 çekirdeği iletişim için yalnızca birkaç ilkel (send, receive, sendrec, notify) sunar.
Bir özellik ya da sistem çağrısı verimli gerçekleştirilemiyorsa muhtemelen sahip olmaya değmez. Programcı bir çağrının yaklaşık maliyetini sezebilmelidir: lseek ucuz, read pahalı olabilir.
Arayüz Tasarımı
“Küçük Güzeldir”
- Küçük güzeldir: az sayıda genel çağrı, çok sayıda özel çağrıdan iyidir.
- seL4’ün çekirdeği yaklaşık 10 bin satırdır ve biçimsel olarak doğrulanmıştır; milyonlarca satırlık bir çekirdekte bu mümkün değil.
- Linux’ta çağrı sayısı her yıl birkaç tane artar (ör.
io_uring_*,landlock_*,mseal). - Windows’ta belgelenmiş arayüz Win32 API’dir; çekirdek çağrıları sürümden sürüme değişir.
Arayüz Tasarımı
Kullanıcı Arayüzü Paradigmaları
Kabuk, metin komutları, borular. Otomasyon ve uzaktan yönetim için hâlâ vazgeçilmez.
Pencere, simge, menü, işaretçi (Windows, Icons, Menus, Pointer). Fare tuşlarının anlamı her uygulamada aynı olmalı.
Hareketler (kaydır, yakınlaştır), tam ekran uygulamalar, bildirim merkezi.
Sesli asistanlar ve doğal dil arayüzleri; işletim sistemi hizmetlerine yeni bir giriş noktası.
Arayüz Tasarımı
Yürütme Paradigmaları
(a) Algoritmik kod
int main(void) { int x, y; init(); do_something(); read(fd, &x, sizeof x); do_something_else(); write(fd, &y, sizeof y); keep_going(); exit(0); }
Program akışı program belirler; işletim sistemine hizmet için çağrı yapar. Derleyici, sıralayıcı, toplu işler.
(b) Olay güdümlü kod (event-driven)
int main(void) { msg_t msg; init(); while (get_message(&msg)) { switch (msg.type) { case 1: ... ; break; case 2: ... ; break; case 3: ... ; break; } } }
Akışı dış olaylar (tuş, tıklama, paket, zamanlayıcı) belirler. GUI’ler, sunucular, mikroçekirdek sunucuları.
Yürütme paradigmasının seçimi, sistem çağrılarını da biçimlendirir: algoritmik model engelleyen (blocking) read ister; olay güdümlü model ise “sıradaki olayı ver” türünden bir çağrı (get_message, epoll_wait, io_uring_enter) ister.
Arayüz Tasarımı · Kodu adım adım çalıştır
Olay Döngüsü Nasıl Çalışır?
int main(void) { msg_t m; init(); while (get_message(&m)) { switch (m.type) { case KEY: on_key(&m); break; case CLICK: on_click(&m); break; case TIMER: on_timer(&m); break; } } return 0; }
Program başlar, init() pencereyi ve kaynakları hazırlar. Kuyruk boş.
Kullanıcı tıklar, bir tuşa basar, zamanlayıcı dolar. Olayları işletim sistemi/pencere sistemi toplar ve uygulamanın kuyruğuna ekler.
get_message sıranın başındaki CLICK’i döndürür; switch onu on_click’e yönlendirir.
İşleyici kısa sürede biter ve döngü başa döner. Sıradaki olay KEY: on_key çalışır.
TIMER olayı on_timer’a gider (ör. imleci yakıp söndürmek). Uzun süren bir işleyici tüm arayüzü dondurur; uzun işler ayrı iş parçacığına verilir.
Kuyruk boş: get_message engellenir, süreç uyur ve CPU harcamaz. Yeni bir olay onu uyandırır.
Kullanıcı pencereyi kapatır: kuyruğa QUIT olayı gelir.
QUIT için get_message 0 döndürür, döngü biter, program sonlanır. Aynı kalıp nginx/Node.js (epoll) ve mikroçekirdek sunucularında da kullanılır.
Arayüz Tasarımı
Veri Paradigmaları
- Ortak bir veri paradigması, aynı aracın (
cat,dd, yönlendirme) her kaynakta çalışmasını sağlar:cat /proc/loadavg. - Bedeli: bazı aygıtlar dosya modeline sığmaz; o zaman her şey
ioctlgibi “kaçış kapılarına” yığılır.
Arayüz Tasarımı
Sistem Çağrısı Arayüzü
- Ayrımı koru: çağrı az ve genel, kolaylık fonksiyonları kütüphanede. Yeni bir kolaylık eklemek çekirdeği değiştirmez.
- Bağımsız bileşenler: UNIX süreç oluşturmayı ikiye böler:
fork+execve. Arada yönlendirme,chdirvb. yapılabilir.
- Windows’ta
CreateProcesstek çağrıda 10 parametre alır: güçlü ama karmaşık. - Modern eğilim:
posix_spawn(fork maliyetini önler) ve toplu çağrı arayüzleri (io_uring: tek geçişte çok sayıda istek).
Sistem Yapısı
Sistem Yapısı Seçenekleri
| Yapı | Çekirdekte ne var? | Artısı | Eksisi | Örnek |
|---|---|---|---|---|
| Monolitik | Her şey tek adres uzayında | Hızlı, doğrudan çağrı | Bir hata tüm sistemi düşürür | Linux, FreeBSD |
| Katmanlı | Hiyerarşik katmanlar | Modüler, katman katman test | Katman sırası ve geçiş maliyeti | THE (1968), MULTICS |
| Exokernel | Yalnızca güvenli kaynak paylaştırma | Uygulamaya özel soyutlama | Kütüphane OS karmaşıklığı | Aegis/ExOS (MIT) |
| Mikroçekirdek | IPC, zamanlama, bellek eşleme | Güvenilirlik, yalıtım | Mesaj (IPC) maliyeti | MINIX 3, QNX, seL4, Zircon |
| Genişletilebilir | Çekirdek + güvenli eklentiler | Çalışırken uyarlanır | Eklentinin güvenliği | SPIN, Linux eBPF |
| Hibrit | Mikroçekirdek fikri, monolitik uygulama | Pragmatik denge | Saf modelin yararları kısmen kaybolur | Windows NT, macOS XNU |
Sistem Yapısı · Adım adım
Katmanlı Sistemler: Yedi Katmanlı Bir Tasarım
Tanenbaum’ın önerdiği yedi katmanlı tasarım. 1. katman donanım farklılıklarını gizler; üst katmanlar hep alttakinin sunduğu soyutlamayla çalışır.
Uygulama read çağırır; tuzak 7. katmana gelir. Parametreler (fd geçerli mi, tampon kullanıcı alanında mı?) burada denetlenir.
Dosya sistemi (ext4) dosya ofsetini disk bloğuna çevirir. Blok önbellekte yoksa alt katmanlardan istenir.
Sanal bellek, verinin konacağı sayfa önbelleği çerçevesini ayırır ve kullanıcı tamponunun eşlemesini doğrular.
NVMe sürücüsü okuma komutunu aygıtın kuyruğuna yazar.
Çağıran iş parçacığı G/Ç bitene kadar engellenir; zamanlayıcı başka bir iş parçacığını çalıştırır.
Aygıt bitince kesme üretir; 2. katman kesmeyi işler, uyuyan iş parçacığını hazır yapar. Sonuç katmanlar boyunca yukarı döner.
Her katman yalnızca altındakini kullanır; arayüzler iyi tanımlıysa bir katmandaki değişiklik diğerlerini etkilemez ve katmanlar alttan üste tek tek test edilir. Bedeli: katman sırasını seçmek ve geçiş maliyeti. Kaba görünüm: uygulama → kütüphane → sistem çağrısı → çekirdek → donanım.
Sistem Yapısı
Exokernel
- Donanım ile yazılım arasında en ince soyutlama katmanı.
- Kaynaklar ayrıntılı (blok, sayfa, zaman dilimi) düzeyinde ayrılır; soyutlamalar (dosya, süreç) kullanıcı alanındaki kütüphane OS’ta.
- Sanal makineden farkı: her VM’e makinenin kopyası değil, kaynağın bir bölümü verilir; yeniden eşleme katmanı gerekmez.
- Bedeli: tasarım ve uygulama karmaşıklığı, uygulamalar arası paylaşımın zorluğu. Fikri bugün unikernel’lerde yaşıyor.
Düzeltme: eski sunumdaki “çekirdek bileşenlerinin dinamik yüklenmesi” exokernel’e değil, genişletilebilir sistemlere ait bir özelliktir.
Sistem Yapısı · Adım adım
Mikroçekirdek Tabanlı İstemci-Sunucu Sistemleri
Çekirdek olabildiğince küçük: yalnızca süreç yönetimi, süreçler arası iletişim (IPC) ve temel bellek eşleme. Dosya sistemi, sürücüler, hatta bellek politikası kullanıcı süreçleridir.
İstemci bir dosya okumak ister: dosya sunucusuna mesaj gönderir. Bu, çekirdeğe yapılan tek tür çağrıdır.
Mikroçekirdek mesajı dosya sunucusunun adres uzayına kopyalar (veya eşler) ve onu çalıştırır.
Dosya sunucusu bloğu disk sürücüsünden ister; o da bir kullanıcı sürecidir. Aygıt yazmaçlarına erişim çekirdeğin verdiği izinlerle olur.
Yanıt aynı yoldan istemciye döner. Her istek birden çok bağlam anahtarlama ve kopyalama demektir: performans bedeli.
Kazanç: sürücü çökerse yalnız o süreç ölür, çekirdek ve diğer sunucular çalışmaya devam eder.
MINIX 3’te yeniden doğuş sunucusu (reincarnation server) çöken sürücüyü fark edip yeniden başlatır: kendini onaran sistem.
Örnekler: Mach (macOS’taki XNU melez bir türevidir), QNX (otomotiv), L4 ailesi ve biçimsel doğrulanmış seL4, MINIX 3, Google Fuchsia’nın Zircon çekirdeği. Sunucular ağdaki farklı düğümlerde de çalışabilir; ancak “mesajlaşma kendiliğinden güvenlidir” demek abartıdır: güvenlik, çekirdeğin yetki denetimine bağlıdır.
Sistem Yapısı
Genişletilebilir Sistemler
- Çekirdek temel işlevleri sağlar; değişen ihtiyaçlar için çalışırken yeni bileşen eklenir.
- Üçüncü taraflar sisteme özellik ekleyebilir; uyumluluk ve güvenlik en büyük sorundur.
- Klasik modül tam yetkilidir: bir hata çekirdeği çökertir. SPIN (1995) eklentileri güvenli dil (Modula-3) ile sınırladı.
- Bugün Linux’ta eBPF: doğrulanmış küçük programlar; izleme, ağ, güvenlik ve 6.12’den beri zamanlayıcı (sched_ext).
Sistem Yapısı
Çekirdek İş Parçacıkları
- Çekirdek tarafından oluşturulur ve yönetilir (
kthread_create); kullanıcı adres uzayları yoktur, yalnızca çekirdek belleğinde çalışırlar. - Normal iş parçacığı gibi zamanlanır, engellenebilir, uyuyabilir: kesme işleyicisinde yapılamayan uzun işler buraya ertelenir.
- Bağlam anahtarlama ucuz: adres uzayı (sayfa tablosu) değişmez.
- Örnekler: ertelenmiş iş (kworker), yumuşak kesmeler (ksoftirqd), sayfa geri kazanma (kswapd), günlük yazma (jbd2).
ps -efçıktısında köşeli parantez içinde görünürler:[kswapd0].
Düzeltme: kullanıcı süreçleri çekirdek iş parçacığı oluşturamaz; sistem çağrıları yalnızca çekirdeğin zaten çalıştırdığı iş parçacıklarına iş verebilir (ör. io_uring’in iş havuzu).
Tasarım İlkeleri · Adım adım
Mekanizma ve Politikanın Ayrılması
Çekirdek yalnızca bir mekanizma sunar: “en yüksek öncelikli hazır iş parçacığını çalıştır”. Şu an T3 (yedekleme, öncelik 8) seçiliyor.
Veritabanı süreci kendi iş parçacıklarını en iyi bilen taraftır: sorguların önemli, yedeklemenin bekleyebileceğine karar verir. Bu bir politikadır ve bir sistem çağrısıyla aktarılır.
Çekirdek kodu hiç değişmeden artık T2’yi seçer. Aynı mekanizma, farklı politikayla farklı davranış üretir.
Başka bir uygulama başka bir politika uygular. Linux 6.12’deki sched_ext bunu bir adım öteye taşır: zamanlama politikasının tamamı bir eBPF programı olarak yüklenebilir.
Diğer örnekler: sayfalama (mekanizma: MMU, çerçeve tahsisi; politika: hangi sayfa çıkarılsın), CPU frekansı (mekanizma: cpufreq sürücüsü; politika: schedutil/powersave yönetici), güvenlik (mekanizma: LSM kancaları; politika: SELinux kuralları).
Tasarım İlkeleri
Diklik (Orthogonality)
- Diklik (orthogonality): ayrı kavramları bağımsız olarak birleştirebilme. Birini değiştirmek diğerini etkilemez.
- Linux
clone: neyin paylaşılacağı ayrı ayrı bayraklarla seçilir. Süreç ve iş parçacığı aynı mekanizmanın iki ucudur; konteynerler deCLONE_NEW*bayraklarıyla kurulur. - Windows: süreç bir kaynak kabıdır, iş parçacığı ise zamanlama birimi; iki kavram ayrıdır.
- C dilinde de örnek:
static(ömür) ile tür bağımsız seçilir.
Tasarım İlkeleri
Adlandırma: İki Düzeyli Adlar
| Harici ad | Eşleyen | Dahili ad |
|---|---|---|
/usr/ast/mbox | dizin | i-node numarası |
www.giresun.edu.tr | DNS | IP adresi → (ARP) MAC adresi |
dosya tanımlayıcı 3 | süreç dosya tablosu | çekirdekteki struct file |
| sanal adres | sayfa tablosu | fiziksel çerçeve |
\Device\Harddisk0 | nesne yöneticisi | çekirdek nesnesi (HANDLE) |
Ad, insanların hatırlayabileceği biçimde (ASCII/Unicode dizgisi) seçilir; sistem içinde ise hızlı ve benzersiz bir sayı kullanılır. Aradaki eşleme, adı değiştirmeden nesneyi taşımaya olanak verir.
Tasarım İlkeleri · Adım adım
Ad Çözümleme Adım Adım
Çekirdek open("/usr/ast/mbox") çağrısını alır. Yol bileşenlere ayrılır; hiçbir şey önbellekte yoksa her bileşen için diskten okunmak gerekir.
Kök dizinin i-node’u (i-node 1) okunur, ardından kök dizinin verisi (blok 1). Doğrusal arama: usr → i-node 6. 2 okuma.
/usr için i-node 6 okunur, sonra /usr dizini (blok 132). ast → i-node 26. 4 okuma.
/usr/ast için i-node 26 okunur, sonra dizin bloğu 406. mbox → i-node 60. 6 okuma.
Son olarak dosyanın kendi i-node’u (60) okunur. Sadece dosyayı bulmak için 7 disk erişimi gerekti: bu yüzden önbellek şart (bkz. Performans bölümü).
Tasarım İlkeleri
Bağlama Zamanı
Ad ile nesne arasındaki ilişki önceden sabitlenir. Basit ve hızlı, ama az esnek: program yalnızca belirli adreste çalışır, kütüphane güncellenince yeniden derlenir.
İlişki mümkün olan en son anda kurulur. Daha karmaşık (ek dolaylılık, çalışma zamanı maliyeti) ama daha esnek: sayfa nereye yüklenirse yüklensin, kütüphane güncellenince program değişmez.
Bağlama zamanı, “bir ad hangi anda gerçek nesneye bağlanır?” sorusudur. İşletim sistemleri genel olarak geç bağlamayı seçer; örneğin sanal bellek, bir sayfanın fiziksel yerini her erişimde (TLB yardımıyla) yeniden çözer.
Tasarım İlkeleri · Kodu adım adım çalıştır
Statik ve Dinamik Yapılar
found = 0; for (p = &proc_table[0]; p < &proc_table[PROC_TABLE_SIZE]; p++) { if (p->proc_pid == pid) { found = 1; break; } }
Statik süreç tablosu: altı giriş, bazıları boş (pid 0). Aranan pid 42. found sıfırlanır.
İşaretçi tablonun başına konur. proc_table[0]’ın pid’i 1: eşleşmez.
p++ bir sonraki yapıya ilerler (bayta değil). 314 ≠ 42.
Boş giriş (pid 0) da tek tek taranır: doğrusal aramanın bedeli.
Eşleşme: found = 1, break ile döngüden çıkılır. En kötü durumda tüm tablo taranır: O(n). Küçük ve sabit bir tablo için bu basit kod yeterlidir.
Statik tablo: basit, ayırıcı gerektirmez, ama dolunca yeni süreç oluşturulamaz (eski UNIX’te NPROC). Dinamik yapı (bağlı liste, karma tablosu, Linux’ta xarray): esnek ve aramada O(1)’e yakın, ama bellek ayırıcı, parçalanma ve karmaşık kilitleme getirir. Ara yol: boyutu ayarlanabilir tablolar (/proc/sys/kernel/pid_max).
Tasarım İlkeleri
Yukarıdan Aşağı ve Aşağıdan Yukarı Gerçekleştirme
Önce sistem çağrısı işleyicileri yazılır, alt katmanlar koçan (stub) olarak bırakılır. Arayüz erken netleşir; ama tüm sistem bitmeden gerçekçi test zordur.
Önce donanımı gizleyen katman, kesmeler, zamanlayıcı, sonra iş parçacıkları… Her katman bittiğinde test edilir. Tanenbaum gerçekleştirmede bu yolu önerir; tasarım ise yukarıdan aşağı düşünülmelidir.
Tasarım İlkeleri
Senkron ve Asenkron İletişim
- Senkron: programlaması kolay, akış sıralı okunur; ama her istek için bir iş parçacığı bekler.
- Mikroçekirdeklerde IPC genelde senkrondur (L4, seL4): tampon gerekmez, çok hızlıdır.
- Asenkron: tek iş parçacığı binlerce isteği yürütebilir; ama yarış, hata yönetimi ve tamponlama zorlaşır.
- Linux
io_uring: gönderim (SQ) ve tamamlanma (CQ) halkaları, sistem çağrısı olmadan bile istek gönderilebilir.
Yararlı Teknikler
Donanımı Gizleme
CPU türüne bağlı koşullu derleme
#include "config.h" void init(void) { #if (CPU == X86_64) /* x86-64 başlatma kodu */ #endif #if (CPU == ARM64) /* ARM64 başlatma kodu */ #endif #if (CPU == RISCV64) /* RISC-V başlatma kodu */ #endif }
Sözcük uzunluğuna bağlı derleme
#include "config.h" #if (WORD_LENGTH == 32) typedef int Register; #endif #if (WORD_LENGTH == 64) typedef long Register; #endif Register R0, R1, R2, R3; /* bugün: <stdint.h> */ typedef uintptr_t Register;
arch/x86, arch/arm64, arch/riscv dizinleri. Yeni bir işlemciye taşırken yalnız bu kısım yeniden yazılır. Koşullu derleme ölçülü kullanılmalı: her yere dağılan #ifdef kodu okunmaz hâle getirir.Kodlar Tanenbaum’daki Pentium/UltraSPARC örneğinin güncellenmiş hâlidir.
Yararlı Teknikler · Adım adım
Dolaylılık (Indirection)
Kullanıcı U’nun sağındaki tuşa basar. Klavye karakter değil, tuşun konumunu bildirir.
Klavye 0x17 tarama kodunu gönderir (USB klavyelerde HID kullanım kodu; sürücü ortak bir koda çevirir). ABD eşlemesiyle bu i olur.
Yalnızca tablo Türkçe Q ile değiştirilir: aynı tuş artık ı üretir. Donanım ve uygulama değişmedi.
Türkçe F eşlemesinde aynı tuş n olur. Ek bir adım (dolaylılık) esneklik sağlar; bedeli bir tablo okuması.
“Bilgisayar bilimindeki her sorun, bir dolaylılık düzeyi daha ile çözülebilir.” (David Wheeler). İşletim sistemlerinde: sanal bellek (sayfa tablosu), dosya tanımlayıcıları, sembolik bağlar, i-node, aygıt sınıfları.
Yararlı Teknikler
Yeniden Kullanım ve Yeniden Girişlilik
Aynı kodu farklı yerlerde kullanmak: tek bir bit eşlem (bitmap) modülü boş disk bloklarını, bellek çerçevelerini ve PID’leri izleyebilir. Linux’ta ortak list.h, kırmızı-siyah ağaç, xarray.
Bir kod parçası aynı anda iki kez çalışabilmelidir: biri yürütülürken kesme gelip aynı fonksiyonu çağırabilir ya da başka bir çekirdek aynı kodu çalıştırır. Kural: paylaşılan statik değişken yok, gerekiyorsa kilit.
char *strtok(char *s, const char *d) { static char *last; /* paylaşılan durum! */ ... } /* yeniden girişli sürüm: durum çağıranda */ char *strtok_r(char *s, const char *d, char **saveptr);
strtok içindeki static değişken, iki iş parçacığı ya da bir kesme işleyicisi aynı anda kullanınca bozulur.
Yararlı Teknikler
Kaba Kuvvet ve Önce Hata Denetimi
Kaba kuvvet (brute force)
- Basit ama “yavaş” görünen çözüm çoğu zaman yeterlidir: 50 girişlik bir tabloda doğrusal arama, karma tablosundan hızlı ve hatasız olabilir.
- Karmaşık algoritma ancak ölçüm gerekli gösterirse: n büyükse veya kod sıcak yoldaysa.
- Sıralama: küçük dizilerde ekleme sıralaması, büyüklerde birleştirme/yığın sıralaması.
Önce hataları denetle
Kötü: kaynak alındıktan sonra denetim
lock(&tbl_lock); buf = kmalloc(n, GFP_KERNEL); if (n > MAX || !access_ok(u, n)) { kfree(buf); unlock(&tbl_lock); return -EINVAL; }
İyi: önce denetim, sonra kaynak
if (n > MAX || !access_ok(u, n)) return -EINVAL; /* önce denetle */ buf = kmalloc(n, GFP_KERNEL); if (!buf) return -ENOMEM; lock(&tbl_lock); ...
Parametreleri kilit ve bellek almadan önce doğrulamak, hata yolundaki geri alma kodunu (ve unutulan unlock hatalarını) ortadan kaldırır.
Performans
Performans: Neden Yavaş, Neyi Hızlandırmalı?
Özellik şişkinliği: her sürüm yeni özellik ekler, eskisini çıkaramaz. Geriye uyumluluk katmanları, güvenlik önlemleri (ör. Spectre/Meltdown yamaları sistem çağrısı maliyetini artırdı), genellik uğruna eklenen dolaylılık.
Yalnızca ölçümle darboğaz olduğu gösterilen kısımlar. “İyi yeterlidir”: %5’lik kazanç için kodu iki kat karmaşıklaştırmak çoğu zaman değmez.
“Erken optimizasyon tüm kötülüklerin anasıdır.” (D. Knuth). Ölçme araçları: perf, eBPF tabanlı bpftrace, alev grafikleri.
Uzay-zaman takası · önbellekleme · ipuçları · yerelliği kullanmak · yaygın durumu hızlandırmak. Sonraki slaytlarda her biri örnekleniyor.
Performans · Kodu adım adım çalıştır
Uzay-Zaman Takası: (a) Döngüyle Bit Sayma
#define BYTE_SIZE 8 int bit_count(int byte) { int i, count = 0; for (i = 0; i < BYTE_SIZE; i++) if ((byte >> i) & 1) count++; return count; }
Örnek bayt 0xB7 (183). Döngüyle her bit tek tek sınanacak; sayaç sıfırdan başlar.
(byte >> 0) & 1 = 1: sayaç artar.
Kaydır, maskele, karşılaştır: her bit için birkaç komut ve bir dal.
Bit 2 de 1.
Bit 3 sıfır: dal alınmaz, sayaç değişmez.
Bit 4 ve 5 de 1.
Bit 6 sıfır.
Son bit 1. Döngü 8 kez döndü.
Sonuç 6. Bellek yok denecek kadar az, ama bayt başına yaklaşık 8 tur × birkaç komut. Daha hızlı olabilir mi? Bir sonraki slayt: yer harcayıp zaman kazanmak.
Performans
Uzay-Zaman Takası: (b) Makro ve (c) Tablo
/* (b) makro: döngü yok */ #define bit_count(b) ((b&1) + ((b>>1)&1) + \ ((b>>2)&1) + ... + ((b>>7)&1)) /* (c) tablo: 256 bayt */ char bits[256] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ...}; #define bit_count(b) (int) bits[b]
| Yöntem | Bellek | Bayt başına iş |
|---|---|---|
| (a) döngü | ~0 | 8 tur: kaydır, maskele, dallan |
| (b) makro | ~0 | ~24 komut, dal yok |
| (c) 256’lık tablo | 256 B | 1 bellek okuması |
| 16’lık tablo (yarım bayt) | 16 B | 2 okuma + toplama |
| POPCNT komutu | 0 | 1 komut (__builtin_popcount) |
Performans
Uzay-Zaman Takası: Renk Paleti
| 1024 × 768 görüntü | Boyut (bayt) | Oran |
|---|---|---|
| (a) 24 bit/piksel | 2.359.296 (2,25 MiB) | 1 |
| (b)+(c) 8 bit indeks + palet | 786.432 + 768 = 787.200 | ≈ 1/3 |
Yer yaklaşık 3 kat azalır; bedeli her piksel gösterilirken bir tablo bakışı (zaman) ve en fazla 256 renk sınırıdır. GIF ve PNG-8 bu yöntemi kullanır (GIF ayrıca LZW ile sıkıştırır). Boyutlar Python ile hesaplanmıştır.
Performans
Önbelleğe Almak (Caching)
Yol → i-node önbelleği
| Yol | i-node |
|---|---|
/usr | 6 |
/usr/ast | 26 |
/usr/ast/mbox | 60 |
/usr/ast/books | 92 |
/usr/bal | 45 |
/usr/bal/paper.ps | 85 |
- Önbellek yokken
/usr/ast/mbox’ı bulmak: kök i-node’u, kök dizini, /usr i-node’u, /usr dizini, /usr/ast i-node’u, /usr/ast dizini → 6 disk okuması (+ dosyanın i-node’u). - Önbellekte tam yol varsa: bir bellek araması, sıfır disk erişimi.
- Önbellekte yalnızca
/usr/astvarsa, arama oradan devam eder: kısmi isabet de kazançtır. - Linux: dizin girdisi önbelleği (dcache), i-node önbelleği ve sayfa önbelleği. Boş RAM’in “dolu” görünmesinin nedeni budur.
- Önbelleğin bedeli: tutarlılık. Dosya silinir veya yeniden adlandırılırsa kayıt geçersiz kılınmalıdır.
Performans
İpuçları (Hints)
Önbellekteki değer her zaman doğru olmalıdır. İpucu yanlış olabilir; bu yüzden geçersiz kılma gerekmez, ama kullanmadan önce ucuz bir doğrulama şarttır.
Dağıtık dosya sisteminde “dosya muhtemelen sunucu B’de”; web’de yönlendirme önbelleği; dal tahmini; uygulamadan çekirdeğe madvise(MADV_SEQUENTIAL) ile verilen erişim ipucu.
Performans
Yerelliği Kullanmak (Exploiting Locality)
- Zamansal yerellik: yakın zamanda kullanılan tekrar kullanılır (önbelleğin temeli). Uzamsal yerellik: yakınındaki de kullanılır (önden okuma, büyük sayfalar).
- Birlikte kullanılan verileri yan yana koymak: FFS silindir grupları, ext4 blok grupları; NUMA’da belleği iş parçacığının düğümünden ayırmak.
Performans · Adım adım
Yaygın Durumu Hızlandırmak
Kilitlerin büyük çoğunluğu çekişmesiz alınır. Yaygın durum bu ise, hızlandırılması gereken de budur.
A, atomik karşılaştır-değiştir (CAS) ile 0’ı 1 yapar. Çekirdeğe hiç girilmez: birkaç nanosaniye.
Nadir durum: B de kilidi ister, CAS başarısız olur. Değeri 2 yapar ve futex(FUTEX_WAIT) ile çekirdekte uyur. Yavaş yol yalnızca burada.
A bırakırken eski değer 2 ise futex(FUTEX_WAKE) çağırır; 1 olsaydı hiç çağırmazdı. B uyanır ve kilidi alır.
Genel ilke (Lampson): yaygın durumu hızlı, nadir durumu doğru yap. Diğer örnekler: sistem çağrılarının hızlı yolu, vDSO ile çekirdeğe girmeden clock_gettime, TLB isabeti.
Proje Yönetimi · Adım adım
Efsanevi Adam-Ay (The Mythical Man-Month)
“Adam-ay” (man-month) yanıltıcı bir birimdir: iş, kişiler arasında bölünebilir varsayar. 2 kişi arasında yalnızca 1 iletişim yolu var.
4 kişide 6 yol. Her yeni kişi herkesle eşgüdüm kurmak zorunda; toplantı, belge ve arayüz anlaşmaları artar.
6 kişide 15 yol. Gecikmiş projeye eklenen kişiyi eğitmek, mevcut ekibin zamanını da tüketir.
8 kişide 28 yol. Brooks yasası: “Gecikmiş bir yazılım projesine eleman eklemek, onu daha da geciktirir.”
Brooks’un OS/360 deneyiminden önerdiği zaman dağılımı: 1/3 planlama, 1/6 kodlama, 1/4 modül testi, 1/4 sistem testi. Yani kod yazmak işin yalnızca altıda biridir.
Proje Yönetimi
Takım Yapısı, Deneyim ve “Gümüş Kurşun Yok”
Mills/Brooks’un cerrah ekibi: baş programcı tasarlar ve kodlar, diğerleri destekler. Kavramsal bütünlük birkaç kişide kalır.
İyi programcı ortalamadan kat kat verimlidir. “Birini atmayı planlayın”; ikinci sistem etkisine dikkat.
Brooks (1986): araçlar yalnız arızi karmaşıklığı azaltır, özsel karmaşıklık kalır. Yapay zekâ destekli kodlama da tasarım sorumluluğunu kaldırmaz.
Eğilimler
İşletim Sistemlerinde Eğilimler
| Eğilim | İşletim sistemine etkisi |
|---|---|
| Sanallaştırma ve bulut | Hipervizör, konteyner, mikro-VM (Firecracker); çok kiracılı yalıtım |
| Çok çekirdekli yongalar | Ölçeklenebilir kilitler, RCU, NUMA farkındalığı, heterojen çekirdekler |
| Geniş adres uzayı | 64 bit, 5 düzeyli sayfa tabloları; tek adres uzaylı sistem fikirleri |
| Kesintisiz veri erişimi | Ağ, bulut depolama ve yerel disk arasında şeffaf erişim; NVMe, CXL bellek |
| Pille çalışan sistemler | DVFS, “hızla bitir ve uyu” (race-to-idle), Android Doze, enerji farkında zamanlama (EAS) |
| Gömülü ve sensör düğümleri | Gerçek zamanlı küçük çekirdekler: Zephyr, FreeRTOS, Contiki-NG; kilobaytlarca bellek |
| Güvenli diller ve programlanabilir çekirdek | Linux’ta Rust (6.1’den beri), eBPF ve sched_ext, unikernel’ler |
Eğilimler
Sanallaştırma ve Bulut
- Sunucu birleştirme, hızlı taşıma, anlık görüntü; bulutun temeli.
- İşletim sistemi tasarımına etkisi: konuk sistem sanal ortamda olduğunu bilir (paravirtio sürücüleri).
- Konteynerler çekirdeği paylaşır: daha hafif, daha zayıf yalıtım.
- Mikro-VM’ler (Firecracker, AWS Lambda): VM yalıtımı, milisaniyede açılış.
Eğilimler
Çok Çekirdek ve Geniş Adres Uzayları
Sunucularda yuva başına 128-192 çekirdek, telefonlarda büyük/küçük (big.LITTLE) karışık çekirdekler. Tek bir büyük çekirdek kilidi ölçeklenmez: ince taneli kilitler, RCU, çekirdek başına veri yapıları gerekir.
Tüm dosyaları belleğe eşlemek (mmap) ya da tek adres uzaylı sistemler mümkün olur. Bedeli: daha derin sayfa tablosu yürüyüşü, daha fazla TLB baskısı.
Eğilimler
Rust, eBPF ve Unikernel’ler
Linux 6.1 (Aralık 2022) ile Rust desteği ana dala girdi, ardından ilk Rust sürücüleri geldi. Amaç: kullanım-sonrası-serbest bırakma ve taşma gibi bellek hatalarını derlemede önlemek. Windows çekirdeğinde de Rust bileşenleri var.
Doğrulanmış küçük programlarla çekirdeği yeniden derlemeden genişletmek: izleme, ağ (XDP), güvenlik, zamanlama (sched_ext).
Exokernel’in libOS fikrinin bulut sürümü: MirageOS, Unikraft. Tek uygulamalı mikro hizmetler için.
Özet
Özet
| Konu | Ana fikir |
|---|---|
| Zorluklar | Boyut, eşzamanlılık, düşman kullanıcılar, uzun ömür, uyumluluk |
| Hedefler | Soyutlama, ilkel işlemler, yalıtım, donanım yönetimi |
| Arayüz | Basit, bütün, verimli; az ve genel sistem çağrısı |
| Yapı | Katmanlı, exokernel, mikroçekirdek, genişletilebilir, çekirdek iş parçacıkları |
| İlkeler | Mekanizma/politika, diklik, iki düzeyli ad, geç bağlama |
| Teknikler | Donanımı gizle, dolaylılık, yeniden girişlilik, önce hata denetimi |
| Performans | Ölç; uzay-zaman takası, önbellek, ipucu, yerellik, yaygın durum |
| Proje | Brooks yasası, küçük güçlü takımlar, gümüş kurşun yok |
| Eğilimler | Bulut, çok çekirdek, enerji, Rust, eBPF, unikernel |
Özet
Kontrol Soruları
- Tanenbaum’a göre işletim sistemi tasarımını zorlaştıran sekiz etkenden dördünü açıklayın.
- Basitlik, bütünlük ve verimlilik ilkelerini birer sistem çağrısı örneğiyle tartışın.
- Algoritmik ve olay güdümlü yürütme paradigmalarını karşılaştırın. Olay döngüsünde uzun süren bir işleyici neden sorun olur?
- Exokernel ile sanal makine monitörü arasındaki temel fark nedir?
- Mikroçekirdekte bir sürücü çöktüğünde ne olur? Monolitik çekirdekte aynı durum neden daha tehlikelidir?
- Mekanizma ile politikayı ayırmayı zamanlayıcı örneğiyle açıklayın. sched_ext bu ilkeyi nasıl uygular?
- /usr/ast/mbox yolunun çözümlenmesi önbelleksiz kaç disk erişimi gerektirir? Önbellek ile ipucu arasındaki fark nedir?
- Bit sayma örneğinde döngü, makro ve tablo yöntemlerini yer ve zaman açısından karşılaştırın.
- futex neden yaygın durumu hızlandırmanın iyi bir örneğidir?
- Brooks yasasını iletişim yolu sayısıyla gerekçelendirin: 10 kişilik ekipte kaç yol vardır?
Cevaplar
- Örn. devasa boyut, eşzamanlılık, düşmanca kullanıcılar, yalıtıma rağmen paylaşım, uzun ömür, genellik, taşınabilirlik, geriye dönük uyumluluk.
- Basitlik: UNIX’in az sayıda çağrısı; bütünlük: exec ailesinin tek execve ile her ihtiyacı karşılaması; verimlilik: lseek ucuz, read pahalı; maliyet sezilebilmeli.
- Algoritmik: akışı program belirler. Olay güdümlü: dış olaylar belirler. Tek iş parçacıklı döngüde uzun işleyici bitene kadar diğer olaylar işlenemez, arayüz donar.
- VM’e makinenin tam bir kopyası verilir; exokernel kaynakları bölüştürür, kimin neye sahip olduğunu izler, yeniden eşleme gerekmez; soyutlamaları libOS sağlar.
- Yalnız sürücü süreci ölür; yeniden doğuş sunucusu onu yeniden başlatır. Monolitik çekirdekte sürücü çekirdek adres uzayındadır; hatası tüm sistemi çökertir.
- Çekirdek “en yüksek öncelikliyi seç” mekanizmasını sunar; öncelikleri uygulama belirler. sched_ext ile politika eBPF programı olarak yüklenir, çekirdek mekanizması aynı kalır.
- 6 okuma (3 i-node + 3 dizin bloğu), dosyanın i-node’u ile 7. Önbellek her zaman doğrudur; ipucu yanlış olabilir, kullanmadan önce doğrulanır.
- Döngü: yer yok, 8 tur. Makro: yer yok, dal yok ama ~24 komut. Tablo: 256 bayt, tek okuma. Donanımda POPCNT tek komuttur.
- Çekişmesiz kilit kullanıcı alanında tek atomik işlemle alınır; çekirdeğe yalnız nadir çekişme durumunda girilir.
- 10·9/2 = 45 yol. Eklenen her kişi iletişim yükünü karesel artırır ve eğitim için mevcut ekibin zamanını tüketir.
Özet
Kaynaklar
- A. S. Tanenbaum, H. Bos, Modern Operating Systems, 4. baskı (2014) / 5. baskı (2022), Pearson — Bölüm 12: Operating System Design.
- B. W. Lampson, “Hints for Computer System Design”, ACM SOSP, 1983 (ve 2020 güncellemesi “Hints and Principles for Computer System Design”).
- F. P. Brooks Jr., The Mythical Man-Month: Essays on Software Engineering, 20. yıl baskısı, Addison-Wesley, 1995 — “No Silver Bullet” (1986) dahil.
- J. H. Saltzer, M. F. Kaashoek, Principles of Computer System Design: An Introduction, Morgan Kaufmann, 2009.
- D. R. Engler, M. F. Kaashoek, J. O’Toole, “Exokernel: An Operating System Architecture for Application-Level Resource Management”, SOSP 1995.
- G. Klein vd., “seL4: Formal Verification of an OS Kernel”, SOSP 2009; J. N. Herder vd., MINIX 3 güvenilirlik çalışmaları.
- Linux çekirdeği belgeleri: Rust (docs.kernel.org/rust), BPF ve sched_ext; H. Franke vd., “Fuss, Futexes and Furwocks”, 2002.