İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat