Ders 03 / 21
Alt Yapı Teknolojileri
Konteyner yalıtımını kuran üç mekanizma ayrı ayrı modellenir: ad alanı bir görünürlük eşlemesi, denetim grubu bir pay dağıtıcı, birleşim dosya sistemi bir katman yığını olarak; her birinin bedeli ve delindiği yer sayılır.
İçindekiler
Önceki ders konteynerin beş ölçütten dördünü ayırdığını yazdı: dosya sistemi, ağ, süreç tablosu ve kullanıcı. Nasıl ayırdığını sormadı. Soru boş değildir, çünkü ayrılan şeyler tek makinede tek tanedir — süreç tablosu bir tanedir, bellek bir tanedir, disk bir tanedir. “Ayırmak” burada ikinci bir tane üretmek olamaz; başka bir şey olmalıdır.
Bu ders o başka şeyi üç ayrı mekanizma olarak modeller. Ad alanı aynı tablonun üzerine bir görünürlük eşlemesi koyar. Denetim grubu aynı kaynağı pay olarak dağıtır ve sınır aşıldığında bir karar verir. Birleşim dosya sistemi aynı dosyaları bir yığın olarak üst üste koyar. Üçünün ortak yanı şudur: hiçbiri yeni bir kaynak üretmez, üçü de tek bir kaynağın üzerine bir kural koyar. Kuralın olduğu yerde delik de vardır ve her bölüm deliği adıyla yazar.
KK13. Üç mekanizma ayrı ayrı modellenir; birlikte çalıştıkları hâl modellenmez. KK14. Ad alanı modelinde tek bir küresel tablo vardır ve görünüm hiçbir kaydı silmez.
Ad Alanı: Bir Görünürlük Eşlemesi
// olcum-agi/adalani.mjs — ad alani bir gorunurluk eslemesi olarak (model) // Tek bir kuresel surec tablosu vardir; ad alani o tablonun uzerine bir // gorunurluk eslemesi koyar. Kayit silinmez, yalniz gorunmez olur. const TABLO = [ // kuresel tablo: numara, ad, sahip ad alani [1, "yonetici", "kok"], [17, "gunluk-toplayici", "kok"], [41, "zamanlayici", "kok"], [102, "okuma-toplayici", "A"], [103, "okuma-isleyici", "A"], [110, "okuma-kuyrugu", "A"], [121, "dogrulayici", "A"], [140, "dogrulama-onbellegi", "A"], [205, "faturalama", "B"], [206, "fatura-yazici", "B"], [219, "tarife-cozucu", "B"], [233, "is-emri", "B"], [301, "gecelik-is", "C"], [305, "gecelik-yazici", "C"], ]; // Gorunurluk kurali: bir ad alani yalniz kendi kayitlarini gorur ve onlari // 1'den baslayan yerel numaralarla numaralandirir. Kok her kaydi gorur. function gorunum(adAlani) { const kayitlar = adAlani === "kok" ? TABLO : TABLO.filter((k) => k[2] === adAlani); return kayitlar.map(([kuresel, ad], i) => ({ yerel: i + 1, kuresel, ad })); } const ALANLAR = ["kok", "A", "B", "C"]; console.log(`kuresel tabloda ${TABLO.length} kayit var; hicbir gorunum kayit silmez`); console.log("ad alani".padEnd(10) + "goren".padEnd(8) + "gormeyen".padEnd(10) + "yerel -> kuresel"); for (const a of ALANLAR) { const g = gorunum(a); console.log(a.padEnd(10) + `${g.length}/${TABLO.length}`.padEnd(8) + String(TABLO.length - g.length).padEnd(10) + g.slice(0, 3).map((k) => `${k.yerel}->${k.kuresel}`).join(" ") + (g.length > 3 ? " ..." : "")); } console.log("\nayni kayit iki numara tasir:"); for (const a of ["A", "B", "C"]) { const ilk = gorunum(a)[0]; console.log(` ${ilk.ad.padEnd(20)} ${a} icinde ${ilk.yerel}, kok icinde ${ilk.kuresel}`); } const HAVUZ = 32768; console.log(`\ndelik: numara havuzu ortaktir (${HAVUZ} numara, ${TABLO.length} kullanimda)`); const enBuyuk = ALANLAR.slice(1) .map((a) => [a, gorunum(a).length]).sort((x, y) => y[1] - x[1])[0]; console.log(` kok ${gorunum("kok").length} kaydin hepsini gorur; ad alanlari birbirini gormez`); console.log(` en cok kayit acan ad alani ${enBuyuk[0]} (${enBuyuk[1]} kayit); ` + `havuzu tuketen ad alani digerlerini de durdurur`); console.log(` gorunmeyen kayit yine de kaynak tuketir: kok gorunumunde ` + `${TABLO.length - gorunum("A").length} kayit A'nin disinda ama ayni makinede`);
kuresel tabloda 14 kayit var; hicbir gorunum kayit silmez ad alani goren gormeyen yerel -> kuresel kok 14/14 0 1->1 2->17 3->41 ... A 5/14 9 1->102 2->103 3->110 ... B 4/14 10 1->205 2->206 3->219 ... C 2/14 12 1->301 2->305 ayni kayit iki numara tasir: okuma-toplayici A icinde 1, kok icinde 102 faturalama B icinde 1, kok icinde 205 gecelik-is C icinde 1, kok icinde 301 delik: numara havuzu ortaktir (32768 numara, 14 kullanimda) kok 14 kaydin hepsini gorur; ad alanlari birbirini gormez en cok kayit acan ad alani A (5 kayit); havuzu tuketen ad alani digerlerini de durdurur gorunmeyen kayit yine de kaynak tuketir: kok gorunumunde 9 kayit A'nin disinda ama ayni makinede
Ad alanı bir silme değil, bir filtredir. On dört kaydın hepsi tabloda durur; A görünümü beşini, B dördünü, C ikisini görür. Aynı kayıt iki numara taşır — okuma toplayıcı A içinde 1’dir, kök içinde 102 — ve bu ikilik yalıtımın tam olarak neye dayandığını gösterir: numaranın kendisi değil, numaranın hangi tablodan okunduğu ayrılmıştır.
Deliğin üç adı vardır. Kök görünümü on dört kaydın hepsini görür; yalıtım aşağı doğru değil, yalnız yana doğru çalışır. Numara havuzu ortaktır: 32.768 numaranın tümü paylaşılır ve havuzu tüketen bir ad alanı diğerlerinin kayıt açmasını da engeller. Ve görünmeyen kayıt yok değildir — A’nın görmediği dokuz kayıt aynı makinede işlemci ve bellek tüketmeyi sürdürür. Ad alanının bedeli bayt değil, bir eşleme tablosudur; kapattığı ortam farkı ise sıfırdır.
Denetim Grubu: Bir Pay Dağıtıcı
KK15. Sert sınırlı kaynak ile esnek paylaşılan kaynak ayrı modellenir; istekler sırayla gelir. KK16. Sınır aşıldığında verilen karar kaynağın türüne bağlıdır.
// olcum-agi/denetimgrubu.mjs — denetim grubu bir pay dagitici olarak (model) // Iki ayri davranis modellenir: sert sinirli kaynakta sinir asilinca verilen // KARAR, esnek paylasilan kaynakta agirliga gore yapilan DAGITIM. const GRUP = { // kurgu bolgesel olcum aginin uc grubu toplama: { bellekSiniriMB: 512, agirlik: 20, islemciTalebi: 60 }, faturalama: { bellekSiniriMB: 768, agirlik: 50, islemciTalebi: 90 }, gecelikIs: { bellekSiniriMB: 256, agirlik: 30, islemciTalebi: 80 }, }; // Sert sinir: her istek sirayla gelir; sinir asilirsa karar kaynagin turune baglidir. const KARAR = { bellek: "istegi yapan surec durdurulur", tanitici: "istek reddedilir" }; const ISTEK = { // MB cinsinden ardisik bellek istekleri toplama: [180, 140, 120, 90], faturalama: [300, 260, 150, 200], gecelikIs: [120, 100, 90], }; console.log("sert sinir: bellek (MB)"); console.log("grup".padEnd(12) + "sinir".padEnd(8) + "verilen".padEnd(9) + "sigmayan".padEnd(11) + "karar (bellek)"); let toplamVerilen = 0, toplamSigmayan = 0; for (const [ad, istekler] of Object.entries(ISTEK)) { const sinir = GRUP[ad].bellekSiniriMB; let kullanim = 0, sigmayan = []; for (const i of istekler) (kullanim + i <= sinir) ? (kullanim += i) : sigmayan.push(i); toplamVerilen += kullanim; toplamSigmayan += sigmayan.length; console.log(ad.padEnd(12) + String(sinir).padEnd(8) + String(kullanim).padEnd(9) + `${sigmayan.length} istek`.padEnd(11) + (sigmayan.length ? KARAR.bellek : "-")); } console.log(`toplam verilen ${toplamVerilen} MB, sigmayan ${toplamSigmayan} istek; ` + `sinirlarin toplami ${Object.values(GRUP).reduce((t, g) => t + g.bellekSiniriMB, 0)} MB`); console.log(`ayni asim dosya taniticisinda baska bir karar verir: ${KARAR.tanitici}`); // Esnek paylasim: agirlik bir sinir degil, rekabet halinde pay oranidir. const TOPLAM_PAY = 100; const agirlikToplam = Object.values(GRUP).reduce((t, g) => t + g.agirlik, 0); const talepToplam = Object.values(GRUP).reduce((t, g) => t + g.islemciTalebi, 0); console.log(`\nesnek paylasim: islemci (toplam ${TOPLAM_PAY} birim, talep ${talepToplam} birim)`); console.log("grup".padEnd(12) + "agirlik".padEnd(9) + "talep".padEnd(8) + "rekabette".padEnd(11) + "tek basina"); for (const [ad, g] of Object.entries(GRUP)) { const pay = Math.round((g.agirlik / agirlikToplam) * TOPLAM_PAY); console.log(ad.padEnd(12) + String(g.agirlik).padEnd(9) + String(g.islemciTalebi).padEnd(8) + String(Math.min(pay, g.islemciTalebi)).padEnd(11) + Math.min(TOPLAM_PAY, g.islemciTalebi)); } console.log("delik: agirlik bir tavan degildir; rekabet yoksa grup payinin " + `${Math.round(Math.min(TOPLAM_PAY, GRUP.gecelikIs.islemciTalebi) / ((GRUP.gecelikIs.agirlik / agirlikToplam) * TOPLAM_PAY) * 10) / 10}` + " katini alir"); console.log(`delik: sinirlarin toplami ${Object.values(GRUP).reduce((t, g) => t + g.bellekSiniriMB, 0)}` + " MB, makinenin belleginden bagimsizdir; asiri bindirme cekirdek karariyla sonuclanir");
sert sinir: bellek (MB) grup sinir verilen sigmayan karar (bellek) toplama 512 440 1 istek istegi yapan surec durdurulur faturalama 768 710 1 istek istegi yapan surec durdurulur gecelikIs 256 220 1 istek istegi yapan surec durdurulur toplam verilen 1370 MB, sigmayan 3 istek; sinirlarin toplami 1536 MB ayni asim dosya taniticisinda baska bir karar verir: istek reddedilir esnek paylasim: islemci (toplam 100 birim, talep 230 birim) grup agirlik talep rekabette tek basina toplama 20 60 20 60 faturalama 50 90 50 90 gecelikIs 30 80 30 80 delik: agirlik bir tavan degildir; rekabet yoksa grup payinin 2.7 katini alir delik: sinirlarin toplami 1536 MB, makinenin belleginden bagimsizdir; asiri bindirme cekirdek karariyla sonuclanir
Denetim grubunun iki ayrı davranışı vardır ve ikisi karıştırıldığında yanlış beklenti üretir. Sert sınırda karar keskindir: üç grupta da bir istek sınıra sığmaz ve bellekte verilen karar isteği yapan sürecin durdurulmasıdır — reddedilmek değil, durdurulmak. Aynı aşım dosya tanıtıcısında başka bir karar verir; istek reddedilir ve süreç koşmayı sürdürür. Bu ayrım kurgudaki gecelik iş için doğrudan sonuç taşır: 256 MB sınırla 220 MB verilir, dördüncü istek sığmaz ve iş yarıda durur.
Esnek paylaşımda sınır yoktur, ağırlık vardır. Talep 230 birim, makine 100 birimdir; rekabette paylar 20, 50 ve 30 olur. Ama ağırlık bir tavan değildir: rekabet yokken gecelik iş payının 2,7 katını alır. İkinci delik daha sessizdir — üç sınırın toplamı 1.536 MB’dır ve bu sayının makinenin belleğiyle bir ilgisi yoktur. Sınırlar makinenin belleğinden büyük konabilir; aşırı bindirme durumunda karar denetim grubunda değil, çekirdekte verilir. Denetim grubunun bedeli de bayt değildir, muhasebedir; kapattığı ortam farkı yine sıfırdır.
Birleşim Dosya Sistemi: Bir Katman Yığını
KK17. Katmanlar gerçek dizinlerdir ve dosyalar gerçekten yazılıp ölçülür; silme işareti bir dosya adı öneki ile modellenir. KK18. Yalnız en üst katman yazılabilir. KK19. Paylaşım hesabı, alt katmanların içerik olarak aynı olduğunu varsayar.
// olcum-agi/katman.mjs — birlesim dosya sistemi bir katman yigini olarak // Katmanlar GERCEK dizinlerdir; dosyalar gercekten yazilir ve gercekten olculur. import { mkdirSync, writeFileSync, readdirSync, statSync, copyFileSync, rmSync } from "node:fs"; import { join, dirname } from "node:path"; const KOK = "/tmp/olcum-agi-katman"; const KATMAN = ["katman0-taban", "katman1-kural", "katman2-yazilabilir"]; // altan uste const ISARET = ".silindi."; // silme isareti onekidir; alttakini gizler const ICERIK = { "katman0-taban": { "etc/tarife.json": 4096, "lib/cozumleyici.bin": 61440, "lib/kural.bin": 20480, "uygulama/toplayici.mjs": 3072, "uygulama/eski-ayar.json": 1024 }, "katman1-kural": { "lib/kural.bin": 24576, "etc/kural-surum.txt": 32 }, "katman2-yazilabilir": { "uygulama/toplayici.mjs": 3584, "uygulama/yeni-kural.mjs": 2048, ["uygulama/" + ISARET + "eski-ayar.json"]: 0 }, }; rmSync(KOK, { recursive: true, force: true }); for (const [k, dosyalar] of Object.entries(ICERIK)) for (const [yol, boyut] of Object.entries(dosyalar)) { mkdirSync(dirname(join(KOK, k, yol)), { recursive: true }); writeFileSync(join(KOK, k, yol), Buffer.alloc(boyut, k.charCodeAt(6))); } const listele = (k, alt = "") => readdirSync(join(KOK, k, alt), { withFileTypes: true }) .flatMap((g) => g.isDirectory() ? listele(k, join(alt, g.name)) : [join(alt, g.name)]); const bayt = (k, yol) => statSync(join(KOK, k, yol)).size; const katmanBayt = (k) => listele(k).reduce((s, y) => s + bayt(k, y), 0); // Birlestirme: en ustteki katman kazanir; ustteki silme isareti alttakini gizler. const gorunum = new Map(), golge = [], gizli = []; for (const k of [...KATMAN].reverse()) for (const yol of listele(k)) { if (yol.split("/").pop().startsWith(ISARET)) { gizli.push(yol.replace(ISARET, "")); continue; } gorunum.has(yol) ? golge.push(`${yol} (${gorunum.get(yol)} ustte)`) : gorunum.set(yol, k); } for (const y of gizli) gorunum.delete(y); console.log("katman".padEnd(22) + "dosya".padEnd(8) + "bayt"); for (const k of KATMAN) console.log(k.padEnd(22) + String(listele(k).length).padEnd(8) + katmanBayt(k)); console.log("\nbirlesik gorunum (yol -> saglayan katman)"); for (const [yol, k] of [...gorunum].sort()) console.log(" " + yol.padEnd(28) + k); console.log(`gorunen ${gorunum.size} dosya, ` + `${[...gorunum].reduce((s, [y, k]) => s + bayt(k, y), 0)} bayt`); console.log(`golgelenen ${golge.length}: ${golge.join(", ")}`); console.log(`silme isaretiyle gizlenen ${gizli.length}: ${gizli.join(", ")} ` + `(alt katmanda hala ${bayt(KATMAN[0], gizli[0])} bayt duruyor)`); console.log(`okuma icin kopyalanan dosya: 0 (${gorunum.size} dosya yerinde okunur)`); // Kopya-yukari: alt katmandaki bir dosyaya yazilinca once ust katmana kopyalanir. const HEDEF = "etc/tarife.json", ALT = gorunum.get(HEDEF); mkdirSync(dirname(join(KOK, KATMAN[2], HEDEF)), { recursive: true }); copyFileSync(join(KOK, ALT, HEDEF), join(KOK, KATMAN[2], HEDEF)); writeFileSync(join(KOK, KATMAN[2], HEDEF), Buffer.alloc(4096 + 16, 122)); console.log(`\nkopya-yukari: ${HEDEF} ${ALT} icinde ${bayt(ALT, HEDEF)} bayt; ` + `16 baytlik degisiklik icin tasinan ${bayt(ALT, HEDEF)} bayt`); console.log(` alt katmandaki kopya degismedi (${bayt(ALT, HEDEF)} bayt), ` + `ustteki ${bayt(KATMAN[2], HEDEF)} bayt`); const N = 5; // salt okunur alt katmanlar paylasilir const alt = katmanBayt(KATMAN[0]) + katmanBayt(KATMAN[1]), ust = katmanBayt(KATMAN[2]); console.log(`\n${N} ornek alt katmanlari paylasirsa ${alt} + ${N} x ${ust} = ` + `${alt + N * ust} bayt; paylasmasaydi ${N} x ${alt + ust} = ${N * (alt + ust)} bayt ` + `(kazanc ${N * (alt + ust) - (alt + N * ust)} bayt)`); rmSync(KOK, { recursive: true, force: true });
katman dosya bayt katman0-taban 5 90112 katman1-kural 2 24608 katman2-yazilabilir 3 5632 birlesik gorunum (yol -> saglayan katman) etc/kural-surum.txt katman1-kural etc/tarife.json katman0-taban lib/cozumleyici.bin katman0-taban lib/kural.bin katman1-kural uygulama/toplayici.mjs katman2-yazilabilir uygulama/yeni-kural.mjs katman2-yazilabilir gorunen 6 dosya, 95776 bayt golgelenen 2: lib/kural.bin (katman1-kural ustte), uygulama/toplayici.mjs (katman2-yazilabilir ustte) silme isaretiyle gizlenen 1: uygulama/eski-ayar.json (alt katmanda hala 1024 bayt duruyor) okuma icin kopyalanan dosya: 0 (6 dosya yerinde okunur) kopya-yukari: etc/tarife.json katman0-taban icinde 4096 bayt; 16 baytlik degisiklik icin tasinan 4096 bayt alt katmandaki kopya degismedi (4096 bayt), ustteki 4112 bayt 5 ornek alt katmanlari paylasirsa 114720 + 5 x 9744 = 163440 bayt; paylasmasaydi 5 x 124464 = 622320 bayt (kazanc 458880 bayt)
Üç katmanda on dosya vardır, birleşik görünümde altı. Fark iki gölgelemeden ve bir silme işaretinden gelir: kural ikilisi ile toplayıcı üstteki katmandan okunur, eski ayar dosyası ise hiç okunmaz. Okuma için hiçbir dosya kopyalanmaz — altı dosyanın altısı bulunduğu katmanda yerinde okunur. Bu, önceki dersin “kütüphane kümesi örnek başına bir kez yinelenir” satırını düzelten sayıdır: beş örnek alt katmanları paylaştığında toplam 163.440 bayt, paylaşmasaydı 622.320 bayt olurdu; kazanç 458.880 bayttır.
Bedel yazma anında ödenir. Alt katmandaki bir dosyaya yazmak için o dosya önce üst katmana kopyalanır: 16 baytlık bir değişiklik için 4.096 bayt taşınır. Oran dosya boyutuyla büyür ve tarife dosyası yerine çözümleyici ikilisine yazılsaydı taşınan bayt 61.440 olurdu.
Delik üç yerdedir. Silinen dosya silinmemiştir: eski ayar dosyası görünümde yoktur ama alt katmanda 1.024 bayt olarak durmayı sürdürür — çıktıyı okuyan biri onu bulabilir. Kopya-yukarı gecikmesi yazma anında ortaya çıkar ve önceden görünmez. Ve dosyaların hepsi çıktının içinde olsa da dosya sahipliği ile ad eşleştirme kuralı alttaki dosya sisteminden gelir; ilk dersin sınır kovasında duran boyut tam olarak buydu.
Özet
- Üç mekanizmanın hiçbiri yeni kaynak üretmez; üçü de tek bir kaynağın üzerine bir kural koyar ve kuralın olduğu yerde delik vardır.
- Ad alanı bir görünürlük eşlemesidir: 14 kayıtlık tabloda görünümler 5, 4 ve 2 kayıt görür, kök 14’ünü de görür. Numara havuzu ortaktır ve görünmeyen kayıt kaynak tüketmeyi sürdürür.
- Denetim grubu bir pay dağıtıcıdır ve iki davranışı vardır: sert sınırda karar (bellekte süreci durdurma, tanıtıcıda isteği reddetme), esnek paylaşımda ağırlığa göre dağıtım. Ağırlık tavan değildir — rekabet yokken bir grup payının 2,7 katını alır.
- Birleşim dosya sistemi bir katman yığınıdır: 10 dosya 6’ya iner, okuma için 0 dosya kopyalanır, beş örnek paylaşımla 458.880 bayt tasarruf eder. Yazma anında 16 baytlık değişiklik 4.096 bayt taşır.
- Ortamdan fark çıkaran mekanizma yalnız birleşim dosya sistemidir; ad alanı ile denetim grubu 0 fark kapatır, ölçüt ayırır. Silinen dosya alt katmanda durur, dosya sahipliği ve ad eşleştirme kuralı alttan gelir.
Sonraki Adım
Bu üç mekanizma tek bir makinenin çekirdeğine aittir ve o çekirdeğin sunduğu biçimde çalışır. Ölçülen katman yığını ise bir makineye ait değildir: dizinlerden oluşan bir yığındır, kopyalanıp taşınabilir. Peki taşındığı yerde aynı biçimde açılacağını ne garanti eder — katmanların hangi sırayla yığılacağını, hangi yapılandırmayla çalıştırılacağını ve bir katmanın taşınırken bozulmadığını kim yazar? Sonraki ders bu sorunun karşılığı olan belirtimlerin ortak yapısını ele alır: bir imaj biçimi belirtiminin taşıdığı sözü üç parçaya ayırır, özet zincirini denetleyen küçük bir doğrulayıcı yazar ve taşınabilirliğin nereye kadar gittiğini — aynı belirtime uyan iki çalışma zamanı arasında neyin değişmediğini, neyin değiştiğini — sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.