---
title: 'İşletim Sistemi Kavramları'
source: 'https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari'
language: tr
updated: '2026-08-17T04:39:39+00:00'
license: 'CC BY-SA 4.0'
lessons: 15
outcomes:
    - 'Süreç ve iş parçacığı ayrımını planlama ve bağlam değiştirme maliyetiyle açıklar'
    - 'Eşzamanlılık hatalarını tanır ve karşılıklı dışlama araçlarının bedelini sayar'
    - 'Sanal bellek ve dosya sistemi soyutlamalarının program davranışına etkisini ölçer'
---

# İşletim Sistemi Kavramları

Süreç ve iş parçacığı, çizelgeleme, eşzamanlılık hataları, sanal bellek, çöp toplama ve girdi/çıktı modellerinin ödettiği adımların ölçülmesi.

## Süreçler ve İş Parçacıkları

Süreç kavramı, çatallama, iş parçacıkları, yalıtım ile paylaşım arasındaki ödünleşim ve çizelgeleme algoritmalarının karşılaştırılması.

### Süreç Kavramı

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/surec-kavrami

Yürütülmekte olan program örneği olarak süreç ve paylaşımın ödettiği adım: tek başına koşan bir süreç 99 zaman birimi alırken beş süreç aynı makineyi paylaştığında 194 zaman birimi ve 22 bağlam değiştirme çıkıyor, 194 zaman biriminin 44'ü bağlam değiştirmeye gidiyor ve bu, işin kendisine giden 39 zaman biriminden fazla oluyor.

Algoritmalar kursu, bir çözümün maliyetini **kendi adım sayısıyla** ölçerek kapandı:
karşılaştırma sayısı, gezilen kenar, taranan karakter. Bu ölçünün sessiz bir varsayımı vardı —
algoritma makineyi tek başına kullanıyor. Üretimde bu varsayım tutmaz. Aynı algoritma, aynı
makinede başka işlerle birlikte koşar; işlemciyi, belleği ve diski onlarla paylaşır.

Bu kursun ölçtüğü şey **paylaşımın kendisidir**. Bir soyutlamanın sayısı sağladığı kolaylık
değil, **ödettiği adımdır**; bedeli sayılmayan soyutlama ölçülmemiş sayılır. İlk soyutlama
süreçtir ve ilk bedel, bir işlemcinin birden çok süreç arasında gidip gelirken harcadığı
adımdır.

- **SR1.** Makine **ders içinde modellenir**. Aşağıdaki kod bir **benzeticidir**: hiçbir gerçek
  süreç yaratılmaz, hiçbir çekirdek çağrısı yapılmaz, hiçbir gerçek zaman ölçülmez.
- **SR2.** Bütün süreler **zaman birimidir**. Saniye değildir ve saniyeye çevrilmez.
- **SR3.** İş yükü **beş süreçtir**; her süreç **on iki adımlıdır**. Bir adım ya bir **hesap
  adımıdır** (bir zaman birimi sürer, bir sanal sayfaya dokunur) ya da bir **bekleme adımıdır**
  (30 zaman birimi sürer, çekirdeği kullanmaz).
- **SR4.** Tohum **20260218**'dir. Aynı tohum aynı iş yükünü verir; buradaki her sayı yeniden
  üretilebilir.
- **SR5.** Makinede **tek çekirdek** vardır. Her zaman biriminde çekirdek en çok bir adım yürütür.
- **SR6.** **Bağlam değiştirmenin bedeli 2 zaman birimidir.** Çekirdek bu iki birimde hiçbir
  sürecin adımını yürütmez.
- **SR7.** Adres uzayı, yığıt ve öbek **Bilgisayarlar Nasıl Çalışır** kursunun bellek düzeni
  dersinde kurulmuştu; burada **yeniden tanımlanmaz**, yalnız sayılır.
- **SR8.** Ölçünün çözünürlüğü **2 zaman birimidir**. İki kurulum arasındaki 1 birimlik fark
  **ölçülmemiş sayılır**.

#### Süreç: Yürütülmekte Olan Program

Program diskte duran bir bayt dizisidir; **süreç (process)**, o programın yürütülmekte olan bir
örneğidir. Aradaki fark, sürecin programda bulunmayan bir şeye sahip olmasıdır: **durum**.
Bir sürecin durumu, kaldığı adımın sayacını, yığıtının o anki içeriğini, açık tuttuğu
tanıtıcıları ve kendi adres uzayını kapsar.

Bu durum bir yerde tutulmak zorundadır, çünkü işlemci bir süreci bırakıp başkasına geçtiğinde
geri döndüğünde **tam bıraktığı yerden** devam etmelidir. Durumun tutulduğu yapı **süreç
tablosudur (process table)**: her süreç için bir girdi, her girdide sürecin yeniden
başlatılabilmesi için gereken alanlar.

Aynı programdan iki süreç çalıştırıldığında ortaya çıkan şey iki ayrı durumdur. İkisi aynı
komutları yürütür, ama farklı adımlarda, farklı verilerle ve birbirinin belleğine dokunmadan.
Bu ayrılık **yalıtımdır (isolation)** ve bu kursun sonraki derslerinde bedeli sayılacaktır.

Bir **çok görevli işletim sistemi**, tek çekirdekli bir makinede bile birden çok sürecin aynı
anda ilerlediği izlenimini verir. İzlenimin kaynağı, çekirdeğin süreçler arasında yeterince
sık gidip gelmesidir. Sürecin bakış açısından bu geçişler görünmez: süreç kendi adımlarını
kesintisiz yürütüyormuş gibi yazılır. Ölçüm bu izlenimi bozar, çünkü geçişlerin her biri
çekirdekte yer kaplar ve toplam süreye eklenir.

#### Modellenen İş Yükü

İlk blok iş yükünü kurar. Bir süreç, adım listesi olarak temsil edilir; hesap adımları
çekirdeği tutar, bekleme adımları tutmaz.

```python
# Bu makine bir BENZETICIDIR. Hicbir gercek surec, cekirdek cagrisi ya da
# zaman olcumu yoktur; butun sureler modeldeki zaman birimidir.
TOHUM = 20260218
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30      # bir girdi/cikti adimi kac zaman birimi surer
BAGLAM_BEDELI = 2        # bir baglam degistirmenin bedeli (zaman birimi)
SANAL_SAYFA = 16


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    """Bir adim ya ("HESAP", sanal_sayfa) ya da ("BEKLE", sure)."""
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


ISLER = is_yuku()
print("surec  varis  oncelik  hesap  bekleme  tek basina sure")
for i in ISLER:
    h = sum(1 for t, _ in i["adim"] if t == "HESAP")
    b = ADIM_SAYISI - h
    print(f"  {i['ad']:4s} {i['varis']:5d} {i['oncelik']:8d} {h:6d} {b:8d}"
          f" {h + b * BEKLEME_SURESI:15d}")
print("toplam hesap adimi:", sum(1 for i in ISLER for t, _ in i["adim"] if t == "HESAP"))
```

```
surec  varis  oncelik  hesap  bekleme  tek basina sure
  S1       0        3      9        3              99
  S2       4        2      9        3              99
  S3       8        1      7        5             157
  S4      12        3      7        5             157
  S5      16        2      7        5             157
toplam hesap adimi: 39
```

Son sütun **tabandır**: bir süreç makinede yalnız olsaydı ne kadar sürerdi. S1 için bu
$9 + 3 \times 30 = 99$ zaman birimidir. Sayı, sürecin kendi adımlarının toplamıdır ve başka
hiçbir şeye bağlı değildir. Algoritmalar kursunun ölçüsü tam olarak budur.

#### Paylaşımın Kendisi Bir İş

Beş süreç aynı çekirdeği kullandığında çekirdek bir seçim yapmak zorundadır: hangisi koşacak.
Bu seçimi yapan bileşen **çizelgeleyicidir (scheduler)**. Aşağıdaki gerçeklenim bu kursun
bütün çizelgeleme ölçümlerinde kullanılacak; burada yalnız **önalımsız sıra** kipiyle koşar,
yani bir süreç ya bitene ya da bir bekleme adımına girene kadar çekirdeği bırakmaz.

```python
# Ilk blogun uzerine: ISLER, BAGLAM_BEDELI ve ADIM_SAYISI oradan gelir.
SONSUZ = 10**9


def cizelgele(isler, yordam="sirayla", dilim=4, cekirdek=1,
              baglam_bedeli=BAGLAM_BEDELI):
    """yordam: sirayla (onalimsiz) | dilimli | oncelikli | adil
    Her zaman biriminde her cekirdek en cok bir adim yurutur."""
    durum = [{"ad": i["ad"], "adim": list(i["adim"]), "yer": 0, "varis": i["varis"],
              "hazir": i["varis"], "kullanim": 0, "bitis": None,
              "oncelik": i["oncelik"]} for i in isler]
    pay_siniri = SONSUZ if yordam == "sirayla" else dilim
    cekirdekler = [{"surec": None, "onceki": None, "pay": 0, "engel": 0}
                   for _ in range(cekirdek)]
    t, baglam, bos_adim = 0, 0, 0

    def bitmedi(d):
        return d["yer"] < len(d["adim"])

    while any(bitmedi(d) for d in durum) or any(d["bitis"] is None or d["bitis"] > t
                                               for d in durum):
        for c in cekirdekler:                       # birakma
            d = c["surec"]
            if d is not None and (not bitmedi(d) or d["hazir"] > t or c["pay"] >= pay_siniri):
                c["surec"] = None
        for c in cekirdekler:                       # atama
            if c["engel"] or c["surec"] is not None:
                continue
            tutulan = [x["surec"] for x in cekirdekler if x["surec"] is not None]
            hazir = [d for d in durum if bitmedi(d) and d["hazir"] <= t and d not in tutulan]
            if not hazir:
                continue
            if yordam == "oncelikli":
                sec = min(hazir, key=lambda d: (-d["oncelik"], d["hazir"], d["ad"]))
            elif yordam == "adil":
                sec = min(hazir, key=lambda d: (d["kullanim"], d["hazir"], d["ad"]))
            else:
                sec = min(hazir, key=lambda d: (d["hazir"], d["ad"]))
            if c["onceki"] is not None and c["onceki"] is not sec:
                baglam += 1
                c["engel"] = baglam_bedeli
            c["surec"] = sec
            c["onceki"] = sec
            c["pay"] = 0
        for c in cekirdekler:                       # yurutme
            if c["engel"]:
                c["engel"] -= 1
                continue
            d = c["surec"]
            if d is None:
                bos_adim += 1
                continue
            tur, deger = d["adim"][d["yer"]]
            if tur == "BEKLE":
                d["yer"] += 1
                d["hazir"] = t + deger
                c["surec"] = None
                bos_adim += 1
                if not bitmedi(d):                  # son adim beklemeyse is o zaman biter
                    d["bitis"] = t + deger
            else:
                d["yer"] += 1
                d["kullanim"] += 1
                d["hazir"] = t + 1
                c["pay"] += 1
                if not bitmedi(d):
                    d["bitis"] = t + 1
                    c["surec"] = None
        t += 1
    for d, i in zip(durum, isler):
        gc = sum(v for tur, v in i["adim"] if tur == "BEKLE")
        d["bekleme"] = d["bitis"] - d["varis"] - d["kullanim"] - gc
    return {"sure": t, "baglam": baglam, "bos_cekirdek_adimi": bos_adim,
            "toplam_is": sum(d["kullanim"] for d in durum),
            "ortalama_tamamlanma": round(sum(d["bitis"] - d["varis"] for d in durum) / len(durum), 2),
            "ortalama_bekleme": round(sum(d["bekleme"] for d in durum) / len(durum), 2),
            "bitis": {d["ad"]: d["bitis"] for d in durum}}


TEK = [cizelgele([dict(i, varis=0)], "sirayla") for i in ISLER]
PAY = cizelgele(ISLER, "sirayla")
print("taban  (bes surec pesi sira, hic paylasim yok):",
      sum(s["sure"] for s in TEK), "zaman birimi,",
      sum(s["baglam"] for s in TEK), "baglam degistirme")
print("kurulum(bes surec ayni makineyi paylasiyor)   :",
      PAY["sure"], "zaman birimi,", PAY["baglam"], "baglam degistirme")
print()
print("surec  tek basina  paylasimli  fark")
for i, s in zip(ISLER, TEK):
    p = PAY["bitis"][i["ad"]] - i["varis"]
    print(f"  {i['ad']:4s} {s['sure']:10d} {p:11d} {p - s['sure']:5d}")
print("ortalama tamamlanma:", PAY["ortalama_tamamlanma"],
      "| ortalama bekleme:", PAY["ortalama_bekleme"])
```

```
taban  (bes surec pesi sira, hic paylasim yok): 669 zaman birimi, 0 baglam degistirme
kurulum(bes surec ayni makineyi paylasiyor)   : 194 zaman birimi, 22 baglam degistirme

surec  tek basina  paylasimli  fark
  S1           99         103     4
  S2           99         112    13
  S3          157         176    19
  S4          157         173    16
  S5          157         178    21
ortalama tamamlanma: 148.4 | ortalama bekleme: 14.6
```

İki sayı aynı anda doğrudur ve ters yöne bakar. **Toplam süre düşer:** beş süreci peş peşe,
hiç iç içe geçirmeden koşturmak 669 zaman birimi alırken paylaşımlı koşum 194'te biter. Kazanç,
bir süreç beklerken çekirdeğin bir başkasına verilmesinden gelir. **Tekil süre yükselir:** hiçbir
süreç kendi tabanına ulaşamaz; S5 için 157 olan taban 178'e çıkar. Fark, süreç başına ortalama
**14,60 zaman birimidir** ve bu, sürecin kendi işiyle değil, başkalarının işiyle geçirdiği
süredir.

#### Bir Sürecin Üç Durumu

Yukarıdaki fark sütunu bir soru bırakır: bir süreç, kendi işini yapmadığı o fazladan
zaman birimlerinde ne yapıyor. Yanıt, sürecin her an üç durumdan birinde olmasıdır.
**Çalışıyor**, çekirdeği o an sürecin tuttuğu durumdur. **Engellenmiş**, sürecin bir bekleme
adımında olduğu ve çekirdek boşta olsa bile koşamayacağı durumdur. **Hazır**, sürecin
koşabilecek durumda olduğu ama çekirdeğin bir başkasında olduğu durumdur.

```python
# Onceki bloklarin uzerine: ISLER, PAY ve BEKLEME_SURESI oradan gelir.
print("surec  calisiyor  engellenmis  hazir(kuyrukta)  tamamlanma")
toplam_hazir = 0
for i in ISLER:
    calisiyor = sum(1 for t, _ in i["adim"] if t == "HESAP")
    engellenmis = sum(v for t, v in i["adim"] if t == "BEKLE")
    tamamlanma = PAY["bitis"][i["ad"]] - i["varis"]
    hazir = tamamlanma - calisiyor - engellenmis
    toplam_hazir += hazir
    print(f"  {i['ad']:4s} {calisiyor:10d} {engellenmis:12d} {hazir:16d} {tamamlanma:11d}")
print("ortalama hazir sure:", round(toplam_hazir / len(ISLER), 2))
```

```
surec  calisiyor  engellenmis  hazir(kuyrukta)  tamamlanma
  S1            9           90                4         103
  S2            9           90               13         112
  S3            7          150               19         176
  S4            7          150               16         173
  S5            7          150               21         178
ortalama hazir sure: 14.6
```

İlk iki sütun **tabana aittir** ve paylaşımdan etkilenmez: bir sürecin hesap adımı sayısı da
bekleme süresi de kendi programının özelliğidir. Paylaşımın ürettiği sütun üçüncüsüdür.
Hazır süre, tek başına koşan bir süreçte tanım gereği sıfırdır; beş süreç paylaşınca 4 ile 21
arasında bir değere çıkar ve ortalaması **14,60 zaman birimi** olur.

Bu, süreç soyutlamasının ödettiği adımın **süreç tarafından görülen** biçimidir. Sürecin
kendisi ne bağlam değiştirmeyi görür ne de süreç tablosunu; gördüğü tek şey, yürütülmesinin
arada bir durup beklemesidir. S1'in hazır süresi 4, S5'inki 21; aradaki fark varış sırasından
gelir ve **erken gelen az bekler**. Bu sıranın bir seçim olduğu ve farklı seçimlerin farklı
hazır süreler ürettiği, konunun çizelgeleme dersinde ölçülecek.

#### 194 Zaman Biriminin Dağılımı

Bedelin nerede olduğunu görmek için toplam süreyi bileşenlerine ayırmak gerekir. Süreç
tablosunun kendisi de burada sayılır; tanımı **Bilgisayarlar Nasıl Çalışır** kursuna aittir,
buradaki soru kaç alan taşındığıdır.

```python
# Ilk iki blogun uzerine: ISLER, PAY ve BAGLAM_BEDELI oradan gelir.
ALAN = ("ad", "adim", "yer", "varis", "hazir", "kullanim", "bitis", "oncelik", "bekleme")
print("surec tablosu:", len(ISLER), "girdi x", len(ALAN), "alan =",
      len(ISLER) * len(ALAN), "alan")
print()
is_adimi = PAY["toplam_is"]
baglam_adimi = PAY["baglam"] * BAGLAM_BEDELI
print("194 zaman biriminin dagilimi")
print("  hesap adimi        :", is_adimi)
print("  baglam degistirme  :", baglam_adimi, f"({PAY['baglam']} x {BAGLAM_BEDELI})")
print("  bos cekirdek adimi :", PAY["bos_cekirdek_adimi"])
print("  toplam             :", is_adimi + baglam_adimi + PAY["bos_cekirdek_adimi"])
print("  baglam / hesap orani:", round(baglam_adimi / is_adimi, 4))
```

```
surec tablosu: 5 girdi x 9 alan = 45 alan

194 zaman biriminin dagilimi
  hesap adimi        : 39
  baglam degistirme  : 44 (22 x 2)
  bos cekirdek adimi : 111
  toplam             : 194
  baglam / hesap orani: 1.1282
```

Bu dersin en önemli satırı sondan ikincisidir. Beş sürecin **gerçek işi 39 zaman birimidir**;
bu işi yaptırabilmek için harcanan **bağlam değiştirme 44 zaman birimidir**. Soyutlamanın
ödettiği adım, soyutlamanın taşıdığı adımdan **daha büyüktür** — oran 1,1282.

Kalan 111 birim boş çekirdek adımıdır: bütün süreçler bekleme adımındayken çekirdeğin
yapacak işi yoktur. Bu, çizelgeleyicinin kusuru değildir; iş yükünün 21 bekleme adımı toplam
630 zaman birimi tutar ve bunların yalnız bir bölümü iç içe geçirilebilir. Boş adımı düşürmenin
yolu daha iyi bir çizelgeleyici değil, **daha çok süreçtir** — ve her yeni süreç bağlam
değiştirme sayısını da artırır.

Süreç tablosunun 45 alanı, bağlam değiştirmenin neden bedava olmadığının nedenidir: her geçişte
bir girdinin alanları saklanır, bir başkasınınki geri yüklenir. Bu kursun sayacı, o iki işi
2 zaman birimi olarak modeller.

#### Özet

- Süreç, bir programın yürütülmekte olan örneğidir; programdan farkı taşıdığı **durumdur** ve
  bu durum süreç tablosunda tutulur.
- Bu iş yükünde tek başına koşan bir süreç kendi adımı kadar sürer: S1 için 99, S3 için 157
  zaman birimi. Bu **tabandır**.
- Beş süreç aynı makineyi paylaştığında toplam süre 669'dan **194 zaman birimine** iner, ama
  hiçbir sürecin tekil süresi tabanına ulaşmaz; ortalama bekleme **14,60 zaman birimidir**.
- Paylaşımın bedeli **22 bağlam değiştirmedir** ve bu 44 zaman birimi tutar; iş yükünün
  gerçek işi olan 39 zaman biriminden **fazladır**.
- Bir süreç her an çalışıyor, engellenmiş ya da hazır durumdadır; paylaşımın ürettiği tek
  yeni durum **hazırdır** ve bu iş yükünde ortalama 14,60 zaman birimi tutar.
- 194 zaman birimi tam olarak 39 hesap adımı, 44 bağlam adımı ve 111 boş çekirdek adımına
  ayrışır; her soyutlamanın bedeli bu ayrışmada görünür.

#### Sonraki Adım

Bu derste beş süreç hazır verildi. Gerçek bir makinede süreçler yoktan var olmaz: her biri
başka bir süreç tarafından yaratılır ve yaratılırken yaratıcısından bir şeyler devralır.
Sonraki ders çatallamayı ele alacak ve tek bir soruyu sayacak: yeni bir süreç kurmak, **kaç
sayfanın kopyalanması** demektir.

### Süreç Çatallama

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/surec-catallama

Yeni bir sürecin var olan bir süreçten türetilmesi ve kalıtımın ölçülmesi: dört çatallama hevesli kopyalamayla 64 sanal sayfa taşıyor, yazarken kopyalamayla 15 sayfaya iniyor, geriye 49 boşa kopyalanmış sayfa kalıyor ve sayfa hatasının bedeli 4 zaman birimine çıktığında yazarken kopyalama üstünlüğünü kaybediyor.

Önceki derste beş süreç hazır verilmişti; makine açıldığında orada duruyorlardı. Gerçek bir
makinede hiçbir süreç böyle doğmaz. Her süreç, çalışmakta olan bir başka süreç tarafından
yaratılır ve yaratıldığı anda yaratıcısından bir şeyler devralır. Bu türetme işlemine
**çatallama (fork)** denir.

Sorunun ölçülebilir biçimi şudur: bir sürecin kopyası çıkarılırken **kaç sanal sayfanın
kopyalanması** gerekir. Süreç soyutlamasının yalıtım güvencesi tam olarak burada bedelini
ister, çünkü iki sürecin birbirinin belleğine dokunamaması ancak iki ayrı adres uzayı varsa
sağlanır.

- **SR9.** Çatallama, **var olan bir süreçten** yeni bir süreç türetir. Türeten **ebeveyn**,
  türeyen **çocuktur**; ilişki bir ağaç kurar.
- **SR10.** Bu derste ağaç sabittir: S1 çatallayarak S2 ve S3'ü, S2 çatallayarak S4 ve S5'i
  üretir. Toplam **dört çatallama** vardır.
- **SR11.** Bir sürecin adres uzayı **16 sanal sayfadır** (ortak tanımın `SANAL_SAYFA`
  değeri). Bir sayfanın kopyalanması **1 zaman birimi** tutar.
- **SR12.** Bir sayfayı **salt okunur işaretlemek** bir bit çevirmektir ve bu modelde
  **0 zaman birimi** tutar. Bu bir seçimdir ve sonuç ona duyarlıdır; duyarlılık ders içinde
  süpürülür.
- **SR13.** Çocuğun dokunduğu sayfa kümesi, iş yükünün hesap adımlarından okunur. Her dokunuş
  **yazma sayılır**; bu, yazarken kopyalama için **kötümser üst sınırdır**.
- **SR14.** Alt süreç çalıştırma, boru hattı ve iş denetimi **Kabuk Programlama** kursunda
  kullanım düzeyinde ele alındı; burada **tekrarlanmaz**. Bu ders yalnız çatallamanın
  kalıtımını ve maliyetini sayar.
- **SR15.** Ölçünün çözünürlüğü **2 zaman birimidir**. Bunun altındaki fark **ölçülmemiş
  sayılır** ve öyle yazılır.

#### Çatallamanın Üç Ayrı Davranışı

Çatallama tek bir işlem gibi görünür ama sürecin durumunu üç ayrı biçimde ele alır ve bu üç
biçim birbiriyle karıştırılmamalıdır.

**Kopyalama.** Adres uzayı çocuğa aittir. Ebeveyn kopyaladıktan sonra bir sayfayı değiştirirse
çocuk bunu görmez, çocuk değiştirirse ebeveyn görmez. Yalıtımı sağlayan davranış budur ve
**tek bedelli olan da budur**.

**Kalıtım.** Bazı alanlar çocuğa değeriyle geçer: öncelik, çalışma dizini, kaynak sınırları.
Çocuk bunları sonradan değiştirebilir ve değişiklik ebeveyni etkilemez, ama başlangıç değeri
ebeveynden gelir.

**Paylaşım.** Bazı yapılar tek nesne olarak kalır ve iki süreç aynı nesneyi görür. Açık
**dosya tanıtıcısının (file descriptor)** işaret ettiği okuma konumu böyledir: ebeveyn okuyup
ilerlediğinde çocuk kaldığı yerden okur. Paylaşımın bedeli sıfırdır, ama yalıtımı da yoktur.

Üçünün ayrı tutulması gerekir, çünkü bir hata ararken sorulacak soru her birinde farklıdır.
Kopyalanan bir alanda ebeveynin sonraki değişikliği çocuğa **hiçbir zaman** ulaşmaz; kalıtılan
bir alanda çocuğun başlangıç değeri ebeveyninkidir ama sonrası bağımsızdır; paylaşılan bir
nesnede iki sürecin **her** işlemi diğerini etkiler. Çatallama sonrası beklenmedik bir
davranış görüldüğünde ilk iş, ilgili durumun bu üç kümeden hangisine düştüğünü belirlemektir.

Çocuk, ebeveyninden bağımsız bir süreçtir; ebeveyn önce bitse bile çocuk koşmayı sürdürür.
Süreç tablosundaki girdisi kendisine aittir ve önceki dersteki dokuz alanı taşır. Bu yüzden
çatallamanın bedeli iki kalemdir: bir süreç tablosu girdisi ve bir adres uzayı. Girdi sabit
ve küçüktür; adres uzayı sabit değildir ve bu dersin ölçtüğü şey odur.

#### İş Yükü ve Çatallama Ağacı

İlk blok, önceki derste kurulan iş yükünü aynen yeniden kurar. İkinci blok çatallamayı
modeller ve iki kopyalama yordamını sayar.

```python
# Bu makine bir BENZETICIDIR. Hicbir gercek surec, cekirdek cagrisi ya da
# zaman olcumu yoktur; butun sureler modeldeki zaman birimidir.
TOHUM = 20260218
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30      # bir girdi/cikti adimi kac zaman birimi surer
BAGLAM_BEDELI = 2        # bir baglam degistirmenin bedeli (zaman birimi)
SANAL_SAYFA = 16


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    """Bir adim ya ("HESAP", sanal_sayfa) ya da ("BEKLE", sure)."""
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


ISLER = is_yuku()
print("surec  varis  oncelik  hesap adimi")
for i in ISLER:
    h = sum(1 for t, _ in i["adim"] if t == "HESAP")
    print(f"  {i['ad']:4s} {i['varis']:5d} {i['oncelik']:8d} {h:12d}")
```

```
surec  varis  oncelik  hesap adimi
  S1       0        3            9
  S2       4        2            9
  S3       8        1            7
  S4      12        3            7
  S5      16        2            7
```

#### Neyi Kopyalar, Neyi Kalıtır, Neyi Paylaşır

Aşağıdaki `catalla` yordamı çocuğun süreç tablosu girdisini üretir. Alanların yanındaki
yorumlar üç davranışın hangisinin işlediğini gösterir; son satır paylaşımı **nesne kimliğiyle**
kanıtlar.

```python
# Ilk blogun uzerine: ISLER, SANAL_SAYFA ve ADIM_SAYISI oradan gelir.
KOPYA_BEDELI = 1                 # bir sanal sayfayi kopyalamak kac zaman birimi surer
TANITICI = [0, 1, 2]             # her surecin acilista tuttugu tanitici listesi
AGAC = [("S2", "S1"), ("S3", "S1"), ("S4", "S2"), ("S5", "S2")]
TABLO = {i["ad"]: dict(i, tanitici=list(TANITICI)) for i in ISLER}
DOKUNUS = {i["ad"]: sorted({v for t, v in i["adim"] if t == "HESAP"}) for i in ISLER}


def catalla(ad, ebeveyn_ad, yordam="hevesli"):
    """Cocugun surec tablosu girdisini uretir ve kopyalanan sayfayi sayar."""
    e = TABLO[ebeveyn_ad]
    cocuk = {"ad": ad,                          # yeni: cocugun kendi kimligi
             "varis": TABLO[ad]["varis"],       # yeni
             "adim": list(TABLO[ad]["adim"]),   # cocugun catallamadan sonraki adimlari
             "oncelik": e["oncelik"],           # kalitim: ebeveynden gelir
             "tanitici": e["tanitici"]}         # paylasim: ayni liste nesnesi
    sayfa = SANAL_SAYFA if yordam == "hevesli" else len(DOKUNUS[ad])
    return cocuk, sayfa


for yordam in ("hevesli", "yazarken"):
    sayfa = sum(catalla(ad, eb, yordam)[1] for ad, eb in AGAC)
    print(f"{yordam:9s} kopyalama: {len(AGAC)} catallama, {sayfa:2d} sayfa,"
          f" {sayfa * KOPYA_BEDELI:2d} zaman birimi")
print()
print("cocuk  dokundugu sayfa      adet  hevesli  yazarken  bosa kopyalanan")
for ad, eb in AGAC:
    d = len(DOKUNUS[ad])
    print(f"  {ad:4s} {str(DOKUNUS[ad]):20s} {d:4d} {SANAL_SAYFA:8d} {d:9d}"
          f" {SANAL_SAYFA - d:16d}")
print("toplam bosa kopyalanan sayfa:",
      sum(SANAL_SAYFA - len(DOKUNUS[ad]) for ad, _ in AGAC))
print()
c, _ = catalla("S2", "S1")
print("kalitim  : oncelik", TABLO["S1"]["oncelik"], "->", c["oncelik"])
print("paylasim : tanitici listesi ayni nesne mi:", c["tanitici"] is TABLO["S1"]["tanitici"])
print("yenilenen: ad", c["ad"], "| varis", c["varis"])
```

```
hevesli   kopyalama: 4 catallama, 64 sayfa, 64 zaman birimi
yazarken  kopyalama: 4 catallama, 15 sayfa, 15 zaman birimi

cocuk  dokundugu sayfa      adet  hevesli  yazarken  bosa kopyalanan
  S2   [3, 4, 5, 6]            4       16         4               12
  S3   [6, 7, 8, 9]            4       16         4               12
  S4   [9, 11, 12]             3       16         3               13
  S5   [12, 13, 14, 15]        4       16         4               12
toplam bosa kopyalanan sayfa: 49

kalitim  : oncelik 3 -> 3
paylasim : tanitici listesi ayni nesne mi: True
yenilenen: ad S2 | varis 4
```

**Hevesli kopyalama**, çatallama anında adres uzayının tamamını çıkarır: dört çatallama için
$4 \times 16 = 64$ sayfa. **Yazarken kopyalama (copy-on-write)** ise çatallama anında hiçbir
sayfayı kopyalamaz; bütün sayfaları salt okunur işaretler ve bir sayfaya ilk yazma
denendiğinde yalnız o sayfayı çıkarır. Bu iş yükünde dört çocuk toplam **15 ayrı sayfaya**
dokunur, dolayısıyla kopyalanan sayfa 64'ten 15'e iner.

Aradaki 49 sayfa **boşa kopyalanmış** sayfadır: hevesli yordam onları çıkarır, çocuk hiçbirine
dokunmaz. Oran süreç başına belirgindir — S4 kendi adres uzayının 16 sayfasından yalnız 3'üne
dokunur, geri kalan 13'ü kopyalanır ve okunmadan atılır.

#### Kalıtım Zincir Boyunca İşler

Çatallama bir ağaç kurduğu için kalıtım tek adımda kalmaz. S4, S2'den türer ve S2 de S1'den
türemiştir; dolayısıyla S4'ün başlangıç önceliği S1'e kadar geri gider. Ağacın tamamı
kurulduğunda bunun ne yaptığı sayılabilir.

```python
# Onceki bloklarin uzerine: ISLER, AGAC, TABLO ve catalla.
EBEVEYN = dict(AGAC)


def agaci_kur(yordam="hevesli"):
    """Kok S1'den baslayarak butun agaci catallar; oncelik zincir boyunca kalitilir."""
    kurulu, sayfa = {"S1": dict(TABLO["S1"])}, 0
    for ad, eb in AGAC:
        c, s = catalla(ad, eb, yordam)
        c["oncelik"] = kurulu[eb]["oncelik"]          # kalitim zinciri
        kurulu[ad] = c
        sayfa += s
    return kurulu, sayfa


KURULU, SAYFA = agaci_kur()
DERINLIK = {"S1": 0}
print("surec  ebeveyn  derinlik  kendi onceligi  kalitilan oncelik")
for i in ISLER:
    ad = i["ad"]
    if ad in EBEVEYN:
        DERINLIK[ad] = DERINLIK[EBEVEYN[ad]] + 1
    print(f"  {ad:4s} {EBEVEYN.get(ad, '-'):>8s} {DERINLIK[ad]:9d}"
          f" {TABLO[ad]['oncelik']:15d} {KURULU[ad]['oncelik']:18d}")
print("agac derinligi:", max(DERINLIK.values()),
      "| yaprak:", sum(1 for a in DERINLIK if a not in EBEVEYN.values()))
print("kendi onceliklerinde ayri deger:", len({TABLO[a]["oncelik"] for a in DERINLIK}),
      "| kalitilanlarda:", len({KURULU[a]["oncelik"] for a in DERINLIK}))
```

```
surec  ebeveyn  derinlik  kendi onceligi  kalitilan oncelik
  S1          -         0               3                  3
  S2         S1         1               2                  3
  S3         S1         1               1                  3
  S4         S2         2               3                  3
  S5         S2         2               2                  3
agac derinligi: 2 | yaprak: 3
kendi onceliklerinde ayri deger: 3 | kalitilanlarda: 1
```

Son satır kalıtımın sessiz sonucudur. İş yükünün kendi öncelik değerleri üç ayrı düzey taşır;
ağaç çatallamayla kurulduğunda hepsi kökün değerine, yani 3'e iner ve **ayrı düzey sayısı
1'e düşer**. Öncelik bakan bir çizelgeleyici için bu, bütün süreçlerin ayırt edilemez hâle
gelmesi demektir; kalıtımın maliyeti sayfa cinsinden sıfırdır ama bilgi cinsinden değildir.
Çocuk kendi önceliğini sonradan **açıkça** kurmak zorundadır; kurmazsa ebeveyninkiyle koşar.

Ağacın derinliği bu kurulumda 2, yaprak sayısı 3'tür. Derinlik önemlidir çünkü kalıtım
zincirinin uzunluğunu verir: derinliği $d$ olan bir çocuğun başlangıç değerleri, $d$ adım
öteden gelmiş olabilir ve bunu görmenin tek yolu ağacı geri yürümektir.

#### Kopyalama, İşin Kendisiyle Karşılaştırıldığında

Bu sayıların büyük olup olmadığı, ancak iş yükünün gerçek işiyle yan yana konunca anlaşılır.
Aynı beş sürecin toplam hesap adımı **39 zaman birimidir**.

```python
# Onceki bloklarin uzerine: ISLER, AGAC, DOKUNUS, SANAL_SAYFA, KOPYA_BEDELI.
ISARET_BEDELI = 0        # bir sayfayi salt okunur isaretlemek bir bit cevirmektir
HESAP = sum(1 for i in ISLER for t, _ in i["adim"] if t == "HESAP")
SAYFA = sum(len(DOKUNUS[ad]) for ad, _ in AGAC)
HEVESLI = len(AGAC) * SANAL_SAYFA
print("is yukunun toplam hesap adimi        :", HESAP)
print("hevesli kopyalama / hesap adimi orani:", round(HEVESLI / HESAP, 4))
print("yazarken kopyalama / hesap adimi     :", round(SAYFA / HESAP, 4))
print()
print("sayfa hatasi bedeli  hevesli  yazarken  kazanan")
for hata in range(7):
    h = HEVESLI * KOPYA_BEDELI
    y = HEVESLI * ISARET_BEDELI + SAYFA * (hata + KOPYA_BEDELI)
    if abs(h - y) < 2:
        kazanan = "olculemez"
    else:
        kazanan = "yazarken" if y < h else "hevesli"
    print(f"{hata:19d} {h:8d} {y:9d}  {kazanan}")
```

```
is yukunun toplam hesap adimi        : 39
hevesli kopyalama / hesap adimi orani: 1.641
yazarken kopyalama / hesap adimi     : 0.3846

sayfa hatasi bedeli  hevesli  yazarken  kazanan
                  0       64        15  yazarken
                  1       64        30  yazarken
                  2       64        45  yazarken
                  3       64        60  yazarken
                  4       64        75  hevesli
                  5       64        90  hevesli
                  6       64       105  hevesli
```

İlk oran, bu dersin en sert sayısıdır: hevesli çatallama, beş sürecin **bütün hesap işinden
1,641 kat pahalıdır**. Yeni süreç kurmanın maliyeti, o süreçlerin yapacağı işten büyüktür.
Yazarken kopyalamada oran 0,3846'ya iner, yani kopyalama işin üçte birinden azını tutar.

Bu oran iş yüküne bağlıdır ve öyle yazılmalıdır: süreç başına on iki adım yerine yüz iki adım
olsaydı payda büyür, oran düşerdi. Ölçülen şey çatallamanın mutlak pahalılığı değil,
**kısa ömürlü süreçlerde** kurulum bedelinin işi geçtiğidir.

#### Yazarken Kopyalama Ne Zaman Kaybeder

Yazarken kopyalama bedava değildir; kopyalamayı **ertelemekle** birlikte iki yeni kalem ekler.
Birincisi, çatallama anında 64 sayfanın salt okunur işaretlenmesidir. İkincisi, ertelenen her
kopyanın bir **sayfa hatasıyla** başlamasıdır: yazma denemesi yakalanır, denetim çekirdeğe
geçer, sayfa çıkarılır ve komut yeniden yürütülür. Sayfa hatası bu kursun bellek ve depolama
konusunda kendi başına ölçülür; burada yalnız bir bedel parametresidir.

Süpürme, üstünlüğün mutlak olmadığını gösterir. Sayfa hatası 3 zaman birimine kadar yazarken
kopyalama önde; **4 zaman biriminde hevesli kopyalama öne geçer** ve fark açılarak sürer.
Sonuç, kursun ikinci iddiasının bu dersteki karşılığıdır: **soyutlama bazen kötüleştirir.**
Yazarken kopyalamanın kazanması, çocuğun az sayıda sayfaya dokunmasına **ve** yakalamanın
ucuz olmasına birlikte bağlıdır; ikisinden biri bozulunca kazanç yok olur.

Tablonun 3 satırı ayrıca çözünürlüğün nasıl okunacağını gösterir. Orada hevesli 64, yazarken
60 verir; aradaki 4 zaman birimi çözünürlüğün (2 zaman birimi) üstündedir ve sayılır. Fark 1
olsaydı satır **ölçülemez** yazılırdı, çünkü bu modelde 1 zaman birimlik bir üstünlük bir
sonucu değil bir yuvarlamayı ifade eder.

İki uç durum bu bağımlılığı keskinleştirir. Çocuk çatallamadan hemen sonra kendi programını
yükleyip adres uzayını tümüyle değiştirirse, hevesli yordamın kopyaladığı **64 sayfanın
64'ü** boşa gider. Tersine, çocuk bütün 16 sayfaya yazarsa yazarken kopyalama 64 sayfayı yine
kopyalar ve üstüne 64 sayfa hatası ödetir; bu iş yükünde 64 yerine 128 zaman birimi eder.

#### Özet

- Çatallama, var olan bir süreçten yeni bir süreç türetir ve sürecin durumunu üçe ayırır:
  kopyalanan adres uzayı, kalıtılan değerler, paylaşılan nesneler.
- Bedeli olan tek davranış kopyalamadır; kalıtım bir değer atamasıdır, paylaşım ise aynı
  nesneyi göstermektir ve sıfır sayfa tutar.
- Bu ağaçta dört çatallama hevesli yordamla **64 sanal sayfa**, yazarken kopyalamayla
  **15 sanal sayfa** taşır; aradaki 49 sayfa boşa kopyalanmıştır.
- Hevesli çatallama, beş sürecin toplam hesap işinin **1,641 katıdır**; süreç kurmak, süreçlerin
  yapacağı işten pahalı olabilir.
- Kalıtım ağaç boyunca zincirlenir: bu ağaçta beş sürecin üç ayrı öncelik düzeyi, kalıtım
  sonrası **tek düzeye** iner ve öncelik bakan bir çizelgeleyici için ayırt edilemez olur.
- Sayfa hatası bedeli **4 zaman birimine** çıktığında yazarken kopyalama üstünlüğünü yitirir;
  ertelenmiş kopyalama her koşulda daha ucuz değildir.

#### Sonraki Adım

Çatallamanın bedeli, yalıtımın bedelidir: iki süreç birbirinin belleğine dokunamasın diye
sayfalar çoğaltılır. Yalıtımdan vazgeçilirse bu bedel de ortadan kalkar. Sonraki ders,
aynı adres uzayını paylaşan ama kendi yığıtlarını tutan yürütme birimlerini ele alacak ve iki
sayıyı karşılaştıracak: aynı işi süreçle ve iş parçacığıyla yapmanın **kopyalanan sayfa** ve
**bağlam değiştirme** farkı.

### İş Parçacıkları

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/is-parcaciklari

Paylaşılan adres uzayı ve bağımsız yığıtlar: aynı beş birimlik iş, süreçle kurulduğunda yazarken kopyalamayla bile 15 ek sanal sayfa isterken iş parçacığıyla 4 yığıt sayfasına iniyor, süre 194'ten 186 zaman birimine düşüyor ve bağlam değiştirme sayısı 22'de değişmeden kalıyor; kazanç zamanda yüzde 4,12, bellekte yüzde 73,33.

Önceki ders, çatallamanın bedelinin **yalıtımın bedeli** olduğunu ölçtü: iki süreç birbirinin
belleğine dokunamasın diye sayfalar çoğaltılıyordu. Bu, doğal bir soruyu açar. Aynı programın
parçaları birbirinin belleğine **dokunmak istiyorsa** kopyalamaya ne gerek var.

Bu sorunun yanıtı **iş parçacığıdır (thread)**: adres uzayını paylaşan ama kendi yığıtını
tutan bir yürütme birimi. Bu ders, önceki iki dersteki aynı iş yükünü iki kurulumla koşturur
ve iki sayıyı yan yana koyar — kopyalanan sayfa ve bağlam değiştirmenin ödettiği zaman
birimi.

- **SR16.** İş parçacığı, bir sürecin içindeki bir yürütme birimidir. Adres uzayını süreçteki
  öteki iş parçacıklarıyla **paylaşır**, kendi **yığıtını** ayrı tutar.
- **SR17.** Her yeni iş parçacığı yığıtı için **1 sanal sayfa** ister. Bu bir seçimdir;
  sayfa sayısı büyürse iş parçacığının ek maliyeti de büyür.
- **SR18.** Bir **süreç** bağlam değiştirmesi adres uzayını da değiştirir ve **2 zaman birimi**
  tutar (ortak tanımın `BAGLAM_BEDELI` değeri).
- **SR19.** Bir **iş parçacığı** bağlam değiştirmesinde adres uzayı aynı kalır; bu modelde
  bedeli **1 zaman birimidir**. Sonuç bu seçime duyarlıdır ve duyarlılık ders içinde süpürülür.
- **SR20.** İş yükü, tohumu ve çizelgeleyici önceki iki dersteki ile **aynıdır**. Değişen tek
  şey birimlerin nasıl kurulduğudur; iş aynı iştir.
- **SR21.** Paylaşılan adres uzayının **doğruluk** sonuçları bu derste ölçülmez. Yarış koşulu
  ve karşılıklı dışlama, kursun eşzamanlılık konusunun işidir.
- **SR22.** Ölçünün çözünürlüğü **2 zaman birimidir**.

#### Aynı İş, İki Kurulum

Beş yürütme birimi aynı işi yapacak. Süreç kurulumunda her birim kendi adres uzayında koşar
ve önceki dersteki çatallama bedelini öder. İş parçacığı kurulumunda beş birim **tek adres
uzayında** koşar; kopyalanacak sayfa yoktur, ama her birim kendi yığıtını ister.

```python
# Bu makine bir BENZETICIDIR. Hicbir gercek is parcacigi, kilit ya da
# eszamanlilik kitapligi cagrilmaz; butun sureler modeldeki zaman birimidir.
TOHUM = 20260218
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30
BAGLAM_BEDELI = 2        # surec baglam degistirmesi: adres uzayi da degisir
IP_BAGLAM_BEDELI = 1     # is parcacigi baglam degistirmesi: adres uzayi ayni kalir
SANAL_SAYFA = 16
YIGIT_SAYFASI = 1        # her is parcacigi kendi yigiti icin bir sanal sayfa ister


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    """Bir adim ya ("HESAP", sanal_sayfa) ya da ("BEKLE", sure)."""
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


ISLER = is_yuku()
DOKUNUS = {i["ad"]: {v for t, v in i["adim"] if t == "HESAP"} for i in ISLER}
YENI = [i["ad"] for i in ISLER[1:]]          # ilk birim disindaki dort birim
SUREC_HEVESLI = len(YENI) * SANAL_SAYFA
SUREC_YAZARKEN = sum(len(DOKUNUS[a]) for a in YENI)
IS_PARCACIGI = len(YENI) * YIGIT_SAYFASI
print("ayni is, uc kurulum:", len(ISLER), "yurutme birimi,",
      len(set().union(*DOKUNUS.values())), "ayri sanal sayfa")
print()
print("kurulum            adres uzayi  kopyalanan sayfa  ek yigit sayfasi  toplam ek sayfa")
print(f"  surec (hevesli)  {len(ISLER):11d} {SUREC_HEVESLI:17d} {0:17d} {SUREC_HEVESLI:16d}")
print(f"  surec (yazarken) {len(ISLER):11d} {SUREC_YAZARKEN:17d} {0:17d} {SUREC_YAZARKEN:16d}")
print(f"  is parcacigi     {1:11d} {0:17d} {IS_PARCACIGI:17d} {IS_PARCACIGI:16d}")
```

```
ayni is, uc kurulum: 5 yurutme birimi, 15 ayri sanal sayfa

kurulum            adres uzayi  kopyalanan sayfa  ek yigit sayfasi  toplam ek sayfa
  surec (hevesli)            5                64                 0               64
  surec (yazarken)           5                15                 0               15
  is parcacigi               1                 0                 4                4
```

Bellek tarafındaki fark keskindir. Beş süreç, en iyi durumda bile 15 ek sanal sayfa ister;
beş iş parçacığı 4 ister ve bunlar da adres uzayının kopyaları değil, yığıt sayfalarıdır.
Ortak veriye ulaşmak için **hiçbir kopya** yapılmaz: 15 sanal sayfalık ortak alan tek nüsha
olarak durur ve beş birim de aynı nüshayı görür.

#### Neyi Ayrı Tutar, Neyi Ortaklaşır

Bir iş parçacığının bağlam değiştirmesinin neden daha ucuz olduğu, taşınan durumun
sayılmasıyla görülür. Bu kursun ilk dersinde süreç tablosunun dokuz alan taşıdığı sayılmıştı.
O dokuz alanın hepsi yürütme birimine özgü değildir: bir bölümü birimin kendi ilerlemesini,
bir bölümü ise içinde bulunduğu adres uzayını anlatır.

```python
# Ilk blogun uzerine: ISLER oradan gelir.
OZEL = ("ad", "yer", "hazir", "kullanim", "bitis", "bekleme")   # birime ozel
ORTAK = ("adim", "varis", "oncelik")                            # adres uzayi duzeyinde
n = len(ISLER)
surec_alan = n * (len(OZEL) + len(ORTAK))
ip_alan = n * len(OZEL) + len(ORTAK)
print("birime ozel alan :", OZEL)
print("ortak alan       :", ORTAK)
print()
print(f"surec kurulumu       : {n} girdi x {len(OZEL) + len(ORTAK)} alan = {surec_alan} alan")
print(f"is parcacigi kurulumu: {n} x {len(OZEL)} + {len(ORTAK)} = {ip_alan} alan")
print("baglam degistirmede tasinan alan farki:", surec_alan - ip_alan,
      f"| yuzde {round(100 * (surec_alan - ip_alan) / surec_alan, 2)} azalma")
```

```
birime ozel alan : ('ad', 'yer', 'hazir', 'kullanim', 'bitis', 'bekleme')
ortak alan       : ('adim', 'varis', 'oncelik')

surec kurulumu       : 5 girdi x 9 alan = 45 alan
is parcacigi kurulumu: 5 x 6 + 3 = 33 alan
baglam degistirmede tasinan alan farki: 12 | yuzde 26.67 azalma
```

Dokuz alanın altısı birime özeldir: adı, kaldığı adım, ne zaman koşmaya hazır olduğu, ne kadar
çekirdek kullandığı, ne zaman bittiği. Üçü adres uzayı düzeyindedir ve tek nüsha yeter. Süreç
kurulumu bu üç alanı beş kez tutar, iş parçacığı kurulumu bir kez; toplam 45 alandan 33 alana,
yüzde 26,67 azalmayla iner.

Bu, bir sonraki bölümdeki zaman farkının nedenidir. Bağlam değiştirmenin bedeli, saklanan ve
geri yüklenen durumun büyüklüğüyle orantılıdır; iş parçacığı geçişinde adres uzayı düzeyindeki
alanlar **hiç dokunulmadan** yerinde kalır.

#### Bağlam Değiştirmenin İki Fiyatı

Zaman tarafı bellek tarafı kadar keskin değildir ve bu, dersin ölçmesi gereken asıl şeydir.
Çizelgeleyici iki kurulumda **aynı kararları** verir; iş yükü değişmediği için hangi birimin
ne zaman koşacağı da değişmez. Değişen tek şey bir geçişin fiyatıdır.

```python
# Ilk blogun uzerine: ISLER, BAGLAM_BEDELI, IP_BAGLAM_BEDELI,
# SUREC_YAZARKEN ve IS_PARCACIGI oradan gelir.
SONSUZ = 10**9


def cizelgele(isler, yordam="sirayla", dilim=4, cekirdek=1,
              baglam_bedeli=BAGLAM_BEDELI):
    """yordam: sirayla (onalimsiz) | dilimli | oncelikli | adil
    Her zaman biriminde her cekirdek en cok bir adim yurutur."""
    durum = [{"ad": i["ad"], "adim": list(i["adim"]), "yer": 0, "varis": i["varis"],
              "hazir": i["varis"], "kullanim": 0, "bitis": None,
              "oncelik": i["oncelik"]} for i in isler]
    pay_siniri = SONSUZ if yordam == "sirayla" else dilim
    cekirdekler = [{"surec": None, "onceki": None, "pay": 0, "engel": 0}
                   for _ in range(cekirdek)]
    t, baglam, bos_adim = 0, 0, 0

    def bitmedi(d):
        return d["yer"] < len(d["adim"])

    while any(bitmedi(d) for d in durum) or any(d["bitis"] is None or d["bitis"] > t
                                               for d in durum):
        for c in cekirdekler:                       # birakma
            d = c["surec"]
            if d is not None and (not bitmedi(d) or d["hazir"] > t or c["pay"] >= pay_siniri):
                c["surec"] = None
        for c in cekirdekler:                       # atama
            if c["engel"] or c["surec"] is not None:
                continue
            tutulan = [x["surec"] for x in cekirdekler if x["surec"] is not None]
            hazir = [d for d in durum if bitmedi(d) and d["hazir"] <= t and d not in tutulan]
            if not hazir:
                continue
            if yordam == "oncelikli":
                sec = min(hazir, key=lambda d: (-d["oncelik"], d["hazir"], d["ad"]))
            elif yordam == "adil":
                sec = min(hazir, key=lambda d: (d["kullanim"], d["hazir"], d["ad"]))
            else:
                sec = min(hazir, key=lambda d: (d["hazir"], d["ad"]))
            if c["onceki"] is not None and c["onceki"] is not sec:
                baglam += 1
                c["engel"] = baglam_bedeli
            c["surec"] = sec
            c["onceki"] = sec
            c["pay"] = 0
        for c in cekirdekler:                       # yurutme
            if c["engel"]:
                c["engel"] -= 1
                continue
            d = c["surec"]
            if d is None:
                bos_adim += 1
                continue
            tur, deger = d["adim"][d["yer"]]
            if tur == "BEKLE":
                d["yer"] += 1
                d["hazir"] = t + deger
                c["surec"] = None
                bos_adim += 1
                if not bitmedi(d):                  # son adim beklemeyse is o zaman biter
                    d["bitis"] = t + deger
            else:
                d["yer"] += 1
                d["kullanim"] += 1
                d["hazir"] = t + 1
                c["pay"] += 1
                if not bitmedi(d):
                    d["bitis"] = t + 1
                    c["surec"] = None
        t += 1
    for d, i in zip(durum, isler):
        gc = sum(v for tur, v in i["adim"] if tur == "BEKLE")
        d["bekleme"] = d["bitis"] - d["varis"] - d["kullanim"] - gc
    return {"sure": t, "baglam": baglam, "bos_cekirdek_adimi": bos_adim,
            "toplam_is": sum(d["kullanim"] for d in durum),
            "ortalama_tamamlanma": round(sum(d["bitis"] - d["varis"] for d in durum) / len(durum), 2),
            "ortalama_bekleme": round(sum(d["bekleme"] for d in durum) / len(durum), 2),
            "bitis": {d["ad"]: d["bitis"] for d in durum}}


KURULUM = (("surec", BAGLAM_BEDELI, SUREC_YAZARKEN),
           ("is parcacigi", IP_BAGLAM_BEDELI, IS_PARCACIGI),
           ("bedelsiz taban", 0, 0))
OLCUM = {ad: cizelgele(ISLER, "sirayla", baglam_bedeli=b) for ad, b, _ in KURULUM}
print("kurulum          baglam bedeli  baglam  sure  ort.tamamlanma  ek sayfa")
for ad, b, sayfa in KURULUM:
    s = OLCUM[ad]
    print(f"  {ad:15s} {b:13d} {s['baglam']:7d} {s['sure']:5d}"
          f" {s['ortalama_tamamlanma']:15.2f} {sayfa:9d}")
p, ip = OLCUM["surec"], OLCUM["is parcacigi"]
print()
print("surecten is parcacigina gecis")
print("  ek sayfa :", SUREC_YAZARKEN, "->", IS_PARCACIGI,
      f"| yuzde {round(100 * (SUREC_YAZARKEN - IS_PARCACIGI) / SUREC_YAZARKEN, 2)} azalma")
print("  sure     :", p["sure"], "->", ip["sure"],
      f"| yuzde {round(100 * (p['sure'] - ip['sure']) / p['sure'], 2)} azalma")
print("  baglam   :", p["baglam"], "->", ip["baglam"], "| degismedi")
print()
print("baglam bedeli duyarliligi")
print("bedel  sure  ort.bekleme")
for b in range(5):
    s = cizelgele(ISLER, "sirayla", baglam_bedeli=b)
    print(f"{b:5d} {s['sure']:5d} {s['ortalama_bekleme']:12.2f}")
```

```
kurulum          baglam bedeli  baglam  sure  ort.tamamlanma  ek sayfa
  surec                       2      22   194          148.40        15
  is parcacigi                1      22   186          142.80         4
  bedelsiz taban              0      22   178          137.20         0

surecten is parcacigina gecis
  ek sayfa : 15 -> 4 | yuzde 73.33 azalma
  sure     : 194 -> 186 | yuzde 4.12 azalma
  baglam   : 22 -> 22 | degismedi

baglam bedeli duyarliligi
bedel  sure  ort.bekleme
    0   178         3.40
    1   186         9.00
    2   194        14.60
    3   202        20.20
    4   211        26.80
```

#### Kazanç Nerede, Nerede Değil

Üçüncü satır tabandır: bağlam değiştirme bedava olsaydı bu iş yükü 178 zaman biriminde
biterdi. 194 ile 178 arasındaki 16 birim, süreç kurulumunda paylaşımın ödettiği adımdır ve
tam olarak $22 \times 2$'dir. İş parçacığı kurulumu bu kalemi yarıya indirir: 186, yani
$22 \times 1$ fazlasıyla taban.

**Bağlam değiştirme sayısı değişmez.** Her iki kurulumda da 22 geçiş olur, çünkü geçiş
kararını iş yükü verir, birimin türü değil. İş parçacığı geçişleri azaltmaz; **her geçişi
ucuzlatır**.

Kazancın büyüklüğü iki tarafta çok farklıdır. Bellekte ek sayfa 15'ten 4'e, yani yüzde
73,33 azalır. Zamanda süre 194'ten 186'ya, yani yüzde 4,12 azalır. Sekiz zaman birimi
çözünürlüğün (2 birim) üstündedir, dolayısıyla ölçülmüştür ve gerçektir — ama bellekteki
kazanç yanında küçüktür. **İş parçacığının bu iş yükündeki kazancı zamanda değil,
bellektedir.** Zaman kazancı ancak bağlam değiştirme oranı yükseldikçe büyür; süpürme bunu
gösterir, bedel 4'e çıktığında aynı iş yükü 211 zaman birimi sürer.

Kurulum anındaki fark, koşum sırasındaki farktan büyüktür. Önceki dersin bedel ölçüsüyle —
bir sanal sayfanın kopyalanması 1 zaman birimi — dört süreç kurmak hevesli yordamla 64,
yazarken kopyalamayla 15 zaman birimi tutar; dört iş parçacığı kurmak 4 yığıt sayfası, yani
**4 zaman birimi** tutar. Kısa ömürlü birimlerde toplam maliyeti belirleyen kalem koşum değil
kurulumdur ve orada oran 15'e 4'tür.

Kazancın küçük kalmasının nedeni iş yükünün bileşimidir: 60 adımın 21'i bekleme adımıdır ve
bekleme adımları 630 zaman birimi tutar. Bağlam değiştirmeye giden 44 birim, bu büyüklüğün
yanında küçük kalır. Hesap ağırlıklı bir iş yükünde oran tersine döner ve iş parçacığının
zaman kazancı belirginleşir.

#### Paylaşımın İkinci Yüzü

Tabloda görünmeyen bir kalem var ve dersin dürüst olması için yazılması gerekir. Süreç
kurulumunda beş birim birbirinin belleğini **göremez**; iş parçacığı kurulumunda beşi de
15 sanal sayfalık ortak alanın tamamını görür ve değiştirebilir. Kopyalamadan kaçınmanın
yolu, kopyayı gereksiz kılmaktan geçer — yani yalıtımı kaldırmaktan.

Yığıtların ayrı olması bu görünürlüğü sınırlamaz. Yığıt ayrımı, iki birimin birbirinin yerel
değişkenlerini kazara ezmesini engeller; ortak alandaki veriyi korumaz. Ortak alan zaten
paylaşılsın diye oradadır ve iş parçacığı kurulumunun bütün kazancı ondan gelir; korunması
gereken şeyle kazancın kaynağı aynı 15 sayfadır.

Bu, ek bir sayfa maliyeti değildir; bir **doğruluk** maliyetidir ve sayfa cinsinden
ölçülemez. İki birim aynı sayfaya aynı anda yazdığında ortaya çıkan sorun, bu kursun
eşzamanlılık konusunda kendi ölçüsüyle ele alınır. Bu konunun sonraki dersi ise ödünleşimin
öteki ucunu sayar: paylaşılan durumu bozan tek bir adımın **kaç birimi** etkilediğini.

#### Özet

- İş parçacığı, adres uzayını süreçteki öteki iş parçacıklarıyla paylaşan, kendi yığıtını
  ayrı tutan bir yürütme birimidir.
- Aynı beş birimlik iş, süreçle kurulduğunda yazarken kopyalamayla bile **15 ek sanal sayfa**
  ister; iş parçacığıyla **4 yığıt sayfası** yeter, azalma yüzde 73,33'tür.
- Bağlam değiştirme **sayısı** iki kurulumda da 22'dir; birimin türü geçiş sayısını değil,
  geçişin fiyatını değiştirir.
- Süre 194'ten 186 zaman birimine iner. Fark 8 birimdir, çözünürlüğün üstündedir ve
  ölçülmüştür; ama yüzde 4,12'dir ve bellekteki kazanç yanında küçüktür.
- Bedel süpürmesi sonucun bir seçime bağlı olduğunu gösterir: bağlam bedeli 0'da süre 178,
  4'te 211 olur.
- Paylaşımın sayfa cinsinden ölçülemeyen bir bedeli vardır: beş iş parçacığı ortak alanın
  tamamını görür ve yalıtım ortadan kalkar.

#### Sonraki Adım

Elde iki kurulum ve iki sayı var: iş parçacığı daha az sayfa ve biraz daha az zaman
istiyor, süreç ise yalıtım veriyor. Bir seçim yapmak için yalıtımın da bir sayısı olmalı.
Sonraki ders bu sayıyı üretecek: paylaşılan durumu bozan **tek bir adımın** kaç yürütme
birimine yayıldığını ölçecek ve yalıtımın kaç sayfaya mal olduğunu bunun karşısına koyacak.

### Süreç ve İş Parçacığı Seçimi

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/surec-ve-is-parcacigi-secimi

Yalıtım ile paylaşım arasındaki ödünleşimin sayılması: tek adres uzayında beş iş parçacığı ortak sayfalar üzerinden tek bileşen oluşturuyor ve bozulan herhangi bir sayfa beş birimin hepsine yayılıyor, süreç kurulumunda aynı bozulma tek birimde kalıyor; yayılma oranı 5,0 ve bunu 1,0'a indirmenin bedeli 15 kopyalanan sayfa.

Önceki ders iki kurulumu yan yana koydu ve iş parçacığının ucuz olduğunu ölçtü: 15 ek sanal
sayfa yerine 4, 194 zaman birimi yerine 186. Bu sayılara bakıp iş parçacığını seçmek, tabloda
olmayan bir sütunu görmezden gelmek olur. Süreç kurulumunun karşılığında verdiği şey
**yalıtımdı** ve yalıtımın da bir sayısı olmadan seçim yapılamaz.

Bu ders o sayıyı üretir. Soru şudur: paylaşılan durumu bozan **tek bir adım**, kaç yürütme
birimini etkiler. Yanıt, iki kurulumda birbirinden beş kat farklıdır ve bu, kursun ikinci
iddiasının bu konudaki karşılığıdır — **soyutlama bazen kötüleştirir**, çünkü paylaşımın
ucuzluğu bozulmanın da ucuzca yayılması demektir.

- **SR23.** Beş yürütme birimi, önceki iki derste kullanılan aynı iş yükünü koşar. Her birim
  belirli sanal sayfalara dokunur; dokunma kümesi iş yükünün hesap adımlarından okunur.
- **SR24.** **Bozulma**, bir birimin bir sayfaya hatalı değer yazmasıdır. Tek adımdır ve
  ölçüm boyunca bir kez olur.
- **SR25.** **Doğrudan etki**, bozulan sayfaya dokunan birimlerin sayısıdır.
- **SR26.** **Yayılan etki**, bozulmanın ortak sayfalar üzerinden ulaştığı birimlerin
  sayısıdır: bozuk veriyi okuyan birim kendi sayfalarına da bozuk değer yazar. Bu bir
  modelleme seçimidir ve kötümser yöndedir.
- **SR27.** Aynı adres uzayındaki birimler, **ortak sayfa** üzerinden birbirine bağlı sayılır.
  Yayılan etki, bu çizgenin bağlantılı bileşenidir.
- **SR28.** Ayrı adres uzayındaki birimler arasında **kenar yoktur**. Yalıtımın tanımı budur:
  bir sürecin yazması bir başkasının sayfasına ulaşamaz.
- **SR29.** Bileşen hesabı **Veri Yapıları** kursunun derine arama dersinde kurulan gezinmeyi
  doğrudan kullanır; yordam burada **yeniden anlatılmaz**.
- **SR30.** Bu derste zaman ölçülmez. Ölçülen iki büyüklük **etkilenen birim** ve
  **kopyalanan sayfadır**.

#### Ödünleşimin İki Ucu

Bir uçta **tam paylaşım** vardır: beş birim tek adres uzayında koşar, hiçbir sayfa
kopyalanmaz, her birim her sayfayı görür. Öteki uçta **tam yalıtım** vardır: beş ayrı adres
uzayı, dört çatallama, kopyalanan sayfalar ve birbirinin belleğine dokunamayan birimler.

Bu iki ucun arasında sürekli bir aralık yoktur; **adres uzayı sayısı bir tam sayıdır** ve
seçim, birimlerin kaç adres uzayına bölüştürüleceğidir. Beş birim tek uzayda, iki uzayda, üç
uzayda ya da beş uzayda kurulabilir. Her bölüşüm iki sayı üretir ve bu iki sayı ters yönde
hareket eder.

Ödünleşim terimi burada gevşek kullanılmaz. İki büyüklük aynı nedenden doğar: kopyalamaktan
kaçınmak, kopyanın verdiği ayrımdan da kaçınmaktır. Bir kurulumda ikisini birden kazanmanın
yolu yoktur ve bu dersin bütün sayıları o kısıtın içinde okunur.

#### Paylaşılan Sayfa Haritası

Ölçüm, hangi birimin hangi sayfaya dokunduğunun bilinmesini gerektirir. İş yükü bunu zaten
belirler; ilk blok haritayı çıkarır.

```python
# Bu makine bir BENZETICIDIR. Hicbir gercek surec ya da is parcacigi yaratilmaz;
# butun sayilar modelin icinde uretilir.
TOHUM = 20260218
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30
SANAL_SAYFA = 16


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    """Bir adim ya ("HESAP", sanal_sayfa) ya da ("BEKLE", sure)."""
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


ISLER = is_yuku()
ADLAR = [i["ad"] for i in ISLER]
DOKUNUS = {i["ad"]: {v for t, v in i["adim"] if t == "HESAP"} for i in ISLER}
print("birim  dokundugu sanal sayfa  adet")
for a in ADLAR:
    print(f"  {a:4s} {str(sorted(DOKUNUS[a])):22s} {len(DOKUNUS[a]):4d}")
print("ayri sayfa:", len(set().union(*DOKUNUS.values())))
print()
print("sayfa  dokunan birim")
for s in range(SANAL_SAYFA):
    dokunan = [a for a in ADLAR if s in DOKUNUS[a]]
    if len(dokunan) >= 2:
        print(f"{s:5d}  {' '.join(dokunan)}")
```

```
birim  dokundugu sanal sayfa  adet
  S1   [0, 1, 2, 3]              4
  S2   [3, 4, 5, 6]              4
  S3   [6, 7, 8, 9]              4
  S4   [9, 11, 12]               3
  S5   [12, 13, 14, 15]          4
ayri sayfa: 15

sayfa  dokunan birim
    3  S1 S2
    6  S2 S3
    9  S3 S4
   12  S4 S5
```

Harita dar görünür: 15 sayfanın yalnız **dördü** birden çok birim tarafından kullanılır ve
hiçbiri ikiden fazlası tarafından kullanılmaz. Doğrudan etkiye bakılırsa paylaşımın riski
küçük durur — en kötü durumda iki birim.

#### Bozulma Nereye Kadar Yayılır

Doğrudan etki yanıltıcıdır, çünkü bozulma orada durmaz. S1 ile S2 sayfa 3'ü paylaşır, S2 ile
S3 sayfa 6'yı, S3 ile S4 sayfa 9'u, S4 ile S5 sayfa 12'yi. Dört ortak sayfa, beş birimi uçtan
uca bağlayan bir **zincir** kurar.

```python
# Ilk blogun uzerine: ADLAR, DOKUNUS ve SANAL_SAYFA oradan gelir.


def bilesen(grup):
    """Ayni adres uzayindaki birimler, ortak sayfa uzerinden birbirine baglidir.
    Bozulmanin yayildigi kume, bu cizgenin baglantili bilesenidir."""
    komsu = {a: [b for b in grup if b != a and DOKUNUS[a] & DOKUNUS[b]] for a in grup}
    goruldu, bilesenler = set(), []
    for a in grup:
        if a in goruldu:
            continue
        yigit, kume = [a], []
        while yigit:                                  # derine arama
            d = yigit.pop()
            if d in goruldu:
                continue
            goruldu.add(d)
            kume.append(d)
            yigit.extend(komsu[d])
        bilesenler.append(sorted(kume))
    return bilesenler


def kurulum(gruplar):
    """gruplar: her biri bir adres uzayini paylasan birim adlari."""
    bs = [b for g in gruplar for b in bilesen(g)]
    hevesli = (len(gruplar) - 1) * SANAL_SAYFA
    yazarken = sum(len(set().union(*(DOKUNUS[a] for a in g))) for g in gruplar[1:])
    return {"adres_uzayi": len(gruplar), "bilesen": len(bs),
            "en_buyuk_bilesen": max(len(b) for b in bs),
            "hevesli": hevesli, "yazarken": yazarken}


BOLUSUM = [[ADLAR], [ADLAR[:3], ADLAR[3:]], [ADLAR[:2], ADLAR[2:4], ADLAR[4:]],
           [[a] for a in ADLAR]]
print("adres uzayi  bilesen  en buyuk bilesen  hevesli kopya  yazarken kopya")
for g in BOLUSUM:
    k = kurulum(g)
    print(f"{k['adres_uzayi']:11d} {k['bilesen']:8d} {k['en_buyuk_bilesen']:17d}"
          f" {k['hevesli']:14d} {k['yazarken']:15d}")
print("tek adres uzayinda bilesen:", bilesen(ADLAR))
```

```
adres uzayi  bilesen  en buyuk bilesen  hevesli kopya  yazarken kopya
          1        1                 5              0               0
          2        2                 3             16               6
          3        3                 2             32              10
          5        5                 1             64              15
tek adres uzayinda bilesen: [['S1', 'S2', 'S3', 'S4', 'S5']]
```

Son satır dersin çekirdeğidir. Tek adres uzayında beş birim **tek bileşen** oluşturur; hiçbir
birim çiftinin doğrudan ortak sayfası olmasa bile, zincir üzerinden hepsi birbirine bağlıdır.
S1 ile S5'in ortak hiçbir sayfası yoktur, ama S1'in bozduğu bir değer S2 üzerinden S3'e, S3
üzerinden S4'e, oradan S5'e ulaşır.

#### Tek Adımın Etkisi

Yayılmayı sayfa sayfa saymak, doğrudan etki ile yayılan etki arasındaki farkı görünür kılar.

```python
# Onceki bloklarin uzerine: ADLAR, DOKUNUS, SANAL_SAYFA ve bilesen.
YAYILAN = {a: b for b in bilesen(ADLAR) for a in b}
print("bozulan sayfa  surec modeli  is parcacigi dogrudan  is parcacigi yayilan")
toplam = [0, 0, 0]
for s in sorted(set().union(*DOKUNUS.values())):
    dokunan = [a for a in ADLAR if s in DOKUNUS[a]]
    yayilan = len({b for a in dokunan for b in YAYILAN[a]})
    toplam = [toplam[0] + 1, toplam[1] + len(dokunan), toplam[2] + yayilan]
    print(f"{s:13d} {1:13d} {len(dokunan):22d} {yayilan:21d}")
print("15 sayfanin toplami:", toplam[0], "|", toplam[1], "|", toplam[2])
print("yayilan / surec modeli orani:", round(toplam[2] / toplam[0], 4))
```

```
bozulan sayfa  surec modeli  is parcacigi dogrudan  is parcacigi yayilan
            0             1                      1                     5
            1             1                      1                     5
            2             1                      1                     5
            3             1                      2                     5
            4             1                      1                     5
            5             1                      1                     5
            6             1                      2                     5
            7             1                      1                     5
            8             1                      1                     5
            9             1                      2                     5
           11             1                      1                     5
           12             1                      2                     5
           13             1                      1                     5
           14             1                      1                     5
           15             1                      1                     5
15 sayfanin toplami: 15 | 19 | 75
yayilan / surec modeli orani: 5.0
```

Son sütun **hangi sayfa bozulursa bozulsun 5'tir**. Bozulmanın nerede başladığı önemli
değildir; tek adres uzayında her yol her yere çıkar. Süreç modelinde aynı sütun sabit 1'dir,
çünkü kenar yoktur. **Yayılma oranı 5,0'dır** ve bu, tam olarak birim sayısıdır.

Doğrudan etkinin toplamı 19'dur, yayılan etkinin toplamı 75. Aradaki 56 birim-sayfa, yalnız
haritanın dar görünmesine bakan bir çözümlemenin **kaçıracağı** risktir. Paylaşılan sayfa
sayısını azaltmak bu farkı kapatmaz; zinciri **kesmek** gerekir ve zinciri kesmenin tek yolu
ayrı bir adres uzayıdır.

Çökme bu tablonun uç durumudur. Bir iş parçacığı adres uzayını kullanılamaz hâle getirirse
etkilenen birim doğrudan 5 olur; ara adım yoktur. Bir süreç aynı şeyi yaparsa 1 olur ve
kalan dört birim koşmayı sürdürür. Yalıtımın satın aldığı şey, tam olarak bu farktır: beş
birimlik bir kurulumda dört birimin ayakta kalması ile hiçbirinin kalmaması arasındaki fark.

#### Yalıtımı Kısmi Almak

Bölüşüm tablosunun iki sütunu ters yönde hareket eder ve bir eğri kurar. Adres uzayı sayısı
1'den 5'e çıkarken en büyük bileşen 5'ten 1'e iner; kopyalanan sayfa ise 0'dan 15'e çıkar
(hevesli yordamla 0'dan 64'e). Ara noktalar bu değiş tokuşun tümden geçmek zorunda olmadığını
gösterir: iki adres uzayı, 6 kopyalanan sayfayla en büyük bileşeni 5'ten 3'e indirir. İlk
kopyalanan 6 sayfa yayılmayı iki birim azaltır; son kopyalanan 5 sayfa yalnız bir birim
azaltır. **Yalıtımın ilk birimi son biriminden ucuzdur.**

Bu, seçimin "hepsi ya da hiçbiri" olmadığı anlamına gelir. Bir grup birim birbirine güvenip
tek adres uzayını paylaşabilir, güvenilmeyen bir grup ayrı bir adres uzayına konabilir.
Bölüşümün nereden geçeceğine karar veren şey, ortak sayfa haritası değil, **hangi bozulmanın
kabul edilebilir olduğudur**.

#### Zinciri Ucuza Kesmek

Bölüşüm tablosu adres uzayı sayısını değiştirir. Daha ucuz bir yol daha var: adres uzayı tek
kalsın, yalnız **ortak sayfalar** çoğaltılsın. Zinciri kuran şey dört sayfa olduğuna göre,
onları kesmek yeter mi.

```python
# Onceki bloklarin uzerine: ADLAR, DOKUNUS ve SANAL_SAYFA.
ORTAK = [s for s in range(SANAL_SAYFA) if sum(1 for a in ADLAR if s in DOKUNUS[a]) >= 2]


def kesilmis_bilesen(kesilen):
    """Kesilen sayfa her birime ozel kopyalanir; artik kenar kurmaz."""
    kalan = {a: DOKUNUS[a] - set(kesilen) for a in ADLAR}
    komsu = {a: [b for b in ADLAR if b != a and kalan[a] & kalan[b]] for a in ADLAR}
    goruldu, kumeler = set(), []
    for a in ADLAR:
        if a in goruldu:
            continue
        yigit, kume = [a], []
        while yigit:
            d = yigit.pop()
            if d in goruldu:
                continue
            goruldu.add(d)
            kume.append(d)
            yigit.extend(komsu[d])
        kumeler.append(sorted(kume))
    return kumeler


print("ortak sayfa:", ORTAK)
print()
print("kesilen sayfa  ek kopya  bilesen  en buyuk bilesen")
for k in range(len(ORTAK) + 1):
    b = kesilmis_bilesen(ORTAK[:k])
    print(f"{str(ORTAK[:k]):14s} {k:9d} {len(b):8d} {max(len(x) for x in b):17d}")
print()
print("ayni sonuc icin ayri adres uzayi bedeli:",
      sum(len(DOKUNUS[a]) for a in ADLAR[1:]), "kopyalanan sayfa")
```

```
ortak sayfa: [3, 6, 9, 12]

kesilen sayfa  ek kopya  bilesen  en buyuk bilesen
[]                     0        1                 5
[3]                    1        2                 4
[3, 6]                 2        3                 3
[3, 6, 9]              3        4                 2
[3, 6, 9, 12]          4        5                 1

ayni sonuc icin ayri adres uzayi bedeli: 15 kopyalanan sayfa
```

Sayı çarpıcıdır: **4 kopyalanan sayfa**, ayrı adres uzaylarının 15 sayfayla vardığı sonuca
varır. Her kesilen sayfa en büyük bileşeni tam bir birim küçültür ve dördüncüsünde bileşen
sayısı 5'e çıkar.

Bu ucuzluğun üç koşulu vardır ve üçü de yazılmalıdır. Birincisi, **harita önceden
bilinmelidir**; hangi sayfanın paylaşıldığı ancak bütün iş yükü tanındığında söylenebilir.
İkincisi, kesme **donanım tarafından zorlanmaz**; bir birim yanlışlıkla kesilmemiş bir
sayfaya yazarsa zincir geri döner. Üçüncüsü, kesilen sayfa artık paylaşılmıyordur — ortak
veriyle iletişim kuran bir tasarımda kesme, işi bozar.

Ayrı adres uzayının 15 sayfası tam da bu üç koşulu satın alır: harita gerekmez, kural
donanım tarafından zorlanır ve yanlışlıkla bozulamaz. **Yalıtımın 11 fazla sayfası, bilgiye
ve dikkate değil mekanizmaya ödenmiş fiyattır.**

#### Seçim Kuralı

Üç sayı yan yana konduğunda kural kendini yazar. **Taban**, tek birim ve tek adres uzayıdır:
0 kopyalanan sayfa, 1 etkilenen birim, yayılma yok. **Kurulum**, beş birimin tek adres
uzayında koşmasıdır: yine 0 kopyalanan sayfa, ama bileşen 1 ve yayılma 5. **Bedel**, bu
yayılmayı 1'e indirmek için ödenen 15 kopyalanan sayfadır — hevesli yordamla 64.

Karar, işin doğasına bakar. Birimler aynı veriyi sürekli paylaşıyorsa ayrı adres uzayları
paylaşımı da keser ve iş yapılamaz hâle gelir; bu durumda iş parçacığı kaçınılmazdır ve
korunma başka bir katmanda aranır. Birimler birbirinden bağımsız işler yapıyorsa 15 sayfa,
yayılmayı beşte bire indirmenin ucuz bir fiyatıdır. Önceki dersin zaman kazancı olan 8 zaman
birimi, bu tabloda küçük bir kalemdir; **iş parçacığını hızı için seçmek, ölçülen en zayıf
gerekçedir.**

#### Özet

- Yalıtım ile paylaşım arasındaki seçim, birimlerin kaç adres uzayına bölüştürüleceği
  sorusudur ve iki sayı üretir: kopyalanan sayfa ve etkilenen birim.
- Bu iş yükünde 15 sanal sayfanın yalnız dördü birden çok birim tarafından kullanılır;
  doğrudan etki en kötü durumda 2'dir.
- Dört ortak sayfa beş birimi zincirler: tek adres uzayında bileşen sayısı 1'dir ve
  **hangi sayfa bozulursa bozulsun yayılan etki 5'tir**. Yayılma oranı 5,0.
- Süreç kurulumunda aynı bozulma 1 birimde kalır; yalıtımın satın aldığı fark budur ve
  bedeli 15 kopyalanan sayfadır (hevesli yordamla 64).
- Kısmi yalıtım işe yarar ve doğrusal değildir: iki adres uzayı 6 sayfayla en büyük bileşeni
  5'ten 3'e indirir, son 5 sayfa ise yalnız 1 birim kazandırır.
- İş parçacığının zaman kazancı olan 8 zaman birimi, bu ödünleşimin yanında küçüktür;
  seçim hız üzerinden savunulamaz.

#### Sonraki Adım

Buraya kadar birimlerin nasıl kurulduğu soruldu: kaç adres uzayı, kaç kopyalanan sayfa, kaç
etkilenen birim. Kurulum bittiğinde geriye bir soru kalır ve onu her zaman birimi yeniden
sormak gerekir: hazır birimlerden **hangisi** koşacak. Sonraki ders bu kararı veren
çizelgeleyiciyi ele alacak, aynı iş yükünü dört ayrı yordamla koşturacak ve hiçbirinin her
iş yükünde en iyi olmadığını gösterecek.

### Çizelgeleme Algoritmaları

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/cizelgeleme-algoritmalari

Dört çizelgeleyici, aynı iş yükü: önalımsız sıra 194, dilimli 197, öncelikli 195, adil paylaşımlı 192 zaman birimi veriyor; dilim 4'ten 1'e indiğinde süre 206'ya, bağlam değiştirme 36'ya çıkıyor, ikinci iş yükünde sıralama değişiyor ve iki iş yükünde de en iyi olan hiçbir yordam bulunmuyor.

Önceki dört ders yürütme birimlerinin nasıl kurulduğunu ölçtü: kaç adres uzayı, kaç
kopyalanan sayfa, kaç etkilenen birim. Kurulum bittiğinde geriye her zaman biriminde yeniden
sorulan tek bir soru kalır — hazır birimlerden hangisi koşacak.

Bu kararı veren bileşen **çizelgeleyicidir (scheduler)** ve kararın nasıl verildiği ölçülebilir
bir farktır. Bu ders aynı beş süreçlik iş yükünü dört ayrı yordamla koşturur, sonra aynı
karşılaştırmayı ikinci bir iş yükünde yineler. Dersin sonucu tek bir yordamın seçilmesi
değildir; **hiçbir yordamın her iş yükünde en iyi olmadığının** gösterilmesidir.

- **SR31.** Çizelgeleyici her zaman biriminde hazır birimlerden birini seçer. Hazır olmayan
  birim, bekleme adımındadır ve seçilemez.
- **SR32.** **Önalımsız sıra**, bir süreci bitene ya da bir bekleme adımına girene kadar
  bırakmaz; hazır olanlar arasında **en erken hazır olanı** seçer.
- **SR33.** **Dilimli** yordam **önalımlıdır**: bir süreç en çok **4 zaman birimi** koşar,
  sonra çekirdeği bırakır. Bu süreye **zaman dilimi**, kısaca **dilim** denir.
- **SR34.** **Öncelikli** yordam en yüksek öncelikli hazır süreci seçer; öncelikler iş yükünün
  parçasıdır ve ikinci iş yükünde farklıdır.
- **SR35.** **Adil paylaşımlı** yordam, o ana kadar **en az çekirdek kullanmış** hazır süreci
  seçer.
- **SR36.** Ölçülen büyüklükler: **süre** (bütün işin bitmesine kadar geçen zaman birimi),
  **bağlam değiştirme sayısı**, **boş çekirdek adımı**, **ortalama tamamlanma süresi**
  (varıştan bitişe) ve **ortalama bekleme** (hazır ama koşamadığı zaman birimi).
- **SR37.** İkinci iş yükü tohumu **20260219**'dur. Aynı beş süreç yapısı, farklı adım
  bileşimi ve farklı öncelikler verir.
- **SR38.** Çözünürlük **2 zaman birimidir**. İki yordam arasındaki 1 birimlik fark
  **ölçülmemiş sayılır** ve sıralamaya girmez.

#### İki İş Yükü

İki tohum aynı yapıyı üretir ama farklı bir bileşim verir. Fark, hesap adımı ile bekleme adımı
arasındaki dağılımdadır ve çizelgeleyicilerin davranışını belirleyen şey tam olarak budur.

```python
# Bu makine bir BENZETICIDIR. Hicbir gercek cizelgeleyici cagrilmaz; butun
# sureler modeldeki zaman birimidir.
TOHUM = 20260218
IKINCI_TOHUM = 20260219
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30
BAGLAM_BEDELI = 2
SANAL_SAYFA = 16


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    """Bir adim ya ("HESAP", sanal_sayfa) ya da ("BEKLE", sure)."""
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


ISLER = is_yuku()
print("is yuku  hesap adimi  bekleme adimi  oncelikler")
for tohum in (TOHUM, IKINCI_TOHUM):
    y = is_yuku(tohum)
    h = sum(1 for i in y for t, _ in i["adim"] if t == "HESAP")
    print(f"{tohum}  {h:10d}  {SUREC_SAYISI * ADIM_SAYISI - h:13d}"
          f"  {[i['oncelik'] for i in y]}")
```

```
is yuku  hesap adimi  bekleme adimi  oncelikler
20260218          39             21  [3, 2, 1, 3, 2]
20260219          46             14  [1, 2, 3, 1, 1]
```

İkinci iş yükü **daha hesap ağırlıklıdır**: 60 adımın 46'sı çekirdeği tutar, ilkinde bu sayı
39'du. Bu, çizelgeleyici için daha zor bir iştir, çünkü çekirdek için çekişen süreç sayısı
artmıştır. Öncelikler de değişmiştir ve tek bir sürecin önceliği 3'tür.

#### Dört Yordam, Aynı İş Yükü

Aşağıdaki gerçeklenim dört yordamı tek gövdede toplar. Aralarındaki tek fark iki satırdır:
hazır kümesinden hangisinin seçildiği ve bir sürecin dilimini doldurup doldurmadığına
bakılıp bakılmadığı.

```python
# Ilk blogun uzerine: ISLER ve BAGLAM_BEDELI oradan gelir.
SONSUZ = 10**9


def cizelgele(isler, yordam="sirayla", dilim=4, cekirdek=1,
              baglam_bedeli=BAGLAM_BEDELI):
    """yordam: sirayla (onalimsiz) | dilimli | oncelikli | adil
    Her zaman biriminde her cekirdek en cok bir adim yurutur."""
    durum = [{"ad": i["ad"], "adim": list(i["adim"]), "yer": 0, "varis": i["varis"],
              "hazir": i["varis"], "kullanim": 0, "bitis": None,
              "oncelik": i["oncelik"]} for i in isler]
    pay_siniri = SONSUZ if yordam == "sirayla" else dilim
    cekirdekler = [{"surec": None, "onceki": None, "pay": 0, "engel": 0}
                   for _ in range(cekirdek)]
    t, baglam, bos_adim = 0, 0, 0

    def bitmedi(d):
        return d["yer"] < len(d["adim"])

    while any(bitmedi(d) for d in durum) or any(d["bitis"] is None or d["bitis"] > t
                                               for d in durum):
        for c in cekirdekler:                       # birakma
            d = c["surec"]
            if d is not None and (not bitmedi(d) or d["hazir"] > t or c["pay"] >= pay_siniri):
                c["surec"] = None
        for c in cekirdekler:                       # atama
            if c["engel"] or c["surec"] is not None:
                continue
            tutulan = [x["surec"] for x in cekirdekler if x["surec"] is not None]
            hazir = [d for d in durum if bitmedi(d) and d["hazir"] <= t and d not in tutulan]
            if not hazir:
                continue
            if yordam == "oncelikli":
                sec = min(hazir, key=lambda d: (-d["oncelik"], d["hazir"], d["ad"]))
            elif yordam == "adil":
                sec = min(hazir, key=lambda d: (d["kullanim"], d["hazir"], d["ad"]))
            else:
                sec = min(hazir, key=lambda d: (d["hazir"], d["ad"]))
            if c["onceki"] is not None and c["onceki"] is not sec:
                baglam += 1
                c["engel"] = baglam_bedeli
            c["surec"] = sec
            c["onceki"] = sec
            c["pay"] = 0
        for c in cekirdekler:                       # yurutme
            if c["engel"]:
                c["engel"] -= 1
                continue
            d = c["surec"]
            if d is None:
                bos_adim += 1
                continue
            tur, deger = d["adim"][d["yer"]]
            if tur == "BEKLE":
                d["yer"] += 1
                d["hazir"] = t + deger
                c["surec"] = None
                bos_adim += 1
                if not bitmedi(d):                  # son adim beklemeyse is o zaman biter
                    d["bitis"] = t + deger
            else:
                d["yer"] += 1
                d["kullanim"] += 1
                d["hazir"] = t + 1
                c["pay"] += 1
                if not bitmedi(d):
                    d["bitis"] = t + 1
                    c["surec"] = None
        t += 1
    for d, i in zip(durum, isler):
        gc = sum(v for tur, v in i["adim"] if tur == "BEKLE")
        d["bekleme"] = d["bitis"] - d["varis"] - d["kullanim"] - gc
    return {"sure": t, "baglam": baglam, "bos_cekirdek_adimi": bos_adim,
            "toplam_is": sum(d["kullanim"] for d in durum),
            "ortalama_tamamlanma": round(sum(d["bitis"] - d["varis"] for d in durum) / len(durum), 2),
            "ortalama_bekleme": round(sum(d["bekleme"] for d in durum) / len(durum), 2),
            "bitis": {d["ad"]: d["bitis"] for d in durum}}


YORDAM = ("sirayla", "dilimli", "oncelikli", "adil")
print("cizelgeleyici  sure  baglam  bos.cek  ort.tamamlanma  ort.bekleme")
for y in YORDAM:
    s = cizelgele(ISLER, y)
    print(f"  {y:12s} {s['sure']:4d}  {s['baglam']:5d}  {s['bos_cekirdek_adimi']:6d}"
          f"  {s['ortalama_tamamlanma']:13.2f}  {s['ortalama_bekleme']:10.2f}")
print()
print("dilim supurmesi (dilimli)")
print("dilim  sure  baglam  ort.bekleme")
for d in (1, 2, 4, 8, 16):
    s = cizelgele(ISLER, "dilimli", dilim=d)
    print(f"{d:5d}  {s['sure']:4d}  {s['baglam']:5d}  {s['ortalama_bekleme']:11.2f}")
```

```
cizelgeleyici  sure  baglam  bos.cek  ort.tamamlanma  ort.bekleme
  sirayla       194     22     111         148.40       14.60
  dilimli       197     25     108         153.80       20.00
  oncelikli     195     21     114         149.00       15.20
  adil          192     25     103         153.00       19.20

dilim supurmesi (dilimli)
dilim  sure  baglam  ort.bekleme
    1   206     36        33.00
    2   202     29        26.60
    4   197     25        20.00
    8   194     22        14.60
   16   194     22        14.60
```

Dört yordamın süreleri 192 ile 197 arasındadır; aralarındaki en büyük fark **5 zaman
birimidir**, yani toplam sürenin yüzde 2,6'sı. Bu, önemli bir okumadır: **çizelgeleyici
seçimi bu iş yükünde büyük bir kaldıraç değildir.** Asıl belirleyici, iş yükünün kendi
bekleme adımlarıdır ve onlar 630 zaman birimi tutar.

Önalımlı yordam, önalımsız sıradan **3 zaman birimi** daha uzun sürer ve 3 bağlam değiştirme
fazla yapar. Ortalama bekleme 14,60'tan 20,00'a çıkar. Dilimlemenin sağladığı şey — hiçbir
sürecin çekirdeği uzun süre tutamaması — süreyle ödenir.

#### Dilim Küçüldükçe Kötüleşir

Süpürme bu ödemenin doğrudan ölçüsüdür. Dilim 4'ten 1'e indiğinde süre 197'den **206**'ya,
bağlam değiştirme 25'ten **36**'ya, ortalama bekleme 20,00'dan **33,00**'a çıkar. Her adımda
çekirdeği bırakmak, adımların kendisinden çok bağlam değiştirmeye zaman harcamak demektir:
36 geçiş 72 zaman birimi tutar ve iş yükünün toplam hesap adımı 39'dur.

Öteki uçta süpürme durur. Dilim 8 ile dilim 16 **tıpatıp aynı** sonucu verir: 194, 22, 14,60.
Bunun nedeni açıktır — bu iş yükünde hiçbir süreç art arda sekizden fazla hesap adımı
yürütmez, dolayısıyla dilim 8'in üstünde önalım hiç tetiklenmez. **Bu iş yükünün çözünürlüğü
dilim 8'de tükenir**; daha büyük dilimlerle yapılan bir karşılaştırma yeni bilgi vermez.
Dilim 8'deki 194 ve 22, önalımsız sıranın sayılarının tıpatıp aynısıdır ve olması gereken de
budur: önalımın hiç tetiklenmediği bir dilimli yordam, önalımsız yordamdır.

#### Toplam Süre Neyi Gizler

Toplam süre tek başına bakıldığında dört yordam birbirine yakın durur. Yordamların birbirinden
ayrıldığı yer toplam değil, **dağılımdır**: aynı 192 ile 197 arasındaki süre, süreçler
arasında çok farklı biçimlerde bölüşülebilir.

```python
# Onceki bloklarin uzerine: ISLER, cizelgele ve YORDAM.
print("cizelgeleyici  S1   S2   S3   S4   S5   en genis fark")
for y in YORDAM:
    s = cizelgele(ISLER, y)
    t = [s["bitis"][i["ad"]] - i["varis"] for i in ISLER]
    print(f"  {y:12s} " + " ".join(f"{x:4d}" for x in t) + f" {max(t) - min(t):13d}")
print()
print("oncelik 3 olan surecler:", [i["ad"] for i in ISLER if i["oncelik"] == 3])
print("oncelik 1 olan surecler:", [i["ad"] for i in ISLER if i["oncelik"] == 1])
for y in ("sirayla", "oncelikli"):
    s = cizelgele(ISLER, y)
    yuksek = [s["bitis"][i["ad"]] - i["varis"] for i in ISLER if i["oncelik"] == 3]
    dusuk = [s["bitis"][i["ad"]] - i["varis"] for i in ISLER if i["oncelik"] == 1]
    print(f"{y:10s} yuksek oncelik ort. {sum(yuksek) / len(yuksek):6.2f}"
          f" | dusuk oncelik ort. {sum(dusuk) / len(dusuk):6.2f}")
```

```
cizelgeleyici  S1   S2   S3   S4   S5   en genis fark
  sirayla       103  112  176  173  178            75
  dilimli       111  120  175  185  178            74
  oncelikli     103  112  187  169  174            84
  adil          127  115  170  180  173            65

oncelik 3 olan surecler: ['S1', 'S4']
oncelik 1 olan surecler: ['S3']
sirayla    yuksek oncelik ort. 138.00 | dusuk oncelik ort. 176.00
oncelikli  yuksek oncelik ort. 136.00 | dusuk oncelik ort. 187.00
```

Son sütun adil paylaşımlı yordamın ne yaptığını gösterir: en hızlı ve en yavaş sürecin
tamamlanma süreleri arasındaki fark 65 zaman birimidir, dört yordamın en darı. Öncelikli
yordamda aynı fark **84**'tür, en genişi. Adaletin ölçüsü toplam süre değil bu farktır ve
adil paylaşımlı yordam onu 19 birim daraltır.

Öncelikli yordamın kime ne kazandırdığı ayrıca sayılabilir. Önceliği 3 olan iki süreç,
önalımsız sırada ortalama 138,00 zaman biriminde biterken öncelikli yordamda 136,00'da biter:
**2 zaman birimi**, yani çözünürlüğün tam sınırında bir kazanç. Önceliği 1 olan süreç ise
176,00'dan 187,00'a geriler — **11 zaman birimi** kayıp. Öncelik vermek, kayırılanlara
ölçülemeyecek kadar az kazandırıp kayırılmayana ölçülebilir bir bedel ödetir.

#### Sıralama İkinci İş Yükünde Ayakta Kalıyor mu

Tek bir iş yükünde ölçülmüş bir sıralama, o iş yüküne ait bir olgudur. Sonucun bir kural
olabilmesi için ikinci bir iş yükünde de sınanması gerekir.

```python
# Onceki bloklarin uzerine: is_yuku, cizelgele, YORDAM ve IKINCI_TOHUM.
SONUC = {y: [cizelgele(is_yuku(t), y)["sure"] for t in (TOHUM, IKINCI_TOHUM)]
         for y in YORDAM}
EN_IYI = [min(SONUC[y][k] for y in YORDAM) for k in (0, 1)]
print("cizelgeleyici  20260218  fark  20260219  fark")
for y in YORDAM:
    a, b = SONUC[y]
    print(f"  {y:12s} {a:8d} {a - EN_IYI[0]:5d} {b:9d} {b - EN_IYI[1]:5d}")
print()
for k, tohum in enumerate((TOHUM, IKINCI_TOHUM)):
    en_iyi = [y for y in YORDAM if SONUC[y][k] == EN_IYI[k]]
    en_kotu = [y for y in YORDAM if SONUC[y][k] == max(SONUC[z][k] for z in YORDAM)]
    print(f"{tohum}: en iyi {en_iyi} | en kotu {en_kotu}")
print("iki is yukunde de en iyi olan:",
      [y for y in YORDAM if SONUC[y][0] == EN_IYI[0] and SONUC[y][1] == EN_IYI[1]])
print("cozunurluk altinda kalan cift (fark < 2):",
      [(y, z, k) for k in (0, 1) for i, y in enumerate(YORDAM)
       for z in YORDAM[i + 1:] if abs(SONUC[y][k] - SONUC[z][k]) < 2])
```

```
cizelgeleyici  20260218  fark  20260219  fark
  sirayla           194     2       189     0
  dilimli           197     5       202    13
  oncelikli         195     3       196     7
  adil              192     0       195     6

20260218: en iyi ['adil'] | en kotu ['dilimli']
20260219: en iyi ['sirayla'] | en kotu ['dilimli']
iki is yukunde de en iyi olan: []
cozunurluk altinda kalan cift (fark < 2): [('sirayla', 'oncelikli', 0), ('oncelikli', 'adil', 1)]
```

#### Hangi Sonuç Ayakta Kalır

Çıktının son dört satırı bu dersin bütün iddialarını sınırlar.

**Ayakta kalan sonuç: dilimleme adaleti süreyle ödetir.** Dilimli yordam **iki iş yükünde de
en kötüdür** — ilkinde en iyiden 5, ikincisinde 13 zaman birimi geride. İkinci iş yükünde
fark `13/5 = 2,6` katına çıkar, çünkü hesap ağırlıklı bir iş yükünde önalım daha sık
tetiklenir. Bu sonuç
iki ölçümde de aynı yönde durur ve yazılabilir.

**Ayakta kalmayan sonuç: adil paylaşımın üstünlüğü.** Adil paylaşımlı yordam ilk iş yükünde
en iyidir, ikincisinde en iyiden 6 zaman birimi geridedir. Aynı yordam bir iş yükünde birinci,
ötekinde değildir; dolayısıyla **"adil paylaşım en iyisidir" cümlesi ölçülmemiş bir iddiadır
ve kurulamaz.**

**İki iş yükünde de en iyi olan yordam listesi boştur.** Bu, dersin ana sonucudur: bir
çizelgeleyicinin üstünlüğü kendisinin değil, **iş yükünün bileşiminin** özelliğidir. Önalımsız
sıra hesap ağırlıklı ikinci iş yükünde birinciye çıkar, çünkü orada süreçlerin bekleme adımı
azdır ve bölmeden koşturmak toplamı kısaltır.

Son satır çözünürlüğün nasıl çalıştığını gösterir. İlk iş yükünde önalımsız sıra (194) ile
öncelikli (195) arasındaki 1 birim, ikinci iş yükünde öncelikli (196) ile adil paylaşımlı
(195) arasındaki 1 birim çözünürlüğün altındadır. Bu çiftler **ayrılmaz**; aralarında bir
sıralama yazmak, ölçülmemiş bir farkı ölçülmüş gibi sunmak olur.

#### Özet

- Çizelgeleyici, her zaman biriminde hazır birimlerden hangisinin koşacağına karar verir;
  dört yordam bu kararı önalım ve seçim ölçütü bakımından farklı verir.
- Aynı iş yükünde dört yordamın süresi 192 ile 197 arasındadır; en büyük fark 5 zaman
  birimi, yani yüzde 2,6'dır. Çizelgeleyici seçimi bu iş yükünde büyük bir kaldıraç değildir.
- Dilim küçüldükçe kötüleşir: dilim 1'de süre 206, bağlam değiştirme 36, ortalama bekleme
  33,00 olur. Dilim 8 ile 16 aynı sonucu verir; bu iş yükünün çözünürlüğü dilim 8'de tükenir.
- Dilimli yordam **iki iş yükünde de en kötüdür** (197 ve 202); dilimleme adaleti süreyle
  ödetir ve bu sonuç iki ölçümde de ayaktadır.
- İki iş yükünde de en iyi olan yordam **yoktur**: adil paylaşımlı ilkinde birinci, önalımsız
  sıra ikincisinde birincidir. Üstünlük yordamın değil, iş yükünün bileşiminin özelliğidir.
- Çözünürlük altındaki 1 birimlik farklar ölçülmemiş sayılır; iki çift bu nedenle ayrılmaz
  kalır ve aralarına sıralama yazılmaz.

#### Sonraki Adım

Bu konu boyunca birimler birbirinden ayrı sayıldı: her biri kendi adımlarını yürütüyor,
çizelgeleyici aralarında gidip geliyordu. Bir varsayım hiç sınanmadı — bir birimin adımı
bölündüğünde sonucun değişmeyeceği varsayımı. Kursun sonraki konusu bu varsayımı bozar ve
tek bir cümleyi kanıtlar: aynı programın iki birimi aynı veriye dokunduğunda **doğru çıktı
doğru program demek değildir.**

## Eşzamanlılık

Yarış koşulları, karşılıklı dışlama araçları, kilitlenme ve açlık, çok çekirdekte bellek görünürlüğü ve üç eşzamanlılık modelinin karşılaştırması.

### Yarış Koşulları

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/yaris-kosullari

Oku-artır-yaz üçlemesinin bütün serpiştirmeleri, yanlış sonuç veren serpiştirmelerin sayısı ve çizelgeleyici diliminin hatayı nasıl görünmez kıldığı.

Önceki ders çizelgeleyicinin bedelini süreyle ölçtü: dilim küçüldükçe bağlam değiştirme
artıyor, süre uzuyordu. Orada dilim bir **başarım** ayarıydı. Bu derste aynı ayarın ikinci
bir yüzü çıkıyor — dilim, iki iş parçacığının adımlarının **hangi sıralarda** birbirine
girebileceğini de belirler, dolayısıyla programın **doğru sonuç verip vermeyeceğini** de
belirler.

Kurs bu noktada kendi en değerli iddiasını ödemek zorundadır: **doğru çıktı doğru program
demek değildir.** Aşağıdaki ölçü bunu bir kez ve sayıyla gösteriyor. Ölçülen makine yine
ortak tanımın makinesidir; hiçbir gerçek iş parçacığı yaratılmaz, gerçek zaman ölçülmez.

#### Bir Artırma Üç Adımdır

Paylaşılan bir sayacı bir artırmak, kaynak metinde tek satırdır. Yürütmede tek adım
değildir: değer **okunur**, bir **artırılır**, geri **yazılır**. Üç adımın arasında
çizelgeleyici başka bir iş parçacığına geçebilir.

**EZ1.** Sayaç artırımı üç ayrı adımdır; adımların arası bölünebilir.
**EZ2.** İki iş parçacığı vardır, her biri sayacı bir kez artırır; beklenen sonuç **2**'dir.
**EZ3.** Okuma ve yazma anlıktır; iki adım aynı zaman biriminde çakışmaz. Yürütme her zaman
bir **serpiştirme (interleaving)** ile, yani iki adım dizisinin tek bir sıralamasıyla
tarif edilebilir.

Ortak tanımın `sayac_kosumu` yordamı bir serpiştirmeyi alır ve o serpiştirmenin ürettiği
sayacı döndürür. Serpiştirme, hangi adımda hangi iş parçacığının ilerlediğini gösteren bir
0/1 dizisidir.

```python
def sayac_kosumu(desen: list[int]) -> int:
    """Iki is parcacigi birer kez oku-artir-yaz yapar. desen: hangi adimda kim ilerledi."""
    sayac, yerel = 0, {0: None, 1: None}
    asama = {0: 0, 1: 0}
    for kim in desen:
        a = asama[kim]
        if a == 0:
            yerel[kim] = sayac              # oku
        elif a == 1:
            yerel[kim] = yerel[kim] + 1     # artir
        else:
            sayac = yerel[kim]              # yaz
        asama[kim] = a + 1
    return sayac


def serpistirmeler(a: int, b: int) -> list[list[int]]:
    """a ve b adimlik iki dizinin butun serpistirmeleri."""
    if a == 0:
        return [[1] * b]
    if b == 0:
        return [[0] * a]
    sol = [[0] + s for s in serpistirmeler(a - 1, b)]
    sag = [[1] + s for s in serpistirmeler(a, b - 1)]
    return sol + sag


for ad, desen in (("sirali", [0, 0, 0, 1, 1, 1]),
                  ("tam serpisik", [0, 1, 0, 1, 0, 1]),
                  ("kismi", [0, 0, 1, 1, 1, 0])):
    print(f"{ad:13s} {desen} -> sayac {sayac_kosumu(desen)}")

hepsi = serpistirmeler(3, 3)
dogru = [d for d in hepsi if sayac_kosumu(d) == 2]
yanlis = len(hepsi) - len(dogru)
print()
print(f"serpistirme {len(hepsi)} | dogru {len(dogru)} | yanlis {yanlis} "
      f"| yanlis orani {yanlis / len(hepsi):.4f}")
print("dogru olanlar:", dogru)
```

```
sirali        [0, 0, 0, 1, 1, 1] -> sayac 2
tam serpisik  [0, 1, 0, 1, 0, 1] -> sayac 1
kismi         [0, 0, 1, 1, 1, 0] -> sayac 1

serpistirme 20 | dogru 2 | yanlis 18 | yanlis orani 0.9000
dogru olanlar: [[0, 0, 0, 1, 1, 1], [1, 1, 1, 0, 0, 0]]
```

Üç adımlık iki dizinin **20** serpiştirmesi var. Bunların yalnız **2**'si beklenen sonucu
üretiyor; **18**'i, yani yürütme sıralarının **yüzde doksanı**, sayacı 1'de bırakıyor.
Doğru sonucu veren iki serpiştirme de zaten aynı şeyi söylüyor: biri tümüyle bitmeden
diğeri başlamamış.

#### Kayıp Nerede Oluşuyor

`[0, 1, 0, 1, 0, 1]` serpiştirmesini adım adım izlemek yeter. Birinci iş parçacığı sayacı
okur ve **0** görür. İkinci iş parçacığı da okur ve yine **0** görür — birincinin yazması
henüz olmadı. İkisi de kendi yerel kopyasını 1 yapar. Birinci yazar, sayaç 1 olur; ikinci
yazar, sayaç yine 1 olur. İkinci yazma, birincinin sonucunu **üzerine yazdı**.

Buna **yarış koşulu (race condition)** denir: sonucun, işin kendisine değil adımların
sırasına bağlı olması. Adımların sırasını program yazan seçmez; çizelgeleyici seçer. Bu
nedenle yarış koşulu, kaynak metne bakarak görülemeyen bir hatadır — kaynak metinde
yalnızca **bir** artırma satırı vardır ve o satır doğrudur.

Sayacın bozulduğu bölge, **kritik bölge (critical section)** olarak adlandırılır: iki iş
parçacığının aynı anda içinde bulunmaması gereken adım dizisi. Burada kritik bölge üç
adımdır.

#### İki Ayrı Kusur

Yukarıdaki durum iki kusuru üst üste taşır ve ikisi karıştırılmamalıdır.

**Veri yarışı (data race)**, aynı bellek konumuna iki erişimin sıralanmamış olması ve en az
birinin yazma olmasıdır. Yapısal bir özelliktir: erişimlere bakarak, sonuca bakmadan
saptanır. **Yarış koşulu** ise sonucun adım sırasına bağlı olmasıdır; bir doğruluk
özelliğidir.

Sayaç örneğinde ikisi birden var. Ama biri diğerini gerektirmez. Bir kap her erişimde
kilitleniyorsa veri yarışı yoktur; buna karşın "boş mu diye bak, boş değilse bir eleman al"
dizisi iki ayrı kilitli işlemden oluşuyorsa, ikisinin arasında başka bir iş parçacığı son
elemanı alabilir ve sonuç yine sıraya bağlı kalır. Veri yarışı ortadan kalkmış, yarış
koşulu durmaktadır.

Bu ayrımın pratik sonucu şudur: **her erişimi ayrı ayrı korumak yetmez.** Korunması gereken
şey erişim değil, **değişmezin ihlal edildiği aralıktır** — yani kritik bölgenin sınırını
program yazan çizer, araç değil.

#### Dilim Serpiştirmeyi Kısıtlar

Önceki dersin çizelgeleyicisi iş parçacıklarını her adımda değiştirmez; **dilim** boyunca
tutar, dilim dolunca bırakır. O hâlde 20 serpiştirmenin hepsi erişilebilir değildir.

**EZ4.** Çizelgeleyici yalnız dilim sınırında geçiş yapar; dilim içinde önalım yoktur.
**EZ5.** Ölçülen şey bir olasılık değil, bir serpiştirmenin **erişilebilir olup
olmadığıdır**: o serpiştirme ya bu çizelgeleyiciyle üretilebilir ya da üretilemez.

Ortak tanımın `dilimli_serpistirmeler` yordamı, verilen dilimle üretilebilen serpiştirmeleri
sayar.

```python
def sayac_kosumu(desen: list[int]) -> int:
    sayac, yerel = 0, {0: None, 1: None}
    asama = {0: 0, 1: 0}
    for kim in desen:
        a = asama[kim]
        if a == 0:
            yerel[kim] = sayac
        elif a == 1:
            yerel[kim] = yerel[kim] + 1
        else:
            sayac = yerel[kim]
        asama[kim] = a + 1
    return sayac


def dilimli_serpistirmeler(adim: int, dilim: int) -> list[list[int]]:
    """Cizelgeleyici ancak `dilim` adimda bir gecis yapiyorsa erisilebilen serpistirmeler."""
    sonuc = []

    def gez(kalan_a, kalan_b, kim, dizi):
        if not kalan_a and not kalan_b:
            sonuc.append(list(dizi))
            return
        for yeni in (0, 1):
            kalan = kalan_a if yeni == 0 else kalan_b
            if not kalan:
                continue
            n = min(dilim, kalan) if yeni != kim else min(dilim, kalan)
            dizi.extend([yeni] * n)
            gez(kalan_a - n if yeni == 0 else kalan_a,
                kalan_b - n if yeni == 1 else kalan_b, yeni, dizi)
            del dizi[len(dizi) - n:]
    gez(adim, adim, None, [])
    ayri = []
    for d in sonuc:
        if d not in ayri:
            ayri.append(d)
    return ayri


print("dilim  erisilebilen  yanlis  yanlis orani")
for dilim in (1, 2, 3, 4, 5):
    erisilebilen = dilimli_serpistirmeler(3, dilim)
    yanlis = sum(1 for d in erisilebilen if sayac_kosumu(d) != 2)
    print(f"  {dilim:3d}  {len(erisilebilen):11d}  {yanlis:6d}  "
          f"{yanlis / len(erisilebilen):11.4f}")
print()
print("dilim 2 erisilebilenler:")
for d in dilimli_serpistirmeler(3, 2):
    print("  ", d, "-> sayac", sayac_kosumu(d))
print("dilim 3 erisilebilenler:")
for d in dilimli_serpistirmeler(3, 3):
    print("  ", d, "-> sayac", sayac_kosumu(d))
```

```
dilim  erisilebilen  yanlis  yanlis orani
    1           20      18       0.9000
    2            6       4       0.6667
    3            2       0       0.0000
    4            2       0       0.0000
    5            2       0       0.0000

dilim 2 erisilebilenler:
   [0, 0, 0, 1, 1, 1] -> sayac 2
   [0, 0, 1, 1, 0, 1] -> sayac 1
   [0, 0, 1, 1, 1, 0] -> sayac 1
   [1, 1, 0, 0, 0, 1] -> sayac 1
   [1, 1, 0, 0, 1, 0] -> sayac 1
   [1, 1, 1, 0, 0, 0] -> sayac 2
dilim 3 erisilebilenler:
   [0, 0, 0, 1, 1, 1] -> sayac 2
   [1, 1, 1, 0, 0, 0] -> sayac 2
```

Dilim 1'de çizelgeleyici her adımda geçiş yapabiliyor ve **20 serpiştirmenin tamamı**
erişilebilir; yanlış oran **0,9000**. Dilim 2'de erişilebilen serpiştirme **6**'ya iniyor,
yanlış **4**, oran **0,6667**. Dilim 3'te erişilebilen serpiştirme **2**'ye iniyor ve
**hiçbiri yanlış değil**.

#### Hata Yok Olmadı, Görünmez Oldu

Dilim 3'te kalan iki serpiştirme, `[0, 0, 0, 1, 1, 1]` ile `[1, 1, 1, 0, 0, 0]`. İkisi de
bir iş parçacığının üç adımını kesintisiz yürütüyor. Dilim, kritik bölgeden **büyük ya da
eşit** olduğu için hiçbir geçiş kritik bölgenin ortasına düşemiyor.

Bu sonucun okunma biçimi kursun ayırt edici noktasıdır. Program **değişmedi**: sayaç hâlâ
korumasız, artırma hâlâ üç adım, kritik bölge hâlâ açık. Değişen tek şey **çizelgeleyicinin
ayarı**. Dilim 3 ile bin kez koşulsa bin kez 2 yazacaktı; bu koşumlar programın doğru
olduğuna dair **hiçbir kanıt üretmez**, yalnız hatanın o ayarda erişilemez olduğunu
gösterir.

Ayar bir güvence değildir. Dilim 2'ye inildiğinde yanlış sonuç **anında geri gelir**: 6
serpiştirmenin 4'ü bozuktur. Kritik bölge üç adımdan dörde çıktığında dilim 3 de yetmez.
Ve bir sonraki dersin konusu olan çekirdek eklendiğinde dilim koruması tümüyle ortadan
kalkar — çünkü iki iş parçacığı gerçekten aynı anda ilerler.

Buradan çıkan kural şudur: **bir eşzamanlılık hatasının varlığı çıktıyla değil, erişilebilen
serpiştirme kümesiyle sınanır.** Çıktı bir örneklemdir; küme bir kanıttır.

#### Koşum Sayısı Kanıt Değildir

Dilim, geçişi yasaklamanın tek yolu değil; **seyrekleştirmek** de aynı sonucu üretir. Gerçek
bir kritik bölge birkaç adımdır, gerçek bir dilim ise binlerce adım; geçişin tam o birkaç
adıma denk gelme olasılığı küçüktür. Küçük olması yok olması değildir, ama **gözlemi yok
eder**.

**EZ6.** Geçiş olasılığı adım başınadır ve koşum boyunca sabittir; örneklem, erişilebilen
kümeyi değil o kümenin ziyaret sıklığını değiştirir.

Aşağıdaki ölçü bunu sayar. Çizelgeleyici her adımda belirli bir olasılıkla öbür iş
parçacığına geçiyor; iki bin koşum yapılıyor ve yanlış sonuç sayılıyor. Üreteç ortak
tanımın üreteci, tohum aynı tohumdur; sonuç belirlenimcidir.

```python
TOHUM = 20260218


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def sayac_kosumu(desen: list[int]) -> int:
    sayac, yerel = 0, {0: None, 1: None}
    asama = {0: 0, 1: 0}
    for kim in desen:
        a = asama[kim]
        if a == 0:
            yerel[kim] = sayac
        elif a == 1:
            yerel[kim] = yerel[kim] + 1
        else:
            sayac = yerel[kim]
        asama[kim] = a + 1
    return sayac


def ornek_kosum(r, onbinde: int) -> list[int]:
    """Cizelgeleyici her adimda `onbinde` olasilikla obur is parcacigina gecer."""
    kalan, kim, desen = {0: 3, 1: 3}, 0, []
    while kalan[0] or kalan[1]:
        if not kalan[kim] or (kalan[1 - kim] and r(10000) < onbinde):
            kim = 1 - kim
        desen.append(kim)
        kalan[kim] -= 1
    return desen


print("gecis olasiligi  kosum  yanlis  gozlenen yanlis orani")
for onbinde in (5000, 1000, 100, 10, 1):
    r = uretec(TOHUM)
    yanlis = sum(1 for _ in range(2000) if sayac_kosumu(ornek_kosum(r, onbinde)) != 2)
    print(f"  onbinde {onbinde:6d}   2000  {yanlis:6d}  {yanlis / 2000:21.4f}")
```

```
gecis olasiligi  kosum  yanlis  gozlenen yanlis orani
  onbinde   5000   2000    1519                 0.7595
  onbinde   1000   2000     346                 0.1730
  onbinde    100   2000      40                 0.0200
  onbinde     10   2000       1                 0.0005
  onbinde      1   2000       0                 0.0000
```

Son satır dersin cümlesidir: **iki bin koşumun iki bininde sayaç 2 çıkıyor ve program
yanlış.** Aradaki satırlar da aynı derecede öğreticidir — on binde on olasılıkta iki bin
koşumda yalnız **1** yanlış görülüyor. Bu tek gözlemi yakalayan bir sınama, tekrar
çalıştırıldığında büyük olasılıkla temiz sonuç verecek ve hata "düzeldi" sayılacaktır.

Erişilebilen serpiştirme kümesi bu beş satırın hiçbirinde değişmedi: **20 serpiştirme,
18'i yanlış**. Değişen yalnız örneklemin bu kümeyi ne sıklıkla ziyaret ettiğidir. Koşum
sayısını artırmak bir çözüm değildir; on binde bir olasılıkta hatayı **beklenen değerle**
görmek için yüz binlerce koşum gerekir ve o sayı da kanıt üretmez, yalnız gözlem üretir.

#### Üç Sayı

| Ölçüt | Soyutlamasız taban | Soyutlamalı kurulum | Bedel |
|---|---|---|---|
| Yürütme sırası | tek iş parçacığı, **1** sıra | iki iş parçacığı, **20** serpiştirme | 19 ek sıra |
| Yanlış sonuç | **0** | **18** | 18 bozuk yürütme |
| Yanlış oranı | 0,0000 | 0,9000 | — |

Dördüncü bir satır, dilim ayarının ne yaptığını gösterir: dilim 3'te erişilebilen
serpiştirme **2**, yanlış **0**, oran **0,0000**. Bu satır tabloya **düzeltme** olarak
değil, **görünürlük** ölçüsü olarak girer. Hatanın kendisi hâlâ 18 serpiştirmede duruyor;
yalnız o serpiştirmelere bu ayarla ulaşılamıyor.

#### Doğru Çözüm Ayar Değil, Dışlama

Yarış koşulunu ortadan kaldıran şey çizelgeleyicinin nasıl ayarlandığı olamaz, çünkü
çizelgeleyici programın denetimi altında değildir. Ortadan kaldıran şey **karşılıklı
dışlamadır (mutual exclusion)**: kritik bölgenin, herhangi bir anda en çok bir iş
parçacığını içine alacak biçimde korunması.

Ortak tanımın `sayac_kosumu` yordamı bunu bir seçenekle taşır: `kilit=True` verildiğinde
kritik bölge bölünemez sayılır ve yordam serpiştirmeden bağımsız olarak **2** döndürür.
Yirmi serpiştirmenin yirmisi de doğru olur. Kazanılan şey, erişilebilen serpiştirme
kümesini daraltmak değil, **kümedeki her serpiştirmeyi doğru kılmaktır**.

Bunun bir bedeli vardır ve o bedel sonraki dersin ölçüsüdür: kritik bölge sıraya girdiğinde
iş parçacıkları birbirini bekler, bekleme adımı birikir ve işlemci kullanımı düşer.

#### Özet

- Paylaşılan bir sayacın artırılması oku-artır-yaz olmak üzere üç adımdır; adımların arası
  bölünebilir ve bölündüğünde bir yazma diğerinin üzerine yazar.
- İki iş parçacığının üçer adımının **20** serpiştirmesi vardır; **18**'i yanlış sonuç
  verir, yanlış oranı **0,9000**.
- Çizelgeleyici dilimi erişilebilen serpiştirme kümesini daraltır: dilim 1'de 20, dilim
  2'de 6, dilim 3'te 2 serpiştirme erişilebilir.
- Dilim kritik bölgeden büyük ya da eşitken erişilebilen serpiştirmelerin **hiçbiri yanlış
  değildir**; program değişmemiştir, yalnız hata erişilemez olmuştur.
- Bir eşzamanlılık hatası çıktıyla sınanamaz; sınama, erişilebilen serpiştirme kümesi
  üzerinden yapılır.

#### Sonraki Adım

Bu ders sorunu kurdu ve çözümün adını koydu: karşılıklı dışlama. Sonraki ders çözümü
kurar ve **bedelini sayar**. Kritik bölgesi 10, kritik olmayan işi 10 adım olan iş
parçacıkları dışlama kilidi altında koşturulacak; tek iş parçacığının 20 zaman birimi,
ikincisiyle 29, dördüncüsüyle 47, sekizincisiyle 83 olduğu ve bekleme adımının sıfırdan
252'ye çıktığı gösterilecek. Sayan semaforun bu sayıyı nasıl düşürdüğü ve düşürürken neyi
gevşettiği de aynı tabloda durur.

### Kilitler, Dışlama Kilitleri ve Sayan Semaforlar

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/kilitler-ve-karsilikli-dislama

Karşılıklı dışlamanın kritik bölgeyi nasıl güvence altına aldığı, çekişmenin süre ve bekleme adımı olarak bedeli, sayan semaforun neyi kazandırıp neyi gevşettiği.

Önceki ders yarış koşulunu kurdu ve çözümün adını koydu: karşılıklı dışlama. Adı koymak
bedava, uygulamak değil. Bu ders önce dışlamanın gerçekten işe yaradığını gösterir, sonra
**ne kadar tuttuğunu** sayar.

Ölçünün eksenini baştan söylemek gerekir. Kilit, bir programı hızlandırmaz; hiçbir işi
ortadan kaldırmaz, hiçbir adımı kısaltmaz. Yaptığı tek şey, bazı iş parçacıklarını
**bekletmektir**. O hâlde bedeli de tek bir birimde ölçülür: **bekleme adımı**.

#### Kilit Neyi Güvence Altına Alıyor

**Kilit (lock)**, bir kritik bölgeye giriş hakkını taşıyan nesnedir. Aynı anda tek bir iş
parçacığına verilebilen kilide **dışlama kilidi (mutex)** denir; adı doğrudan sağladığı
güvenceden gelir.

Ortak tanımın `sayac_kosumu` yordamı bu güvenceyi bir seçenekle taşır: `kilit=True`
verildiğinde kritik bölge bölünemez sayılır. Aşağıdaki ölçü, önceki dersin 20
serpiştirmesini iki kez süpürüyor.

```python
def sayac_kosumu(desen: list[int], kilit: bool = False) -> int:
    """kilit=True ise kritik bolge bolunemez sayilir."""
    if kilit:
        return 2
    sayac, yerel = 0, {0: None, 1: None}
    asama = {0: 0, 1: 0}
    for kim in desen:
        a = asama[kim]
        if a == 0:
            yerel[kim] = sayac
        elif a == 1:
            yerel[kim] = yerel[kim] + 1
        else:
            sayac = yerel[kim]
        asama[kim] = a + 1
    return sayac


def serpistirmeler(a: int, b: int) -> list[list[int]]:
    if a == 0:
        return [[1] * b]
    if b == 0:
        return [[0] * a]
    return ([[0] + s for s in serpistirmeler(a - 1, b)]
            + [[1] + s for s in serpistirmeler(a, b - 1)])


hepsi = serpistirmeler(3, 3)
for kilit in (False, True):
    sonuc = [sayac_kosumu(d, kilit) for d in hepsi]
    yanlis = sum(1 for s in sonuc if s != 2)
    print(f"kilit {str(kilit):5s} | serpistirme {len(hepsi):2d} | ayri sonuc {sorted(set(sonuc))}"
          f" | yanlis {yanlis:2d} | yanlis orani {yanlis / len(hepsi):.4f}")
```

```
kilit False | serpistirme 20 | ayri sonuc [1, 2] | yanlis 18 | yanlis orani 0.9000
kilit True  | serpistirme 20 | ayri sonuc [2] | yanlis  0 | yanlis orani 0.0000
```

Fark, önceki dersteki dilim ayarının yaptığından **türce** başkadır. Dilim, erişilebilen
serpiştirme kümesini 20'den 2'ye daraltıyordu; kilit kümeyi daraltmıyor, **kümedeki
yirmi serpiştirmenin yirmisini de doğru kılıyor**. Ayrı sonuç kümesi `[1, 2]` iken `[2]`
oluyor. Doğruluk artık çizelgeleyicinin ayarına bağlı değil.

#### Çekişmeyi Ölçmek

Kazanç sayıldı; şimdi bedel. Ortak tanımın `kilitli_kosum` yordamı, her biri bir kritik
bölge ve bir kritik olmayan iş taşıyan iş parçacıklarını koşturur ve üç sayı döndürür:
süre, bekleme adımı, işlemci kullanımı.

**EZ7.** Kilit edinme ve bırakma bedava sayılır; ölçülen yalnız bekleme adımıdır.
**EZ8.** Her iş parçacığı **10 adım** kritik bölge ve **10 adım** kritik olmayan iş taşır.
**EZ9.** Kritik olmayan iş tümüyle paraleldir; çekirdek sayısı burada sınırlayıcı değildir.
**EZ10.** Bekleyen iş parçacığı çekirdeği tutmaz; bekleme adımı sayılır ama meşgul sayılmaz.
**EZ11.** Kilit sırası adildir; hiçbir iş parçacığı süresiz dışarıda kalmaz.

Aynı kritik bölgeye giren iş parçacığı sayısının artmasına **çekişme (contention)** denir.
Süpürme çekişmeyi 1'den 16'ya çıkarıyor.

```python
def kilitli_kosum(is_parcacigi: int, kritik: int, kritik_disi: int, izin: int = 1) -> dict:
    """Her is parcacigi `kritik` uzunlukta bir kritik bolge ile `kritik_disi` adim
    kritik olmayan is yapar. izin=1 dislama kilidi , izin>1 sayan semafordur."""
    kalan = [{"kritik": kritik, "disi": kritik_disi} for _ in range(is_parcacigi)]
    icerde, t, bekleme, mesgul = [], 0, 0, 0
    while any(k["kritik"] or k["disi"] for k in kalan):
        calisan = 0
        for j, k in enumerate(kalan):
            if k["disi"]:                             # kritik olmayan is paraleldir
                k["disi"] -= 1
                calisan += 1
                continue
            if not k["kritik"]:
                continue
            if j in icerde or len(icerde) < izin:
                if j not in icerde:
                    icerde.append(j)
                k["kritik"] -= 1
                calisan += 1
                if not k["kritik"]:
                    icerde.remove(j)
            else:
                bekleme += 1
        mesgul += calisan
        t += 1
    return {"sure": t, "bekleme": bekleme, "mesgul": mesgul,
            "kullanim": round(mesgul / (t * is_parcacigi), 4)}


print("dislama kilidi (izin 1), kritik 10 , kritik disi 10")
print("  is parcacigi  sure  bekleme  kullanim  bekleme/(n*(n-1))")
for n in (1, 2, 3, 4, 6, 8, 12, 16):
    k = kilitli_kosum(n, 10, 10, 1)
    oran = "-" if n == 1 else f"{k['bekleme'] / (n * (n - 1)):.1f}"
    print(f"  {n:12d}  {k['sure']:4d}  {k['bekleme']:7d}  {k['kullanim']:8.4f}  {oran:>17s}")
```

```
dislama kilidi (izin 1), kritik 10 , kritik disi 10
  is parcacigi  sure  bekleme  kullanim  bekleme/(n*(n-1))
             1    20        0    1.0000                  -
             2    29        9    0.6897                4.5
             3    38       27    0.5263                4.5
             4    47       54    0.4255                4.5
             6    65      135    0.3077                4.5
             8    83      252    0.2410                4.5
            12   119      594    0.1681                4.5
            16   155     1080    0.1290                4.5
```

Tek iş parçacığı işini **20 zaman biriminde** bitiriyor, bekleme adımı **0**, kullanım
**1,0000**. İkincisi eklendiğinde süre **29** oluyor. Dördüncüde **47**, sekizincide
**83**, on altıncıda **155**.

Bu, kursun ikinci iddiasının en yalın hâlidir: **ikinci iş parçacığı işi yavaşlatır.** İki
iş parçacığı toplamda 40 adımlık iş taşıyor ve tek iş parçacığının 20 adımını 29 zaman
biriminde bitiriyor; kritik bölgeler sıraya girdiği için hiçbir hızlanma yok, yalnız 9
zaman birimlik gecikme var.

#### Süre Doğrusal, Bekleme İkinci Dereceden

İki sütunun büyüme hızı aynı değildir ve fark ölçülebilir.

Süre sütunu doğrusaldır: her iş parçacığı **9 zaman birimi** ekliyor. Kritik bölgeler
sıraya girdiği için toplam süre, sıralanan kritik bölgelerin toplamına yaklaşıyor.

Bekleme sütunu ikinci derecedendir. Son sütun bunu doğrudan gösteriyor: bekleme adımının
$n(n-1)$'e oranı bütün satırlarda **4,5**'te sabit. Yani bekleme adımı yaklaşık
$4{,}5 \cdot n^2$ ile büyüyor. İş parçacığı sayısını ikiye katlamak süreyi ikiye
katlamıyor ama beklemeyi **yaklaşık dörde** katlıyor: 8 iş parçacığında **252**, 16'da
**1080** — oran tam dört değil `1080/252 = 4,29`'dur ve $n$ büyüdükçe dörde yaklaşır.

Bunun sonucu, işlemci kullanımı sütununda okunuyor: **1,0000**'den **0,1290**'a düşüyor.
On altı iş parçacığının on altı katlık gücü, kritik bölge yüzünden aslında kullanılamıyor.

Buradan çıkan tasarım kuralı sayıya dayanır: **çekişmeyi düşürmenin yolu daha hızlı bir
kilit değil, daha kısa bir kritik bölgedir.** Bu iddia bedelsiz bırakılamaz; sayılması
gerekir.

#### Kritik Bölgeyi Kısaltmanın Ölçüsü

Bir sonraki süpürme iş parçacığı sayısını **8**'de sabitliyor ve kritik bölgenin toplam iş
içindeki payını değiştiriyor.

**EZ12.** İş parçacığı başına toplam iş **20 adımda** sabittir; değişen yalnız bu 20 adımın
kaçının kritik bölgede olduğudur.

```python
def kilitli_kosum(is_parcacigi: int, kritik: int, kritik_disi: int, izin: int = 1) -> dict:
    kalan = [{"kritik": kritik, "disi": kritik_disi} for _ in range(is_parcacigi)]
    icerde, t, bekleme, mesgul = [], 0, 0, 0
    while any(k["kritik"] or k["disi"] for k in kalan):
        calisan = 0
        for j, k in enumerate(kalan):
            if k["disi"]:
                k["disi"] -= 1
                calisan += 1
                continue
            if not k["kritik"]:
                continue
            if j in icerde or len(icerde) < izin:
                if j not in icerde:
                    icerde.append(j)
                k["kritik"] -= 1
                calisan += 1
                if not k["kritik"]:
                    icerde.remove(j)
            else:
                bekleme += 1
        mesgul += calisan
        t += 1
    return {"sure": t, "bekleme": bekleme, "mesgul": mesgul,
            "kullanim": round(mesgul / (t * is_parcacigi), 4)}


TOPLAM = 20
print("8 is parcacigi , is parcacigi basina toplam 20 adim , dislama kilidi")
print("  kritik  kritik disi  kritik pay  sure  bekleme  kullanim")
for kritik in (1, 2, 5, 10, 15, 20):
    k = kilitli_kosum(8, kritik, TOPLAM - kritik, 1)
    print(f"  {kritik:6d}  {TOPLAM - kritik:11d}  {kritik / TOPLAM:10.2f}"
          f"  {k['sure']:4d}  {k['bekleme']:7d}  {k['kullanim']:8.4f}")
```

```
8 is parcacigi , is parcacigi basina toplam 20 adim , dislama kilidi
  kritik  kritik disi  kritik pay  sure  bekleme  kullanim
       1           19        0.05    20        0    1.0000
       2           18        0.10    27       28    0.7407
       5           15        0.25    48      112    0.4167
      10           10        0.50    83      252    0.2410
      15            5        0.75   118      392    0.1695
      20            0        1.00   153      532    0.1307
```

Yapılan iş her satırda aynı: sekiz iş parçacığı, her biri 20 adım. Değişen tek şey bu
adımların kaçının sıraya girmek zorunda olduğu.

Kritik pay 0,50 iken süre **83**; pay 0,25'e inince **48**. Kritik bölgeyi yarıya indirmek
süreyi neredeyse yarıya indiriyor. Pay 0,05'e inince süre **20** oluyor — yani tek iş
parçacığının süresi. Sekiz iş parçacığı, sekiz katlık gücünü ancak kritik bölge tek adıma
indiğinde kullanabiliyor.

Ters yön de aynı ölçüde nettir. Payı 0,50'den 0,75'e çıkarmak süreyi 83'ten **118**'e,
1,00'e çıkarmak **153**'e taşıyor. Bekleme adımı 252'den **532**'ye çıkıyor. Bütünüyle
kritik bölgeden oluşan bir işte eşzamanlılık hiçbir şey kazandırmaz; yalnız bekleme üretir.

Tablonun ilk satırı sonraki derslerin de dayanağıdır: kritik bölgesi **tek adım** olan bir
iş, çekişme üretmeyen iştir. Donanımın böyle bir adımı bölünmez biçimde yürütmesine
**atomik işlem (atomic operation)** denir ve bedeli bu tabloda **sıfırdır**.

#### Sayan Semafor

**Sayan semafor (semaphore)**, kritik bölgeye aynı anda birden çok iş parçacığı alabilen
araçtır. Taşıdığı sayı, verilebilecek **izin** sayısıdır.

**EZ13.** İzin sayısı sabittir ve koşum boyunca değişmez.

```python
def kilitli_kosum(is_parcacigi: int, kritik: int, kritik_disi: int, izin: int = 1) -> dict:
    """Her is parcacigi `kritik` uzunlukta bir kritik bolge ile `kritik_disi` adim
    kritik olmayan is yapar. izin=1 dislama kilidi , izin>1 sayan semafordur."""
    kalan = [{"kritik": kritik, "disi": kritik_disi} for _ in range(is_parcacigi)]
    icerde, t, bekleme, mesgul = [], 0, 0, 0
    while any(k["kritik"] or k["disi"] for k in kalan):
        calisan = 0
        for j, k in enumerate(kalan):
            if k["disi"]:
                k["disi"] -= 1
                calisan += 1
                continue
            if not k["kritik"]:
                continue
            if j in icerde or len(icerde) < izin:
                if j not in icerde:
                    icerde.append(j)
                k["kritik"] -= 1
                calisan += 1
                if not k["kritik"]:
                    icerde.remove(j)
            else:
                bekleme += 1
        mesgul += calisan
        t += 1
    return {"sure": t, "bekleme": bekleme, "mesgul": mesgul,
            "kullanim": round(mesgul / (t * is_parcacigi), 4)}


print("sayan semafor: izin sayisi degisiyor")
print("  is parcacigi  izin  sure  bekleme  kullanim")
for n in (2, 4, 8, 16):
    for izin in (1, 2, 4):
        k = kilitli_kosum(n, 10, 10, izin)
        print(f"  {n:12d}  {izin:4d}  {k['sure']:4d}  {k['bekleme']:7d}  {k['kullanim']:8.4f}")
```

```
sayan semafor: izin sayisi degisiyor
  is parcacigi  izin  sure  bekleme  kullanim
             2     1    29        9    0.6897
             2     2    20        0    1.0000
             2     4    20        0    1.0000
             4     1    47       54    0.4255
             4     2    29       18    0.6897
             4     4    20        0    1.0000
             8     1    83      252    0.2410
             8     2    47      108    0.4255
             8     4    29       36    0.6897
            16     1   155     1080    0.1290
            16     2    83      504    0.2410
            16     4    47      216    0.4255
```

İki izinli semafor, iki iş parçacığının süresini **29'dan 20'ye** indiriyor ve beklemeyi
**9'dan 0'a**. Tablonun tamamında düzenli bir bağıntı var: **izin sayısı çekişmeyi böler.**
Sekiz iş parçacığı iki izinle **47**'de bitiyor, yani dört iş parçacığının bir izinli
süresinde. On altı iş parçacığı dört izinle yine **47**.

Bekleme adımı aynı biçimde bölünmüyor. Dört iş parçacığı bir izinle **54** bekleme adımı
üretirken, sekiz iş parçacığı iki izinle **108**, on altı iş parçacığı dört izinle **216**
üretiyor. Süre eşit, bekleme dört katı: **aynı süreyi daha çok iş parçacığıyla elde etmek
bedavaya gelmiyor.**

#### Semafor Karşılıklı Dışlama Sağlamaz

Tablodaki kazanç, kolay bir yanlış çıkarıma davet eder: izin sayısını artırınca hem
korunuyoruz hem hızlanıyoruz. Bu yanlıştır.

İki izinli semafor, tanımı gereği **iki iş parçacığını aynı anda kritik bölgeye alır.**
Önceki dersin sayacı bu koşulda korunmaz: iki iş parçacığı içerideyken oku-artır-yaz
üçlemeleri yeniden serpişir ve **20 serpiştirmenin 18'i** yeniden yanlış sonuç verir. Bu
dersin ilk ölçüsünün `kilit=False` satırına geri dönülmüş olur.

O hâlde iki aracın işi başkadır. **Dışlama kilidi bir doğruluk aracıdır**; kritik bölgeyi
korur. **Sayan semafor bir kaynak aracıdır**; aynı anda kaç iş parçacığının bir kaynak
havuzunu kullanabileceğini sınırlar. Havuzda dört bağlantı varsa dört izinli semafor
doğrudur; paylaşılan bir sayacın önünde iki izinli semafor **hatadır**.

Ortak tanımın dördüncü okuması bunu tek cümlede toplar: iki izinli semafor 29'u 20'ye
indirir, ama bu **karşılıklı dışlamayı gevşetmektir** ve bedava değildir.

#### Bekleme Nasıl Beklenir

Bu ölçüde bekleyen iş parçacığı çekirdeği tutmuyor (EZ10). Beklemenin ikinci bir biçimi
daha vardır: **meşgul bekleme (busy waiting)**, yani kilidin boşalıp boşalmadığını sürekli
yoklamak.

Ortak tanımın üçüncü okuması bu seçeneği tekrar ölçmeye gerek bırakmaz: yoklamalı model
işlemci kullanımını **1,0000** yapar ve süreyi **hiç değiştirmez**. Yukarıdaki tabloya
uygulandığında 16 iş parçacığının kullanımı 0,1290 yerine 1,0000 görünürdü ve süre yine
**155** olurdu. Kullanım burada bir vekil ölçüttür ve yanıltır.

#### Üç Sayı

| Ölçüt | Soyutlamasız taban | Soyutlamalı kurulum | Bedel |
|---|---|---|---|
| Süre | tek iş parçacığı, **20** | sekiz iş parçacığı, dışlama kilidi, **83** | 63 zaman birimi |
| Bekleme adımı | **0** | **252** | 252 adım |
| İşlemci kullanımı | **1,0000** | **0,2410** | 0,7590 düşüş |
| Yanlış serpiştirme | 18 (korumasız) | **0** | doğruluk kazanıldı |

Son satır, ilk üç satırın neden ödendiğini söyler. Bedel yüksek görünüyorsa karşılığı da
yüksektir: koruma olmadan sekiz iş parçacığı 20 zaman biriminde biterdi ve sonucun doğru
olacağına dair hiçbir güvence bulunmazdı.

#### Özet

- Dışlama kilidi, erişilebilen serpiştirme kümesini daraltmaz; kümedeki her serpiştirmeyi
  doğru kılar — ayrı sonuç kümesi `[1, 2]`'den `[2]`'ye iner.
- Çekişme arttıkça süre doğrusal büyür: her iş parçacığı 9 zaman birimi ekler; 1'den 16'ya
  giderken süre 20'den 155'e çıkar.
- Bekleme adımı ikinci dereceden büyür; $n(n-1)$'e oranı bütün satırlarda 4,5'tir. İş
  parçacığı sayısını ikiye katlamak beklemeyi yaklaşık dörde katlar.
- Sayan semafor çekişmeyi izin sayısına böler ve süreyi düşürür, ama karşılıklı dışlamayı
  gevşetir; paylaşılan bir sayacın önünde bir doğruluk aracı değildir.
- Çekişmeyi düşürmenin yolu daha hızlı bir kilit değil, daha kısa bir kritik bölgedir:
  sekiz iş parçacığında kritik pay 0,50'den 0,25'e inince süre 83'ten 48'e, 0,05'e inince
  20'ye düşer.

#### Sonraki Adım

Bu ders tek bir kilidin bedelini ölçtü. Gerçek programlar tek kilit kullanmaz: her paylaşılan
yapının kendi kilidi olur ve bir iş parçacığı ikisini birden isteyebilir. Sonraki ders bu
durumun iki yeni arızasını ele alır — bir iş parçacığı grubunun birbirini süresiz beklediği
**kilitlenme** ve bir iş parçacığının sırasının hiç gelmediği **açlık**. Bekleme ilişkisi bir
çizgeye dönüştürülecek, Veri Yapıları kursunun döngü tespiti aynen çağrılacak ve sıralı
edinimin kilitlenmeyi yapısal olarak ortadan kaldırdığı 648 durumun taranmasıyla
gösterilecek.

### Kilitlenme ve Açlık

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/kilitlenme-ve-aclik

Kilitlenmenin dört koşulu, bekleme çizgesinde döngü tespitiyle saptanması, sıralı edinimin yapısal önlemesi ve bunun bekleme adımı olarak bedeli.

Önceki ders tek bir kilidin bedelini ölçtü ve sonucu tek bir eksende topladı: bekleme
adımı. O eksende kötü bir sayı, yavaş bir programdır. Bu derste ikinci bir arıza türü
çıkıyor ve o eksende **hiç** sayı üretmiyor: kilitlenen bir iş parçacığı yavaşlamaz,
**durur**.

İki kilit yetiyor. Bir iş parçacığı birinci kilidi tutup ikincisini isterken, başka bir iş
parçacığı ikinciyi tutup birinciyi isterse ikisi de bir daha ilerlemez. Bu derste bu durumun
ne zaman oluştuğu **sayılacak**, önlemenin yapısal biçimi kurulacak ve önlemenin bedeli
ölçülecek.

#### Dört Koşul

**Kilitlenme (deadlock)** rastgele oluşmaz. Dört koşul **aynı anda** sağlandığında oluşur ve
herhangi birinin kırılması onu imkânsız kılar.

**Karşılıklı dışlama.** Kaynak paylaşılamıyor; bir anda tek iş parçacığı tutabiliyor. Bu
koşul kaldırılırsa önceki dersin doğruluk güvencesi de gider.

**Tut ve bekle.** Bir iş parçacığı elindekini bırakmadan yenisini istiyor.

**Önalımsızlık.** Kilit zorla geri alınamıyor; ancak tutan bırakabiliyor.

**Dairesel bekleme.** Bekleme ilişkisi bir halka oluşturuyor.

**EZ14.** Bekleme çizgesinin düğümleri iş parçacıkları, kenarları "tutulan bir kilidi
bekleme" ilişkisidir.
**EZ15.** Bir kilidi aynı anda tek iş parçacığı tutar; istenen kilit boştaysa kenar yoktur.
**EZ16.** Her iş parçacığı bir kilit tutar ve başka bir kilit ister; kilit bırakma
zorlanamaz, tespit sonrası kurtarma modellenmez.

İlk üç koşul çoğu programda tasarım gereği vardır. Kalan tek hedef **dairesel beklemedir**
ve bu, çizge diliyle tam olarak **döngüdür**.

#### Döngü Tespiti Yeni Bir Yordam Değildir

Veri Yapıları kursunun derine arama dersi yönlü çizgede döngü tespitini kurdu ve oradaki
ayrım burada aynen geçerlidir: "ziyaret edildi" ile "hâlâ açık" karıştırılmamalıdır. Ortak
tanımın `dongu_var` yordamı bunu üç renkle taşır — 0 hiç görülmedi, 1 hâlâ açık, 2 dalı
bitti. Yordam **tekrar kurulmaz**, doğrudan çağrılır.

Aşağıdaki ölçü önce beş bekleme çizgesini sınıyor, sonra bir durum uzayını tarıyor.

```python
from itertools import permutations, product


def dongu_var(bekleme: dict[str, list[str]]) -> bool:
    """Yonlu cizgede dongu sinamasi. renk 1 = hala acik , renk 2 = dal bitti."""
    renk = {d: 0 for d in bekleme}

    def gez(d):
        renk[d] = 1
        for h in bekleme.get(d, []):
            if renk.get(h, 0) == 1:
                return True
            if renk.get(h, 0) == 0 and gez(h):
                return True
        renk[d] = 2
        return False
    return any(renk[d] == 0 and gez(d) for d in list(bekleme))


for ad, cizge in (("kimse beklemiyor", {"T1": [], "T2": []}),
                  ("zincir T1 -> T2", {"T1": ["T2"], "T2": []}),
                  ("iki dal ortak hedef", {"T1": ["T3"], "T2": ["T3"], "T3": []}),
                  ("halka T1 -> T2 -> T1", {"T1": ["T2"], "T2": ["T1"]}),
                  ("halka T1 -> T2 -> T3", {"T1": ["T2"], "T2": ["T3"], "T3": ["T1"]})):
    print(f"  {ad:22s} kilitlenme {dongu_var(cizge)}")


def tara(kilit_sayisi: int, is_parcacigi: int, sirali: bool) -> tuple[int, int]:
    """Her is parcacigi bir kilidi tutar , baska bir kilidi ister. Butun durumlar."""
    kilitler = list(range(1, kilit_sayisi + 1))
    durum = kilitlenen = 0
    for tutulan in permutations(kilitler, is_parcacigi):
        for istenen in product(kilitler, repeat=is_parcacigi):
            if any(t == i for t, i in zip(tutulan, istenen)):
                continue                        # kendi tuttugu kilidi istemez
            if sirali and any(i < t for t, i in zip(tutulan, istenen)):
                continue                        # sirali edinim: yalniz buyuk numara istenir
            sahip = {k: f"T{j+1}" for j, k in enumerate(tutulan)}
            cizge = {f"T{j+1}": [] for j in range(is_parcacigi)}
            for j, i in enumerate(istenen):
                if i in sahip:                  # istenen kilit bostaysa kenar yoktur
                    cizge[f"T{j+1}"].append(sahip[i])
            durum += 1
            if dongu_var(cizge):
                kilitlenen += 1
    return durum, kilitlenen


print()
print("4 kilit , her is parcacigi bir kilit tutup baska bir kilit istiyor")
print("  is parcacigi  edinim     durum  kilitlenen  kilitlenme orani")
for n in (2, 3, 4):
    for sirali in (False, True):
        d, k = tara(4, n, sirali)
        ad = "sirali " if sirali else "serbest"
        oran = f"{k / d:.4f}" if d else "durum yok"
        print(f"  {n:12d}  {ad}  {d:8d}  {k:10d}  {oran:>16s}")
```

```
  kimse beklemiyor       kilitlenme False
  zincir T1 -> T2        kilitlenme False
  iki dal ortak hedef    kilitlenme False
  halka T1 -> T2 -> T1   kilitlenme True
  halka T1 -> T2 -> T3   kilitlenme True

4 kilit , her is parcacigi bir kilit tutup baska bir kilit istiyor
  is parcacigi  edinim     durum  kilitlenen  kilitlenme orani
             2  serbest       108          12            0.1111
             2  sirali         22           0            0.0000
             3  serbest       648         264            0.4074
             3  sirali         36           0            0.0000
             4  serbest      1944        1944            1.0000
             4  sirali          0           0         durum yok
```

Üçüncü satır ölçünün özüdür: dört kilit ve üç iş parçacığıyla **648** ayrı bekleme durumu
kurulabiliyor ve bunların **264**'ü kilitli. Oran **0,4074**. İkinci dal önemlidir: iki iş
parçacığında oran **0,1111** iken üçte **0,4074**, dörtte **1,0000** oluyor. Kilitlenme,
iş parçacığı eklendikçe **istisna olmaktan çıkıp kural hâline geliyor**.

Dört iş parçacığının dört kilidi tuttuğu satırdaki 1,0000 rastlantı değil. Her iş parçacığı
bir kilit tutup başkasını istediğinde, "kimin kilidini bekliyorum" ilişkisi düğüm başına tek
çıkışlı bir çizge kurar; sonlu ve çıkışsız düğüm içermeyen böyle bir çizge **her zaman** bir
halka barındırır.

#### Tespit ile Önleme Aynı Şey Değildir

Yukarıdaki tarama bir **tespit** yordamıdır: durum verildiğinde kilitlenmiş olup olmadığını
söyler. Çalışan bir sistemde bunu kullanmak üç şey ister. Bekleme çizgesinin **sürekli
güncel tutulması**, çizgenin **düzenli aralıklarla taranması** ve döngü bulunduğunda bir iş
parçacığının **geri alınması**. Tarama maliyeti Veri Yapıları kursunda ölçüldü ve
$O(V + E)$'dir; burada tekrarlanmaz.

Üçüncü adım en pahalısıdır. Geri almak, tutulan kilidi zorla söküp o iş parçacığının yaptığı
işi iptal etmek demektir; yani **önalımsızlık koşulunun kırılmasıdır** ve iptal edilen iş
yeniden yapılır. Bu ders o kurtarmayı modellemez (EZ16).

Önleme ise koşum sırasında **hiçbir şey ölçmez ve hiçbir şey taramaz**. Bedelini tasarım
anında öder: kilitlere bir sıra verilir ve bu sıraya her yerde uyulur.

#### Sıralı Edinim

Tabloda `sirali` satırlarının hepsinde kilitlenen **0**. Bu bir iyileştirme değil, **yapısal
bir imkânsızlıktır** ve gerekçesi tek cümleyle verilebilir.

**EZ17.** Kilitler sabit bir tam sıraya konur; her iş parçacığı kilitleri **yalnız artan
numara sırasında** ister.

Bir halka olduğunu varsayalım. Halkadaki her iş parçacığı, tuttuğundan **büyük** numaralı
bir kilidi bekliyor demektir. Halka boyunca yürünürse numaralar sürekli artar; ama halka
başladığı yere döner ve bir sayı kendinden büyük olamaz. Çelişki. Yani halka yoktur.

Tablo bunu iki ayrı biçimde gösteriyor. Üç iş parçacığında durum uzayı **648**'den
**36**'ya iniyor ve kalan 36 durumun **hiçbiri** kilitli değil. Dört iş parçacığında sıralı
edinim altında **hiç durum kalmıyor** — o durum kurulamıyor bile.

Buradaki 648'den 36'ya iniş, önlemenin bedelinin nerede olduğunu da söylüyor: **ortadan
kaldırılan durumların çoğu kilitli değildi.** Sıralı edinim yalnız halkaları değil, birçok
zararsız bekleme biçimini de yasaklar.

#### Önlemenin Bedeli

Yasaklanan zararsız durumlar bir yerde ödenir. İkinci kilidi isteyen bir iş parçacığı,
sıraya uymak için birinci kilidi **erken almak** ve **elinde tutmak** zorundadır; iki ayrı
kritik bölge tek bir bölgeye kaynar.

**EZ18.** Serbest edinimde iki kaynağın iki ayrı kilidi vardır ve aynı anda iki iş parçacığı
kritik bölgede olabilir; bu iki izinli bir sayan semaforla aynı sayıyı verir. Sıralı
edinimde iki kilit iç içe tutulduğu için kritik bölge tektir.

```python
def kilitli_kosum(is_parcacigi: int, kritik: int, kritik_disi: int, izin: int = 1) -> dict:
    kalan = [{"kritik": kritik, "disi": kritik_disi} for _ in range(is_parcacigi)]
    icerde, t, bekleme, mesgul = [], 0, 0, 0
    while any(k["kritik"] or k["disi"] for k in kalan):
        calisan = 0
        for j, k in enumerate(kalan):
            if k["disi"]:
                k["disi"] -= 1
                calisan += 1
                continue
            if not k["kritik"]:
                continue
            if j in icerde or len(icerde) < izin:
                if j not in icerde:
                    icerde.append(j)
                k["kritik"] -= 1
                calisan += 1
                if not k["kritik"]:
                    icerde.remove(j)
            else:
                bekleme += 1
        mesgul += calisan
        t += 1
    return {"sure": t, "bekleme": bekleme, "mesgul": mesgul,
            "kullanim": round(mesgul / (t * is_parcacigi), 4)}


print("iki kilit , kritik bolge toplam 10 adim , kritik disi 10 adim")
print("  is parcacigi  edinim   izin  sure  bekleme  kullanim")
for n in (2, 4, 8):
    for ad, izin in (("serbest", 2), ("sirali ", 1)):
        k = kilitli_kosum(n, 10, 10, izin)
        print(f"  {n:12d}  {ad}  {izin:4d}  {k['sure']:4d}  {k['bekleme']:7d}"
              f"  {k['kullanim']:8.4f}")
print()
for n in (2, 4, 8):
    s = kilitli_kosum(n, 10, 10, 1)["sure"] - kilitli_kosum(n, 10, 10, 2)["sure"]
    b = kilitli_kosum(n, 10, 10, 1)["bekleme"] - kilitli_kosum(n, 10, 10, 2)["bekleme"]
    print(f"  {n} is parcacigi -> sirali edinimin bedeli: sure +{s} , bekleme +{b}")
```

```
iki kilit , kritik bolge toplam 10 adim , kritik disi 10 adim
  is parcacigi  edinim   izin  sure  bekleme  kullanim
             2  serbest     2    20        0    1.0000
             2  sirali      1    29        9    0.6897
             4  serbest     2    29       18    0.6897
             4  sirali      1    47       54    0.4255
             8  serbest     2    47      108    0.4255
             8  sirali      1    83      252    0.2410

  2 is parcacigi -> sirali edinimin bedeli: sure +9 , bekleme +9
  4 is parcacigi -> sirali edinimin bedeli: sure +18 , bekleme +36
  8 is parcacigi -> sirali edinimin bedeli: sure +36 , bekleme +144
```

Dört iş parçacığında sıralı edinim süreyi **29'dan 47'ye**, bekleme adımını **18'den 54'e**
çıkarıyor. Sekiz iş parçacığında fark daha da açılıyor: süre **47'den 83'e**, bekleme
**108'den 252'ye**. Bedel iş parçacığı sayısıyla büyüyor — **+9, +18, +36**.

Bu sayılar takas edilecek şeyi net kılar. Sıralı edinim kilitlenme olasılığını 0,4074'ten
**0,0000**'a indiriyor; karşılığında sekiz iş parçacığında **144 fazla bekleme adımı**
ödeniyor. Sıfır kilitlenme bir başarım kaybı karşılığında alınır ve bu takas sayılmadan
savunulamaz.

#### Açlık

Kilitlenme, iş parçacıklarının **birlikte** durmasıdır. **Açlık (starvation)** ise bir iş
parçacığının, sistem çalışmaya devam ederken sırasının hiç gelmemesidir. Kilitlenmenin
aksine bekleme çizgesinde döngü yoktur; kilit sürekli veriliyordur, yalnız hep aynılara.

**EZ19.** İş parçacıkları kritik bölgeyi **sürekli** ister; kritik olmayan işi bitirir
bitirmez yeniden sıraya girer.
**EZ20.** Kilit boşaldığında bekleyenlerden biri seçilir; seçim kuralı ya en küçük numaralı
bekleyeni ya da en uzun bekleyeni alır.

```python
def surekli_kosum(is_parcacigi: int, kritik: int, kritik_disi: int,
                  sure: int, adil: bool) -> dict:
    """Kritik bolgeyi surekli isteyen is parcaciklari. Kilit , adil ise en uzun
    bekleyene , degilse en kucuk numarali bekleyene verilir."""
    kalan = [0] * is_parcacigi                 # kritik bolgede kalan adim
    disi = [0] * is_parcacigi                  # kritik olmayan iste kalan adim
    istek = [0] * is_parcacigi                 # ne zamandir bekliyor
    tur = [0] * is_parcacigi                   # kac kez kritik bolgeye girdi
    icerde = None
    for t in range(sure):
        for j in range(is_parcacigi):
            if disi[j]:
                disi[j] -= 1
            elif icerde != j:
                istek[j] += 1
        if icerde is None:
            bekleyen = [j for j in range(is_parcacigi) if not disi[j]]
            if bekleyen:
                sec = max(bekleyen, key=lambda j: istek[j]) if adil else min(bekleyen)
                icerde, kalan[sec], istek[sec] = sec, kritik, 0
                tur[sec] += 1
        if icerde is not None:
            kalan[icerde] -= 1
            if not kalan[icerde]:
                disi[icerde] = kritik_disi
                icerde = None
    return {"tur": tur, "acikta": istek}   # acikta: son istekten beri beklenen adim


print("4 is parcacigi , kritik 10 , kritik disi 10")
print("  sure  secim kurali  tur dagilimi          toplam tur  acikta beklenen")
for sure in (400, 4000):
    for adil in (False, True):
        s = surekli_kosum(4, 10, 10, sure, adil)
        ad = "adil    " if adil else "en kucuk"
        print(f"  {sure:4d}  {ad}      {str(s['tur']):20s}  {sum(s['tur']):10d}"
              f"  {s['acikta']}")
```

```
4 is parcacigi , kritik 10 , kritik disi 10
  sure  secim kurali  tur dagilimi          toplam tur  acikta beklenen
   400  en kucuk      [20, 20, 0, 0]                40  [0, 0, 400, 400]
   400  adil          [10, 10, 10, 10]              40  [20, 10, 0, 0]
  4000  en kucuk      [200, 200, 0, 0]             400  [0, 0, 4000, 4000]
  4000  adil          [100, 100, 100, 100]         400  [20, 10, 0, 0]
```

Dört yüz zaman biriminde kritik bölgeye toplam **40** giriş oluyor — **her iki seçim
kuralında da**. Verim aynı. Dağılım aynı değil: en küçük numaralı bekleyeni seçen kural
girişleri `[20, 20, 0, 0]` biçiminde dağıtıyor. Üçüncü ve dördüncü iş parçacıkları
**hiç girmiyor** ve **400 adım boyunca** açıkta bekliyor.

Süre on katına çıkarıldığında sonuç değişmiyor: `[200, 200, 0, 0]`. Açıkta beklenen adım
400'den **4000**'e çıkıyor. Bu, kilitlenmeden farklı bir arızadır ve **beklemekle
geçmez**; sınırı yoktur, süreyle büyür.

Adil seçim kuralı aynı 40 girişi `[10, 10, 10, 10]` biçiminde dağıtıyor. Toplam verim
değişmiyor, yani **adalet burada bedava geliyor**. Bedava gelmesinin nedeni kritik bölgenin
zaten sıralanmış olmasıdır; ödenen bedel önceki derste çoktan ödendi.

Bu ölçünün asıl uyarısı şudur: verim ölçütü açlığı **göremez**. Toplam tur sayısı iki
kuralda da 40'tır; arızayı gören tek şey **dağılımdır**.

#### Üç Sayı

| Ölçüt | Soyutlamasız taban | Soyutlamalı kurulum | Bedel |
|---|---|---|---|
| Kilitlenen durum | tek kilit, **0** | dört kilit, üç iş parçacığı, **264/648** | oran 0,4074 |
| Sıralı edinim sonrası | — | **0/36** | süre +18, bekleme +36 |
| Açlık | tek iş parçacığı, **0** | dört iş parçacığı, adaletsiz seçim | 2 iş parçacığı, **4000** adım açıkta |

Tablonun ikinci satırı kursun kuralını taşır: kilitlenmenin ortadan kalkması bir kazanç
değil, bir **takastır**. Dört iş parçacığında 0,4074'lük kilitlenme oranı sıfırlanıyor ve
karşılığında her iş parçacığı daha uzun bekliyor.

#### Özet

- Kilitlenme dört koşulun aynı anda sağlanmasıyla oluşur; pratikte kırılabilen tek koşul
  dairesel beklemedir ve bu, bekleme çizgesindeki döngüdür.
- Döngü tespiti yeni bir yordam değildir; Veri Yapıları kursunun derine arama dersinde
  kurulan üç renkli sınama aynen çağrılır.
- Dört kilit ve üç iş parçacığında 648 bekleme durumunun **264**'ü kilitlidir (0,4074); iş
  parçacığı sayısı arttıkça oran 0,1111'den 1,0000'e çıkar.
- Sıralı edinim halkayı yapısal olarak imkânsız kılar — durum uzayı 648'den 36'ya iner ve
  kilitlenen sayısı **0** olur; bedeli sekiz iş parçacığında 144 fazla bekleme adımıdır.
- Açlıkta döngü yoktur ve verim düşmez: 400 zaman biriminde toplam 40 giriş her iki seçim
  kuralında da aynıdır; arıza yalnız dağılımda görünür.

#### Sonraki Adım

Buraya kadar ölçülen her şey tek çekirdekte oldu; iş parçacıkları hiçbir zaman gerçekten
aynı anda ilerlemedi. Sonraki ders bu kısıtı kaldırır. Çekirdek eklendiğinde süre 197'den
180'e iniyor, ama dördüncü çekirdek 179'da kalıyor ve boş çekirdek adımı 108'den 1355'e
çıkıyor. Aynı derste birinci dersin dilim korumasının ne olduğu da görülecek: gerçek
paralellikte erişilebilen serpiştirme yeniden 20'ye çıkar ve 18'i yanlış olur.

### Çok Çekirdekte Eşzamanlılık

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/cok-cekirdekte-eszamanlilik

Gerçek paralelliğin süreye katkısının nerede tükendiği, boş çekirdek adımının ödediği bedel, bellek görünürlüğünün dilim korumasını nasıl ortadan kaldırdığı.

Buraya kadarki bütün ölçüler tek çekirdekte yapıldı. İş parçacıkları sırayla ilerledi;
"aynı anda" sözü hep bir yaklaşımdı, çizelgeleyicinin hızlı geçişlerinin yarattığı bir
görüntüydü. Bu ders o kısıtı kaldırır ve iki soruyu birlikte sorar.

Birincisi başarım sorusudur: çekirdek eklemek işi ne kadar hızlandırır ve nerede durur?
İkincisi doğruluk sorusudur ve daha önemlidir: birinci dersteki **dilim koruması** çok
çekirdekte ne olur? İki soru da aynı ortak tanımın üzerinde sayıyla yanıtlanır. Bu kurs
**tek makineyi** modeller; ağ bedeli yoktur ve hiçbir ölçüye girmez.

#### Çekirdek Eklemek

**Paralellik (parallelism)**, iki işin gerçekten aynı anda yürütülmesidir. Eşzamanlılıktan
ayrılır: eşzamanlılık işlerin **iç içe geçebilmesi**, paralellik **aynı anda
ilerleyebilmesidir**.

**EZ21.** Her çekirdek bir zaman biriminde en çok bir adım yürütür; çekirdekler özdeştir.
**EZ22.** Bekleme adımı çekirdek tutmaz; bekleyen süreç hiçbir çekirdeği meşgul etmez.
**EZ23.** Bir süreç aynı anda tek bir çekirdekte koşar; süreç içi bölünme modellenmez.

```python
TOHUM = 20260218
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30      # bir girdi/cikti adimi kac zaman birimi surer
BAGLAM_BEDELI = 2        # bir baglam degistirmenin bedeli (zaman birimi)
SANAL_SAYFA = 16


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


SONSUZ = 10**9


def cizelgele(isler, yordam="sirayla", dilim=4, cekirdek=1,
              baglam_bedeli=BAGLAM_BEDELI):
    """Her zaman biriminde her cekirdek en cok bir adim yurutur."""
    durum = [{"ad": i["ad"], "adim": list(i["adim"]), "yer": 0, "varis": i["varis"],
              "hazir": i["varis"], "kullanim": 0, "bitis": None,
              "oncelik": i["oncelik"]} for i in isler]
    pay_siniri = SONSUZ if yordam == "sirayla" else dilim
    cekirdekler = [{"surec": None, "onceki": None, "pay": 0, "engel": 0}
                   for _ in range(cekirdek)]
    t, baglam, bos_adim = 0, 0, 0

    def bitmedi(d):
        return d["yer"] < len(d["adim"])

    while any(bitmedi(d) for d in durum) or any(d["bitis"] is None or d["bitis"] > t
                                               for d in durum):
        for c in cekirdekler:                       # birakma
            d = c["surec"]
            if d is not None and (not bitmedi(d) or d["hazir"] > t or c["pay"] >= pay_siniri):
                c["surec"] = None
        for c in cekirdekler:                       # atama
            if c["engel"] or c["surec"] is not None:
                continue
            tutulan = [x["surec"] for x in cekirdekler if x["surec"] is not None]
            hazir = [d for d in durum if bitmedi(d) and d["hazir"] <= t and d not in tutulan]
            if not hazir:
                continue
            if yordam == "oncelikli":
                sec = min(hazir, key=lambda d: (-d["oncelik"], d["hazir"], d["ad"]))
            elif yordam == "adil":
                sec = min(hazir, key=lambda d: (d["kullanim"], d["hazir"], d["ad"]))
            else:
                sec = min(hazir, key=lambda d: (d["hazir"], d["ad"]))
            if c["onceki"] is not None and c["onceki"] is not sec:
                baglam += 1
                c["engel"] = baglam_bedeli
            c["surec"] = sec
            c["onceki"] = sec
            c["pay"] = 0
        for c in cekirdekler:                       # yurutme
            if c["engel"]:
                c["engel"] -= 1
                continue
            d = c["surec"]
            if d is None:
                bos_adim += 1
                continue
            tur, deger = d["adim"][d["yer"]]
            if tur == "BEKLE":
                d["yer"] += 1
                d["hazir"] = t + deger
                c["surec"] = None
                bos_adim += 1
                if not bitmedi(d):
                    d["bitis"] = t + deger
            else:
                d["yer"] += 1
                d["kullanim"] += 1
                d["hazir"] = t + 1
                c["pay"] += 1
                if not bitmedi(d):
                    d["bitis"] = t + 1
                    c["surec"] = None
        t += 1
    for d, i in zip(durum, isler):
        gc = sum(v for tur, v in i["adim"] if tur == "BEKLE")
        d["bekleme"] = d["bitis"] - d["varis"] - d["kullanim"] - gc
    return {"sure": t, "baglam": baglam, "bos_cekirdek_adimi": bos_adim,
            "toplam_is": sum(d["kullanim"] for d in durum),
            "ortalama_tamamlanma": round(sum(d["bitis"] - d["varis"] for d in durum) / len(durum), 2),
            "ortalama_bekleme": round(sum(d["bekleme"] for d in durum) / len(durum), 2),
            "bitis": {d["ad"]: d["bitis"] for d in durum}}


ISLER = is_yuku()
print("cekirdek  sure  baglam  bos cekirdek adimi  toplam is  ort.tamamlanma")
for c in (1, 2, 3, 4, 6, 8):
    m = cizelgele(ISLER, "dilimli", cekirdek=c)
    print(f"  {c:6d}  {m['sure']:4d}  {m['baglam']:5d}  {m['bos_cekirdek_adimi']:17d}"
          f"  {m['toplam_is']:9d}  {m['ortalama_tamamlanma']:14.2f}")

print()
print("her surecin kendi zinciri (hicbir paylasim yokken en erken bitis):")
for i in ISLER:
    h = sum(1 for tur, _ in i["adim"] if tur == "HESAP")
    b = ADIM_SAYISI - h
    print(f"  {i['ad']} varis {i['varis']:2d} hesap {h} bekleme {b}"
          f" -> en erken bitis {i['varis'] + h + BEKLEME_SURESI * b}")
print("  ulasilabilir alt sinir:",
      max(i["varis"] + sum(1 for t, _ in i["adim"] if t == "HESAP")
          + BEKLEME_SURESI * sum(1 for t, _ in i["adim"] if t == "BEKLE") for i in ISLER))
```

```
cekirdek  sure  baglam  bos cekirdek adimi  toplam is  ort.tamamlanma
       1   197     25                108         39          153.80
       2   180     21                279         39          142.40
       3   179     19                460         39          141.40
       4   179     19                639         39          141.40
       6   179     19                997         39          141.40
       8   179     19               1355         39          141.40

her surecin kendi zinciri (hicbir paylasim yokken en erken bitis):
  S1 varis  0 hesap 9 bekleme 3 -> en erken bitis 99
  S2 varis  4 hesap 9 bekleme 3 -> en erken bitis 103
  S3 varis  8 hesap 7 bekleme 5 -> en erken bitis 165
  S4 varis 12 hesap 7 bekleme 5 -> en erken bitis 169
  S5 varis 16 hesap 7 bekleme 5 -> en erken bitis 173
  ulasilabilir alt sinir: 173
```

İkinci çekirdek süreyi **197'den 180'e** indiriyor: 17 zaman birimi, ölçüm bandının çok
üstünde. Üçüncü çekirdek **179** veriyor. Dördüncü de **179**. Altıncı, sekizinci de.

İki çekirdekten sonra hiçbir kazanç yok. 180 ile 179 arasındaki **1 zaman birimlik fark**
zaten ölçüm bandının altındadır ve **ölçülmemiş sayılır**; anlamlı fark en az bağlam bedeli
kadar, yani 2 zaman birimidir.

#### Bedel Boş Çekirdek Adımında

Kazanç durdu, harcama durmadı. Boş çekirdek adımı **108**'den **279**'a, sonra **639**'a,
sekiz çekirdekte **1355**'e çıkıyor. Toplam iş her satırda **39** — yapılan iş hiç
değişmiyor.

Üç sayıyı yan yana koymak bu dersin ana tablosudur:

| Ölçüt | Soyutlamasız taban | Soyutlamalı kurulum | Bedel |
|---|---|---|---|
| Süre | tek çekirdek, **197** | dört çekirdek, **179** | 18 zaman birimi kazanç |
| Boş çekirdek adımı | **108** | **639** | 531 ek boş adım |
| Toplam iş | **39** | **39** | değişmedi |

Dördüncü çekirdek, üçüncüye göre **179**'da hiçbir şey kazandırmıyor ama **179 ek boş
çekirdek adımı** harcıyor. Sekizinci çekirdeğe kadar gidildiğinde 1355 boş adım karşılığında
kazanılan süre **0**'dır. Kursun ikinci iddiası burada da ödeniyor: **soyutlama bazen
kötüleştirir** — burada süreyi kötüleştirmiyor ama kaynağı ölçülebilir biçimde harcıyor.

İki sütun daha okunmalıdır. Bağlam değiştirme **25**'ten **21**'e, sonra **19**'a iniyor:
çekirdek eklendikçe çizelgeleyicinin süreçleri birbirinin yerine koyma ihtiyacı azalıyor.
Ortalama tamamlanma süresi de **153,80**'den **141,40**'a düşüyor; süre 179'da dururken
tekil süreçlerin bitişi iyileşmeye devam ediyor. Süre tek başına bir çekirdek sayısı kararı
vermeye yetmez.

Bir uyarı da buraya aittir. "İki çekirdek yeter" **bu iş yükünün** sonucudur, genel bir
kural değildir. Bu iş yükü girdi/çıktı ağırlıklıdır; hesap ağırlıklı bir iş yükünde eğri
başka yerde düzleşir. Tek iş yükünde ölçülmüş bir sıralama **ölçülmemiş sayılır** ve bu
kursta öyle yazılır.

#### Neden 179'da Duruyor

Çıktının ikinci bölümü nedeni veriyor. Her sürecin kendi adım zinciri var ve bu zincir
bölünemez: bir bekleme adımı bittikten sonra sıradaki hesap adımı gelir, tersi olmaz.

S5, 16. zaman biriminde varıyor, 7 hesap ve 5 bekleme adımı taşıyor. Beklemeler
$5 \times 30 = 150$ zaman birimi tutuyor ve bu süre boyunca **hiçbir çekirdek işe
yaramıyor**. En erken bitişi $16 + 7 + 150 = 173$.

**173, sonsuz çekirdekle bile aşılamayacak alt sınırdır.** Ölçülen 179, bu sınırın 6 zaman
birimi üstünde. Aradaki fark bağlam değiştirmeden ve varış sıralamasından geliyor.

Buradan çıkan kural sayıya dayanır: **iş girdi/çıktıya bağlıysa çekirdek eklemek onu
değiştirmez.** Bu iş yükünde 39 hesap adımına karşılık 21 bekleme adımı var; beklemeler
630 zaman birimi tutuyor, hesap yalnız 39. Hızlandırılacak şey zaten hesap değildi.

#### Bellek Görünürlüğü

Şimdi doğruluk sorusu. Birinci ders, çizelgeleyici dilimi kritik bölgeden büyük olduğunda
erişilebilen serpiştirmenin **2**'ye indiğini ve hiçbirinin yanlış olmadığını gösterdi. O
koruma neye dayanıyordu? Tek bir şeye: **iki iş parçacığının aynı anda ilerleyememesine.**

**EZ24.** İki çekirdekte iki iş parçacığı ayrı çekirdeklere düşer; aralarında çizelgeleyici
geçişi gerekmediği için dilim, bellek erişimlerinin sırasını kısıtlamaz.

```python
def sayac_kosumu(desen: list[int], kilit: bool = False) -> int:
    if kilit:
        return 2
    sayac, yerel = 0, {0: None, 1: None}
    asama = {0: 0, 1: 0}
    for kim in desen:
        a = asama[kim]
        if a == 0:
            yerel[kim] = sayac
        elif a == 1:
            yerel[kim] = yerel[kim] + 1
        else:
            sayac = yerel[kim]
        asama[kim] = a + 1
    return sayac


def serpistirmeler(a: int, b: int) -> list[list[int]]:
    if a == 0:
        return [[1] * b]
    if b == 0:
        return [[0] * a]
    return ([[0] + s for s in serpistirmeler(a - 1, b)]
            + [[1] + s for s in serpistirmeler(a, b - 1)])


def dilimli_serpistirmeler(adim: int, dilim: int) -> list[list[int]]:
    sonuc = []

    def gez(kalan_a, kalan_b, kim, dizi):
        if not kalan_a and not kalan_b:
            sonuc.append(list(dizi))
            return
        for yeni in (0, 1):
            kalan = kalan_a if yeni == 0 else kalan_b
            if not kalan:
                continue
            n = min(dilim, kalan)
            dizi.extend([yeni] * n)
            gez(kalan_a - n if yeni == 0 else kalan_a,
                kalan_b - n if yeni == 1 else kalan_b, yeni, dizi)
            del dizi[len(dizi) - n:]
    gez(adim, adim, None, [])
    ayri = []
    for d in sonuc:
        if d not in ayri:
            ayri.append(d)
    return ayri


print("cekirdek  dilim  kilit  erisilebilen  yanlis  yanlis orani")
for cekirdek, dilim, kilit in ((1, 1, False), (1, 3, False), (2, 3, False),
                               (4, 3, False), (2, 3, True)):
    # iki cekirdekte iki is parcacigi ayri cekirdeklere duser: dilim kisiti kalkar
    kume = serpistirmeler(3, 3) if cekirdek > 1 else dilimli_serpistirmeler(3, dilim)
    yanlis = sum(1 for d in kume if sayac_kosumu(d, kilit) != 2)
    print(f"  {cekirdek:6d}  {dilim:5d}  {str(kilit):5s}  {len(kume):12d}  {yanlis:6d}"
          f"  {yanlis / len(kume):11.4f}")
```

```
cekirdek  dilim  kilit  erisilebilen  yanlis  yanlis orani
       1      1  False            20      18       0.9000
       1      3  False             2       0       0.0000
       2      3  False            20      18       0.9000
       4      3  False            20      18       0.9000
       2      3  True             20       0       0.0000
```

İkinci satır birinci dersin sonucudur: tek çekirdek, dilim 3, **2** erişilebilen
serpiştirme, **0** yanlış. Üçüncü satır aynı dilimle, yalnız bir çekirdek fazlasıyla
alınmıştır: erişilebilen serpiştirme **20**, yanlış **18**, oran **0,9000**.

Dilim hâlâ 3. Program hâlâ aynı. Değişen tek şey çekirdek sayısı — ve birinci dersin
"görünmez olmuş" hatası **bir donanım değişikliğiyle geri gelmiştir**. Birinci dersin
cümlesi tam olarak bunun içindi: hata yok olmamıştı.

Bu, **bellek görünürlüğü (memory visibility)** sorununun en yalın biçimidir. Bir çekirdeğin
yazdığı değerin öbür çekirdeğe ne zaman göründüğü, programın denetiminde değildir. Son satır
tek çözümü gösteriyor: kritik bölge bölünemez kılındığında yirmi serpiştirmenin yirmisi de
doğru sonuç verir.

Bunun pratik sonucu, birinci dersin örneklem ölçüsünü tamamlar. Orada koşum sayısının kanıt
üretmediği gösterilmişti; burada **koşum ortamının** da kanıt üretmediği görülüyor. Tek
çekirdekli bir ortamda binlerce kez temiz sonuç veren bir program, çekirdek sayısı arttığı
anda bozulabilir — çünkü değişen şey programın kendisi değil, **erişilebilen serpiştirme
kümesidir**. Bir eşzamanlılık güvencesi, üzerinde koşulan çekirdek sayısından bağımsız
olmalıdır; bağımlıysa güvence değildir.

#### Önbellek Uyumu ve Atomik İşlem

Çekirdekler belleği paylaşırken her birinin kendi önbelleği olur. Bir çekirdeğin yazdığı
satırın öbürlerinde geçersizleştirilmesi ve gerektiğinde aktarılması **önbellek uyumu
(cache coherence)** düzeneğiyle sağlanır. Bu düzenek bedava değildir: paylaşılan bir
değişkene sık yazmak, çekirdekler arasında sürekli aktarım üretir.

**EZ25.** Bu tanımda önbellek uyumu ve bellek veri yolu çekişmesi **modellenmemiştir**;
dolayısıyla bu yöndeki bedel bu kursta **ölçülmemiştir** ve ölçülmemiş sayılır. Yukarıdaki
639 boş çekirdek adımı, uyum bedelini **içermez**.

Görünürlük sorununun tek adımlık çözümü **atomik işlemdir**: donanımın
oku-artır-yaz üçlemesini bölünmez tek bir adım olarak yürütmesi. Kritik bölge bir adıma
indiğinde çekişme de ortadan kalkar; önceki dersin süpürmesi bunu ölçmüştü — sekiz iş
parçacığında kritik bölge 10 adımken süre **83** ve kullanım **0,2410** iken, kritik bölge
tek adıma indiğinde süre **20** ve kullanım **1,0000** oluyordu.

Atomik işlemin sınırı da oradadır: yalnız **tek bir konuma** uygulanır. İki alanı birlikte
tutarlı tutmak gerekiyorsa kritik bölge yeniden birden çok adım olur ve kilidin bedeli geri
gelir.

#### Özet

- İkinci çekirdek süreyi 197'den 180'e indirir; üçüncüsü 179 verir ve sonrası hiçbir şey
  kazandırmaz — 180 ile 179 arasındaki 1 zaman birimlik fark ölçüm bandının altındadır.
- Boş çekirdek adımı 108'den 639'a, sekiz çekirdekte 1355'e çıkarken toplam iş 39'da
  sabit kalır; harcanan kaynağın karşılığı yoktur.
- Alt sınır 173'tür ve bekleme zincirlerinden gelir; iş girdi/çıktıya bağlı olduğunda
  çekirdek eklemek bunu değiştirmez.
- Gerçek paralellikte dilim koruması ortadan kalkar: dilim 3'te tek çekirdekte 2 olan
  erişilebilen serpiştirme, iki çekirdekte 20'ye çıkar ve 18'i yanlış olur.
- Önbellek uyumunun bedeli bu tanımda modellenmemiştir ve ölçülmemiş sayılır; atomik işlem
  kritik bölgeyi tek adıma indirir ama yalnız tek bir konum için geçerlidir.

#### Sonraki Adım

Üç ders boyunca tek bir eşzamanlılık modeli kullanıldı: paylaşılan belleğe erişen iş
parçacıkları ve onları koruyan kilitler. Bu modelin bedeli artık sayılı — çekişme, kilitlenme
riski, görünürlük sorunu. Sonraki ders paylaşılan belleğin zorunlu olmadığını gösterir ve üç
modeli **aynı iş yükünde** karşılaştırır: iş parçacığı, olay döngüsü ve ileti geçişi. Ölçü
iki ayrı iş yükünde yinelenir, çünkü tek iş yükünde ölçülmüş bir sıralama ölçülmemiş
sayılır.

### Eşzamanlılık Modelleri

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/eszamanlilik-modelleri

İş parçacığı, olay döngüsü ve ileti geçişi modellerinin aynı iş yükünde süre, boş çekirdek adımı ve doğruluk güvencesi bakımından karşılaştırılması.

Dört ders boyunca tek bir eşzamanlılık modeli kullanıldı: paylaşılan belleğe erişen iş
parçacıkları ve onları koruyan kilitler. Bu modelin bedeli artık sayılı — çekişme sekiz iş
parçacığında 252 bekleme adımı, dört kilitli üç iş parçacığında 0,4074 kilitlenme oranı,
iki çekirdekte 18 yanlış serpiştirme.

Bu bedellerin hepsinin **tek bir ortak kaynağı** var: paylaşılan değiştirilebilir durum.
Kaldırılırsa hepsi birden kalkar. Bu ders paylaşılan durumu kaldıran ya da erişimini
yapısal olarak sıralayan iki modeli ekler ve üçünü **aynı iş yükünde** sayar.

#### Üç Model

**İş parçacığı modeli.** Paylaşılan adres uzayı, önalımlı çizelgeleme, kilitle korunan
kritik bölgeler. Buraya kadar ölçülen model budur.

**Olay döngüsü modeli.** Tek iş parçacığı, önalımsız yürütme, **sonuna kadar çalışma**.
Bekleme işi engellemez; iş bittiğinde sıradaki alınır. Bu model **Eşzamansız JavaScript ve
Çalışma Zamanı** kursunda ayrıntısıyla kuruldu ve ölçüldü; olay döngüsünün kuyruk kuralı,
mikro görev ayrımı ve engelleyen işin sonuçları **burada tekrarlanmaz**. Bu ders onu yalnız
**üç modelden biri** olarak alır.

**İleti geçişi modeli.** Yalıtılmış durum, paylaşım yok, iletişim yalnız kopyalanan
iletilerle. Durum paylaşılmadığı için kilit de gerekmez.

Üç model aynı çizelgeleyicinin üç ayarıdır ve bu, karşılaştırmayı anlamlı kılan şeydir.

**EZ26.** İş parçacığı modeli: önalımlı, dilim 4, tek çekirdek, bağlam bedeli 2.
**EZ27.** Olay döngüsü modeli: önalımsız, tek çekirdek, bağlam bedeli **0** — geçiş bir
adres uzayı değişimi değil, bir sıradaki işe geçiştir.
**EZ28.** İleti geçişi modeli: yalıtılmış durum, dört çekirdek, bağlam bedeli 2.
**EZ29.** İleti kopyalama bedeli bu tanımda **modellenmemiştir** ve ölçülmemiş sayılır.
**EZ30.** Üç model **aynı iş listesini** yürütür; iş yükü özdeştir.

```python
TOHUM = 20260218
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30      # bir girdi/cikti adimi kac zaman birimi surer
BAGLAM_BEDELI = 2        # bir baglam degistirmenin bedeli (zaman birimi)
SANAL_SAYFA = 16


def uretec(tohum):
    """Belirlenimci sozde-rastgele uretec. Ayni tohum ayni diziyi verir."""
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


SONSUZ = 10**9


def cizelgele(isler, yordam="sirayla", dilim=4, cekirdek=1,
              baglam_bedeli=BAGLAM_BEDELI):
    """Her zaman biriminde her cekirdek en cok bir adim yurutur."""
    durum = [{"ad": i["ad"], "adim": list(i["adim"]), "yer": 0, "varis": i["varis"],
              "hazir": i["varis"], "kullanim": 0, "bitis": None,
              "oncelik": i["oncelik"]} for i in isler]
    pay_siniri = SONSUZ if yordam == "sirayla" else dilim
    cekirdekler = [{"surec": None, "onceki": None, "pay": 0, "engel": 0}
                   for _ in range(cekirdek)]
    t, baglam, bos_adim = 0, 0, 0

    def bitmedi(d):
        return d["yer"] < len(d["adim"])

    while any(bitmedi(d) for d in durum) or any(d["bitis"] is None or d["bitis"] > t
                                               for d in durum):
        for c in cekirdekler:                       # birakma
            d = c["surec"]
            if d is not None and (not bitmedi(d) or d["hazir"] > t or c["pay"] >= pay_siniri):
                c["surec"] = None
        for c in cekirdekler:                       # atama
            if c["engel"] or c["surec"] is not None:
                continue
            tutulan = [x["surec"] for x in cekirdekler if x["surec"] is not None]
            hazir = [d for d in durum if bitmedi(d) and d["hazir"] <= t and d not in tutulan]
            if not hazir:
                continue
            if yordam == "oncelikli":
                sec = min(hazir, key=lambda d: (-d["oncelik"], d["hazir"], d["ad"]))
            elif yordam == "adil":
                sec = min(hazir, key=lambda d: (d["kullanim"], d["hazir"], d["ad"]))
            else:
                sec = min(hazir, key=lambda d: (d["hazir"], d["ad"]))
            if c["onceki"] is not None and c["onceki"] is not sec:
                baglam += 1
                c["engel"] = baglam_bedeli
            c["surec"] = sec
            c["onceki"] = sec
            c["pay"] = 0
        for c in cekirdekler:                       # yurutme
            if c["engel"]:
                c["engel"] -= 1
                continue
            d = c["surec"]
            if d is None:
                bos_adim += 1
                continue
            tur, deger = d["adim"][d["yer"]]
            if tur == "BEKLE":
                d["yer"] += 1
                d["hazir"] = t + deger
                c["surec"] = None
                bos_adim += 1
                if not bitmedi(d):
                    d["bitis"] = t + deger
            else:
                d["yer"] += 1
                d["kullanim"] += 1
                d["hazir"] = t + 1
                c["pay"] += 1
                if not bitmedi(d):
                    d["bitis"] = t + 1
                    c["surec"] = None
        t += 1
    for d, i in zip(durum, isler):
        gc = sum(v for tur, v in i["adim"] if tur == "BEKLE")
        d["bekleme"] = d["bitis"] - d["varis"] - d["kullanim"] - gc
    return {"sure": t, "baglam": baglam, "bos_cekirdek_adimi": bos_adim,
            "toplam_is": sum(d["kullanim"] for d in durum),
            "ortalama_tamamlanma": round(sum(d["bitis"] - d["varis"] for d in durum) / len(durum), 2),
            "ortalama_bekleme": round(sum(d["bekleme"] for d in durum) / len(durum), 2),
            "bitis": {d["ad"]: d["bitis"] for d in durum}}


MODELLER = (("is parcacigi", dict(yordam="dilimli", dilim=4, cekirdek=1, baglam_bedeli=2)),
            ("olay dongusu", dict(yordam="sirayla", cekirdek=1, baglam_bedeli=0)),
            ("ileti gecisi", dict(yordam="dilimli", dilim=4, cekirdek=4, baglam_bedeli=2)))

for k, tohum in enumerate((20260218, 20260219)):
    if k:
        print()
    ISLER = is_yuku(tohum)
    hesap = sum(1 for i in ISLER for t, _ in i["adim"] if t == "HESAP")
    print(f"is yuku {tohum}: {hesap} hesap adimi , "
          f"{SUREC_SAYISI * ADIM_SAYISI - hesap} bekleme adimi")
    print("  model         sure  baglam  bos cekirdek  ort.tamamlanma  ort.bekleme")
    for ad, ayar in MODELLER:
        s = cizelgele(ISLER, **ayar)
        print(f"  {ad:12s}  {s['sure']:4d}  {s['baglam']:6d}  {s['bos_cekirdek_adimi']:12d}"
              f"  {s['ortalama_tamamlanma']:14.2f}  {s['ortalama_bekleme']:11.2f}")
```

```
is yuku 20260218: 39 hesap adimi , 21 bekleme adimi
  model         sure  baglam  bos cekirdek  ort.tamamlanma  ort.bekleme
  is parcacigi   197      25           108          153.80        20.00
  olay dongusu   178      22           139          137.20         3.40
  ileti gecisi   179      19           639          141.40         7.60

is yuku 20260219: 46 hesap adimi , 14 bekleme adimi
  model         sure  baglam  bos cekirdek  ort.tamamlanma  ort.bekleme
  is parcacigi   202      22           112          122.20        29.00
  olay dongusu   177      14           131           98.00         4.80
  ileti gecisi   175      12           630           98.00         4.80
```

#### Tabloda Ne Duruyor

En net sonuç, **iş parçacığı modelinin iki iş yükünde de en kötü süreyi vermesidir**:
birincide **197**, ikincide **202**. Bu, ortak tanımın birinci okumasının bu derse düşen
yüzüdür — önalımlı dilimleme adaleti süreyle ödetir ve bedel her iki iş bileşiminde de
ayakta kalır.

Olay döngüsü ile ileti geçişi arasındaki fark ise **ölçülemiyor**. Birinci iş yükünde 178'e
karşı 179, yani **1 zaman birimi**; ikincisinde 177'ye karşı 175, yani **2 zaman birimi**.
Bu iş yükünde ölçüm bandının alt sınırı bağlam bedeli kadardır, yani 2 zaman birimi. İlk
fark bandın **altında** ve yorumlanamaz; ikincisi tam **sınırında** ve tek başına bir
sıralama kurmaya yetmez.

Bu iki modelin farkı süre sütununda değil, **boş çekirdek adımı** sütununda duruyor. Olay
döngüsü birinci iş yükünde **139**, ileti geçişi **639** boş çekirdek adımı harcıyor. Aynı
süreyi almak için ileti geçişi **dört çekirdek** kullanıyor; olay döngüsü **bir**. Süre eşit,
kaynak dört buçuk katı.

Sıralama iddiası kurulamaz, ama **bedel karşılaştırması kurulabilir**: bu iş yükünde ileti
geçişinin dört çekirdeği bir süre kazancı üretmiyor.

#### İş Yükü Bileşimi Neyi Değiştiriyor

İki iş yükü aynı beş süreci farklı bileşimde taşıyor: birincisinde **39 hesap, 21 bekleme**;
ikincisinde **46 hesap, 14 bekleme**. İkincisi daha hesap ağırlıklı.

Hesap payı arttıkça ileti geçişi göreli olarak iyileşiyor: birinci iş yükünde olay
döngüsünün 1 zaman birimi gerisindeyken, ikincisinde 2 zaman birimi önüne geçiyor. Yön
beklenen yöndür — çekirdek eklemek hesabı paralelleştirir, beklemeyi paralelleştirmez.
Ama **büyüklük ölçüm bandının sınırındadır** ve bu kursta böyle bir fark üstünlük iddiasına
çevrilmez.

Bağlam değiştirme sütunu daha net konuşuyor. İkinci iş yükünde iş parçacığı modeli **22**,
olay döngüsü **14**, ileti geçişi **12** bağlam değiştirme üretiyor. Önalımın bedeli
burada doğrudan görünür.

Ortalama tamamlanma süresi de ayrışıyor: ikinci iş yükünde iş parçacığı modeli **122,20**,
diğer ikisi **98,00**. Süre farkı ölçülemezken tekil işlerin bitiş süresi ölçülebilir
biçimde ayrılıyor — **hangi ölçütün sorulduğu, hangi modelin kazandığını değiştirir.**

#### Doğruluk Ekseni

Süre tek eksen değil. Üç model, aynı paylaşılan sayaç sorusuna **üç ayrı güvence** verir.

```python
def sayac_kosumu(desen: list[int]) -> int:
    sayac, yerel = 0, {0: None, 1: None}
    asama = {0: 0, 1: 0}
    for kim in desen:
        a = asama[kim]
        if a == 0:
            yerel[kim] = sayac
        elif a == 1:
            yerel[kim] = yerel[kim] + 1
        else:
            sayac = yerel[kim]
        asama[kim] = a + 1
    return sayac


def serpistirmeler(a: int, b: int) -> list[list[int]]:
    if a == 0:
        return [[1] * b]
    if b == 0:
        return [[0] * a]
    return ([[0] + s for s in serpistirmeler(a - 1, b)]
            + [[1] + s for s in serpistirmeler(a, b - 1)])


def dilimli_serpistirmeler(adim: int, dilim: int) -> list[list[int]]:
    sonuc = []

    def gez(kalan_a, kalan_b, kim, dizi):
        if not kalan_a and not kalan_b:
            sonuc.append(list(dizi))
            return
        for yeni in (0, 1):
            kalan = kalan_a if yeni == 0 else kalan_b
            if not kalan:
                continue
            n = min(dilim, kalan)
            dizi.extend([yeni] * n)
            gez(kalan_a - n if yeni == 0 else kalan_a,
                kalan_b - n if yeni == 1 else kalan_b, yeni, dizi)
            del dizi[len(dizi) - n:]
    gez(adim, adim, None, [])
    ayri = []
    for d in sonuc:
        if d not in ayri:
            ayri.append(d)
    return ayri


print("model         paylasilan durum  kritik bolge bolunur  erisilebilen  yanlis")
for ad, paylasim, bolunur in (("is parcacigi", "var", True),
                              ("olay dongusu", "var", False),
                              ("ileti gecisi", "yok", None)):
    if paylasim == "yok":                 # sayaci tek sahip degistirir , serpisme yoktur
        print(f"  {ad:12s}  {paylasim:16s}  {'-':>20s}  {'-':>12s}  {'-':>6s}")
        continue
    kume = serpistirmeler(3, 3) if bolunur else dilimli_serpistirmeler(3, 3)
    yanlis = sum(1 for d in kume if sayac_kosumu(d) != 2)
    print(f"  {ad:12s}  {paylasim:16s}  {str(bolunur):>20s}  {len(kume):12d}  {yanlis:6d}")
```

```
model         paylasilan durum  kritik bolge bolunur  erisilebilen  yanlis
  is parcacigi  var                               True            20      18
  olay dongusu  var                              False             2       0
  ileti gecisi  yok                                  -             -       -
```

İş parçacığı modelinde kritik bölge bölünebilir; korunmazsa **20 serpiştirmenin 18'i**
yanlış sonuç verir. Korunursa doğruluk gelir ve bedeli önceki derslerde sayıldı: dört iş
parçacığında süre 29'dan 47'ye, bekleme 18'den 54'e çıkar.

Olay döngüsü modelinde kritik bölge **bölünemez**: sonuna kadar çalışma kuralı, bir işin
ortasında başka bir işin çalışmasını yapısal olarak yasaklar. Erişilebilen serpiştirme
**2**, yanlış **0** ve bu, birinci dersteki dilim ayarından **türce** başkadır — orada
koruma bir ayardı, burada modelin tanımıdır. Buna karşılık aynı kural bir sınır getirir:
uzun bir hesap adımı bütün kuyruğu bekletir ve model tek çekirdeği aşamaz.

İleti geçişi modelinde soru **sorulamaz**: sayacı tek bir sahip değiştirir, öbür taraf ileti
gönderir. Paylaşılan değiştirilebilir durum olmadığı için kritik bölge de, kilit de,
bellek görünürlüğü sorunu da yoktur. Kilitlenme ise büsbütün kalkmaz, biçim değiştirir:
kuyruk kapasitesi sınırlıysa ya da iki taraf birbirinin iletisini bekliyorsa **iletişim
kilitlenmesi** doğabilir; bu tanımdaki iş listesi böyle bir bekleme içermez. Bedeli,
ölçülmeyen bir yerde durur — her iletide durumun **kopyalanması** (EZ29).

#### Modeller Birbirini Dışlamaz

Üç modelin aynı çizelgeleyicinin üç ayarı olması yalnız ölçüm kolaylığı değil, yapısal bir
gözlemdir. Ayarlar birbirinden bağımsızdır ve karıştırılabilir.

Bunun en görünür örneği tablodadır. Olay döngüsü ayarı tek çekirdeklidir; ileti geçişi ayarı
dört çekirdeklidir ve durumu yalıtır. İkisini birleştirmek, **her çekirdekte bir olay
döngüsü** çalıştırmak ve aralarında yalnız ileti göndermek demektir. Bu bileşim yeni bir
model değildir; iki ayarın aynı anda seçilmesidir.

Aynı biçimde, iş parçacığı modeli de yalıtımla birleştirilebilir: paylaşılan durum yalnız
gerçekten paylaşılması gereken yerde bırakılır, geri kalanı iş parçacığına özel tutulur.
Kritik bölge kısaldıkça çekişme düşer ve bu düşüşün ölçüsü ikinci derste alınmıştı — sekiz
iş parçacığında kritik pay 0,50'den 0,05'e indiğinde süre 83'ten 20'ye iniyordu.

Buradan çıkan kural şudur: **model bir bayrak değil, bir ayar kümesidir.** Sorulacak soru
"hangi model" değil, "hangi durum paylaşılacak, önalım olacak mı, kaç çekirdek kullanılacak"
sorularıdır. Her birinin bedeli bu konuda ayrı ayrı sayıldı.

#### Model Seçimi Neyi Seçmektir

| Model | Süre (39/21) | Süre (46/14) | Boş çekirdek | Doğruluk güvencesi |
|---|---|---|---|---|
| İş parçacığı | 197 | 202 | 108 / 112 | yok, kilitle kurulur |
| Olay döngüsü | 178 | 177 | 139 / 131 | sonuna kadar çalışma |
| İleti geçişi | 179 | 175 | 639 / 630 | paylaşım yok |

Tablodan çıkarılabilecek tek genel sonuç, **iş parçacığı modelinin iki iş yükünde de en
kötü süreyi verdiğidir**. Diğer iki model arasındaki fark, iki iş yükünde de ölçüm bandının
içinde ya da sınırındadır; bu kursta böyle bir fark **ölçülmemiş sayılır**.

Seçim bu yüzden süreyle yapılmaz. Seçilen şey **hangi bedelin ödeneceğidir**: iş parçacığı
modelinde çekişme ve kilitlenme riski, olay döngüsünde tek çekirdek sınırı, ileti geçişinde
kopyalama ve dört buçuk kat boş çekirdek adımı.

#### Özet

- Üç model aynı iş yükünde ölçüldüğünde iş parçacığı modeli iki iş yükünde de en kötü
  süreyi verir: 197 ve 202.
- Olay döngüsü ile ileti geçişi arasındaki süre farkı 1 ve 2 zaman birimidir; ölçüm bandının
  altında ya da sınırındadır ve bir sıralama kurmaya yetmez.
- Fark boş çekirdek adımında görünür: aynı süre için olay döngüsü 139, ileti geçişi 639
  boş çekirdek adımı harcar.
- Doğruluk güvenceleri türce farklıdır: iş parçacığı modelinde güvence yoktur ve kilitle
  kurulur; olay döngüsünde sonuna kadar çalışmadan gelir; ileti geçişinde paylaşım
  olmadığı için soru sorulamaz.
- Hangi ölçütün sorulduğu kazananı değiştirir: ikinci iş yükünde süre farkı ölçülemezken
  ortalama tamamlanma 122,20'ye karşı 98,00 ile ayrışır.

#### Sonraki Adım

Bu konu, eşzamanlılığın bedelini üç eksende saydı: bekleme adımı, kilitlenme oranı ve
erişilebilen serpiştirme kümesi. Hepsinde ortak varsayım, belleğin sınırsız ve erişimin
bedelsiz olmasıydı — bir hesap adımı her zaman bir zaman birimi sürdü. Sonraki konu bu
varsayımı kaldırır. Bellek sonludur, adres uzayı fizikselden büyüktür ve bir erişim diskten
gelen bir sayfayı beklemek zorunda kalabilir. İlk ders bu iş yükünün ürettiği 39 erişimlik
diziyi alıp 15 ayrı sanal sayfa üzerinde üç sayfa değiştirme yordamını süpürecek ve daha
akıllı yordamın her zaman kazandırmadığını gösterecek.

## Bellek ve Depolama

Sanal bellek ve sayfa değiştirme, öbek ayırma ve parçalanma, çöp toplama duraklatması, dosya sistemi soyutlaması ve girdi/çıktı modelleri.

### Sanal Bellek

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/sanal-bellek

Sayfalı belleğin üç bedeli: adres çevirisi, sayfa hatası ve sayfa değiştirme yordamının seçimi — 39 erişimlik bir dizide fiziksel sayfa sayısı süpürülerek ölçülür.

Eşzamanlılık konusu üç modeli aynı iş yükünde saydı ve üçü de tek bir şeyi sessizce
varsaydı: bir sürecin dokunduğu her adres bellekte hazır duruyor. Bu varsayımın donanımda
karşılığı yoktur. Bir sürecin adres uzayı, makinedeki fiziksel bellekten büyük olabilir; beş
süreç aynı anda çalışıyorsa toplamları neredeyse her zaman büyüktür.

Bu ders aradaki farkı kapatan soyutlamayı kurar ve bedelini sayar. Bilgisayarlar Nasıl
Çalışır kursundaki Bellek Düzeni dersi adres uzayını, yığıtı ve öbeği tanımlamıştı; o tanım
burada tekrarlanmaz. Eklenen tek şey şudur: adres uzayı gerçek belleğe doğrudan oturmuyorsa
her erişim bir çeviriden geçer, çevirinin karşılıksız kaldığı her yer bir **sayfa hatası**
üretir ve sayfa hataları sayılabilir.

#### Sanal Sayfa ile Fiziksel Sayfa

**Sanal bellek (virtual memory)**, sürecin gördüğü adres uzayı ile makinedeki fiziksel
belleği ayıran soyutlamadır. Ayrım sabit boyutlu birimler üzerinden kurulur: adres uzayı eşit
büyüklükte **sanal sayfalara**, fiziksel bellek aynı büyüklükte **fiziksel sayfalara** bölünür
ve hangi sanal sayfanın hangi fiziksel sayfada durduğunu bir **sayfa tablosu** tutar. Bu
düzenlemenin adı **sayfalı bellektir (paging)**.

Soyutlamasız taban tek cümleyle söylenir: sanal sayfa sayısı kadar fiziksel sayfa varsa
eşleme birebirdir, tablo aranmaz ve ilk yüklemeden sonra hiçbir erişim diske gitmez. Ortak
tanımın iş yükünde adres uzayı 16 sanal sayfadır ve beş sürecin ürettiği erişim dizisi bu
sayfaların 15'ine dokunur. Taban bu 15 sayfanın bir kez getirilmesidir.

- **BD1** — Makine ortak tanımın modelidir: adres uzayı 16 sanal sayfa, iş yükü beş süreç ve
  tohum 20260218. Hiçbir gerçek işletim sistemi çağrılmaz.
- **BD2** — Erişim dizisi süreçlerin hesap adımlarından zaman sırasına göre üretilir; bekleme
  adımları belleğe dokunmaz.
- **BD3** — Bir sayfanın diskten getirilmesi bir bekleme adımıdır, yani **30 zaman birimi**.
  Hesap adımı 1 zaman birimidir.
- **BD4** — Sayfa tablosu araması modelde ücretsizdir; ölçülen tek şey hatadır. Gerçek bir
  makinede tablo aramasının da bir bedeli olurdu ve bu ders onu saymaz.

```python
"""M01/K05 ortak tanim (kesit): is yuku ve sanal sayfa erisim dizisi."""
TOHUM = 20260218
SUREC_SAYISI = 5
ADIM_SAYISI = 12
BEKLEME_SURESI = 30
SANAL_SAYFA = 16


def uretec(tohum):
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def is_yuku(tohum=TOHUM):
    r = uretec(tohum)
    isler = []
    for i in range(SUREC_SAYISI):
        adimlar = []
        for _ in range(ADIM_SAYISI):
            if r(10) < 3:
                adimlar.append(("BEKLE", BEKLEME_SURESI))
            else:
                taban = (i * 3) % SANAL_SAYFA
                adimlar.append(("HESAP", (taban + r(4)) % SANAL_SAYFA))
        isler.append({"ad": f"S{i+1}", "adim": adimlar,
                      "oncelik": 1 + r(3), "varis": i * 4})
    return isler


def erisim_dizisi(isler):
    olay = []
    for i in isler:
        t = i["varis"]
        for tur, deger in i["adim"]:
            if tur == "HESAP":
                olay.append((t, deger))
                t += 1
            else:
                t += deger
    olay.sort(key=lambda o: o[0])
    return [s for _, s in olay]


DIZI = erisim_dizisi(is_yuku())
print("erisim:", len(DIZI), "| ayri sanal sayfa:", len(set(DIZI)))
print("dizi:", DIZI)
for pencere in (4, 8, 16):
    genis = [len(set(DIZI[i:i + pencere])) for i in range(len(DIZI) - pencere + 1)]
    print(f"pencere {pencere:2d} calisma kumesi ortalama {sum(genis)/len(genis):5.2f} "
          f"en genis {max(genis):2d}")
```

```
erisim: 39 | ayri sanal sayfa: 15
dizi: [6, 9, 11, 9, 11, 9, 12, 1, 3, 1, 6, 9, 7, 9, 0, 2, 0, 5, 2, 3, 0, 5, 2, 3, 5, 3, 8, 9, 6, 4, 13, 12, 15, 13, 8, 12, 14, 12, 7]
pencere  4 calisma kumesi ortalama  3.47 en genis  4
pencere  8 calisma kumesi ortalama  5.75 en genis  8
pencere 16 calisma kumesi ortalama  9.25 en genis 11
```

Son üç satır **çalışma kümesini (working set)** verir: bir pencere içinde dokunulan ayrı sayfa
sayısı. Dört erişimlik pencerede ortalama 3,47 ayrı sayfa var, yani dört erişimin neredeyse
hepsi başka bir sayfaya gidiyor. İşlemci Önbelleği dersinde tanımlanan yerellik burada
doğrudan bir sayı üretiyor ve bu iş yükünün yerelliği zayıf: sekiz erişimlik pencerelerin en
genişinde sekiz ayrı sayfa dokunuluyor, yani hiç tekrar yok. Zayıf yerellik, sayfa hatası
sayısının fiziksel sayfa sayısına ne kadar duyarlı olacağını baştan belirler.

#### Adres Çevirisi

Bir sanal adres iki parçaya ayrılır: hangi sanal sayfada olduğu ve o sayfanın içinde kaçıncı
bayt olduğu. İkincisine **sayfa içi ofset (page offset)** denir. Sayfa boyu 256 bayt ise
çeviri iki bölmedir — sayfa numarası adresin 256'ya bölümü, ofset kalanıdır. Fiziksel adres,
sayfa tablosunun verdiği fiziksel sayfa numarasıyla aynı ofsetin birleşimidir.

- **BD5** — Sayfa boyu 256 bayt, fiziksel sayfa sayısı 4. Erişim dizisinin ilk on adımı
  kullanılır; her adımın sayfa içi ofseti kurgudur, sayfa numarası ortak tanımdan gelir.
- **BD6** — Boş fiziksel sayfa kalmadığında ilk giren ilk çıkar kuralı uygulanır.

```python
"""Adres cevirisi: sanal adres -> sanal sayfa + ofset -> fiziksel adres."""
SAYFA_BOYU = 256
FIZIKSEL = 4
ILK = [6, 9, 11, 9, 11, 9, 12, 1, 3, 1]      # erisim dizisinin ilk on adimi

tablo, sira = {}, []                          # sanal sayfa -> fiziksel sayfa
print("sanal adres  sanal sayfa  ofset  durum         fiziksel  fiziksel adres")
for k, sanal in enumerate(ILK):
    adres = sanal * SAYFA_BOYU + (k * 23) % SAYFA_BOYU
    sayfa, ofset = divmod(adres, SAYFA_BOYU)
    if sayfa in tablo:
        durum = "yerlesik"
    else:
        durum = "sayfa hatasi"
        if len(sira) == FIZIKSEL:
            tablo.pop(sira.pop(0))
        bos = min(set(range(FIZIKSEL)) - set(tablo.values()))
        tablo[sayfa] = bos
        sira.append(sayfa)
    print(f"{adres:11d}  {sayfa:11d}  {ofset:5d}  {durum:12s}  {tablo[sayfa]:8d}"
          f"  {tablo[sayfa] * SAYFA_BOYU + ofset:14d}")
print("sayfa tablosu:", dict(sorted(tablo.items())))
```

```
sanal adres  sanal sayfa  ofset  durum         fiziksel  fiziksel adres
       1536            6      0  sayfa hatasi         0               0
       2327            9     23  sayfa hatasi         1             279
       2862           11     46  sayfa hatasi         2             558
       2373            9     69  yerlesik             1             325
       2908           11     92  yerlesik             2             604
       2419            9    115  yerlesik             1             371
       3210           12    138  sayfa hatasi         3             906
        417            1    161  sayfa hatasi         0             161
        952            3    184  sayfa hatasi         1             440
        463            1    207  yerlesik             0             207
sayfa tablosu: {1: 0, 3: 1, 11: 2, 12: 3}
```

On erişimin altısı hata verdi. Dikkat edilecek iki nokta var. Birincisi, ofset çeviride hiç
değişmiyor: 2327 adresinin ofseti 23'tü, fiziksel karşılığı 279 oldu ve 279 da 256'ya
bölündüğünde 23 kalanını veriyor. Sayfa boyu ikinin kuvveti seçildiğinde bu bölme bir bit
ayrımına iner ve bölme işlemi hiç yapılmaz. İkincisi, sekizinci satırda 6 numaralı sanal sayfa
atıldı ve yerine 1 numaralı sayfa aynı fiziksel sayfaya yerleşti; sürecin gördüğü adres
değişmedi, altındaki fiziksel yer değişti. Soyutlamanın sağladığı şey budur, ödettiği şey de
o satırdaki hatadır.

#### Sayfa Tablosunun Kendi Bedeli

Sayfa tablosu bir eşleme tutuyorsa o eşleme de bir yerde durur ve durduğu yer bellektir.
Modeldeki tablo 16 girdilidir çünkü adres uzayı 16 sanal sayfadır. Girdi sayısı adres
uzayının sayfa boyuna bölümüdür ve ikisi de ikinin kuvveti seçildiğinde bir çıkarma işlemine
iner.

- **BD7** — Bir tablo girdisinin 8 bayt tuttuğu varsayılır; girdi fiziksel sayfa numarasını,
  geçerlilik bitini ve erişim bayraklarını taşır.

```python
"""Sayfa tablosunun kendi bedeli: girdi sayisi adres uzayiyla buyur."""
GIRDI_BAYT = 8                                # bir tablo girdisi kac bayt tutuluyor
print("adres biti  sayfa boyu  girdi sayisi  tek duzeyli tablo (bayt)")
for adres_biti, sayfa_biti in ((12, 8), (22, 12), (32, 12), (48, 12)):
    girdi = 2 ** (adres_biti - sayfa_biti)
    print(f"{adres_biti:10d}  {2 ** sayfa_biti:10d}  {girdi:12d}  {girdi * GIRDI_BAYT:23d}")
```

```
adres biti  sayfa boyu  girdi sayisi  tek duzeyli tablo (bayt)
        12         256            16                      128
        22        4096          1024                     8192
        32        4096       1048576                  8388608
        48        4096   68719476736             549755813888
```

İlk satır bu dersin modelidir: 12 bitlik adres uzayı, 256 baytlık sayfa, 16 girdi, 128 bayt
tablo. Dördüncü satır soyutlamanın ölçekle nasıl davrandığını gösteriyor — 48 bitlik bir adres
uzayında tek düzeyli tablo, temsil ettiği belleğin kendisinden büyük bir yer ister ve süreç
başına bir tane gerekir. Uygulanan çözüm tabloyu katmanlara bölmektir: üst düzey tablo yalnız
kullanılan aralıkları gösterir, kullanılmayan aralıklar için alt tablo hiç ayrılmaz. Bunun
bedeli çeviri başına birden çok tablo okumasıdır, yani soyutlama bir kez daha adım ödetir.

#### Üç Yordam, Aynı Dizi

Fiziksel sayfa dolduğunda hangi sayfanın atılacağına bir **sayfa değiştirme (page
replacement)** yordamı karar verir. Üç yordam ölçülür: **ilk giren ilk çıkar**, **en az
kullanılanı atmak** ve **ileriye bakan taban** — sonuncusu bir sonraki kullanımı en uzak olan
sayfayı atar, yani geleceği bilir.

```python
"""Uc sayfa degistirme yordami ayni erisim dizisinde. Dizi 01. blogun ciktisidir."""
DIZI = [6, 9, 11, 9, 11, 9, 12, 1, 3, 1, 6, 9, 7, 9, 0, 2, 0, 5, 2, 3, 0,
        5, 2, 3, 5, 3, 8, 9, 6, 4, 13, 12, 15, 13, 8, 12, 14, 12, 7]
GETIRME = 30                                  # bir sayfa hatasi bir bekleme adimidir


def sayfa_hatasi(dizi, fiziksel, yordam="fifo"):
    """yordam: fifo | en_az | en_iyi (ileriyi goren taban)"""
    yerlesik, sira, hata = [], [], 0
    for k, s in enumerate(dizi):
        if s in yerlesik:
            if yordam == "en_az":
                sira.remove(s)
                sira.append(s)
            continue
        hata += 1
        if len(yerlesik) < fiziksel:
            yerlesik.append(s)
            sira.append(s)
            continue
        if yordam in ("fifo", "en_az"):
            cikan = sira.pop(0)
        else:
            uzaklik = {}
            for y in yerlesik:
                uzaklik[y] = dizi.index(y, k + 1) if y in dizi[k + 1:] else 10**9
            cikan = max(yerlesik, key=lambda y: uzaklik[y])
            sira.remove(cikan)
        yerlesik.remove(cikan)
        yerlesik.append(s)
        sira.append(s)
    return hata


zorunlu = len(set(DIZI))
print("erisim", len(DIZI), "| zorunlu hata:", zorunlu,
      "| tabanin suresi:", len(DIZI) + zorunlu * GETIRME)
print("fiziksel  fifo  en_az  en_iyi   fifo fazladan  fifo suresi")
for f in (2, 3, 4, 5, 6, 8, 15):
    a = sayfa_hatasi(DIZI, f, "fifo")
    b = sayfa_hatasi(DIZI, f, "en_az")
    c = sayfa_hatasi(DIZI, f, "en_iyi")
    print(f"  {f:6d}  {a:4d}  {b:5d}  {c:6d}   {a - zorunlu:13d}"
          f"  {len(DIZI) + a * GETIRME:11d}")
```

```
erisim 39 | zorunlu hata: 15 | tabanin suresi: 489
fiziksel  fifo  en_az  en_iyi   fifo fazladan  fifo suresi
       2    30     31      27              15          939
       3    28     28      23              13          879
       4    23     23      19               8          729
       5    23     22      18               8          729
       6    20     20      17               5          639
       8    19     17      15               4          609
      15    15     15      15               0          489
```

Üç sayı yan yana duruyor. **Taban:** 15 fiziksel sayfa, 15 zorunlu hata, 489 zaman birimi.
**Kurulum:** 2 fiziksel sayfa, ilk giren ilk çıkar, 30 hata. **Bedel:** 15 fazladan hata ve
939 − 489 = 450 zaman birimi. Bu iş yükünde sanal bellek soyutlaması, belleği yedi buçuk kat
küçültmenin karşılığında koşum süresini 1,92 katına çıkarıyor.

Bedelin nereden geldiği tabloda açık. Erişimlerin 30'u hatayla sonuçlanıyor, yani 39
erişimin yaklaşık dörtte üçü diske gidiyor ve makine hesap yapmaktan çok sayfa getiriyor. Bu
duruma **çırpınma (thrashing)** denir: fiziksel sayfa sayısı çalışma kümesinin altına
düştüğünde her yeni erişim, birazdan yeniden istenecek bir sayfayı atar. Çalışma kümesi
ölçümü bunu önceden söylüyordu — dört erişimlik pencerede ortalama 3,47 ayrı sayfa varken iki
fiziksel sayfayla çalışmak, pencerenin yarısını her turda yeniden getirmek demektir.

#### Daha Akıllı Yordam Her Zaman Kazandırmaz

En az kullanılanı atan yordam, ilk giren ilk çıkardan daha çok bilgi kullanır: hangi sayfaya
en son ne zaman dokunulduğunu tutar. Buna karşın iki fiziksel sayfada **31** hata veriyor, ilk
giren ilk çıkar ise **30**. Daha çok bilgi kullanan yordam bu noktada bir hata daha üretiyor.

Bu farkın nasıl okunacağı önemlidir. Ortak tanımın çözünürlük kuralı şudur: 39 erişimde bir
hatalık fark 0,0256'lık bir orandır ve **ölçülmemiş sayılır**; anlamlı fark en az üç hatadır.
Yani "en az kullanılanı atmak daha kötüdür" cümlesi de yazılamaz. Tabloya bu ölçütle
bakıldığında sonuç daha da nettir: iki yordam arasındaki fark hiçbir satırda üçe ulaşmıyor —
en büyüğü sekiz fiziksel sayfadaki 19'a karşı 17, yani iki hata. **Bu iş yükünde en az
kullanılanı atmanın üstünlüğü ölçülmemiştir.** Onu tercih eden bir gerçekleştirim, sayfa
başına bir zaman damgası tutmanın bedelini karşılıksız ödemiş olur.

Aynı ölçüt fiziksel sayfa eklemeye de uygulanır. 3'ten 4'e çıkmak ilk giren ilk çıkarı 28'den
23'e indiriyor: beş hata, anlamlı. 4'ten 5'e çıkmak ise 23'te bırakıyor: sıfır. Bir fiziksel
sayfa daha vermek burada hiçbir şey satın almıyor. 5'ten 6'ya çıkmak yeniden üç hata
kazandırıyor. Kaynak eklemenin getirisi düzgün azalmıyor, basamaklı ilerliyor ve basamağın
nerede olduğu iş yükünün erişim düzenine bağlı.

#### İleriye Bakan Taban Neden Ulaşılamaz

Üçüncü sütun her satırda diğer ikisinin altında: 2 sayfada 27, 8 sayfada 15. Sekiz fiziksel
sayfada 15 hata, zorunlu hata sayısına eşittir — yani ileriye bakan yordam sekiz sayfayla
hiçbir sayfayı iki kez getirmiyor.

Bu sütun bir yordam değil, bir **alt sınırdır**. Atılacak sayfayı seçmek için erişim dizisinin
geri kalanını okuması gerekir; o dizi, henüz çalıştırılmamış programın üreteceği adreslerdir.
Bir çizelgeleyicinin hangi süreci ne zaman kesmeye karar vereceği, bir kullanıcının hangi
veriyi isteyeceği ve bir dalın hangi yöne gideceği çeviri anında bilinmez. Alt sınırın işlevi
uygulanmak değil, ölçmektir: sekiz fiziksel sayfada ilk giren ilk çıkarın 19 hatası, alt
sınırın 15'ine dört hata uzaktır ve iyileştirme için kalan bütün pay budur.

#### Özet

- Sanal bellek, sürecin gördüğü adres uzayı ile fiziksel belleği sabit boyutlu sayfalar
  üzerinden ayırır; her erişim sanal sayfa numarası ile sayfa içi ofsete bölünüp çevrilir.
- Ortak tanımın 39 erişimlik dizisi 15 ayrı sanal sayfaya dokunuyor; taban 15 zorunlu hata ve
  489 zaman birimi, iki fiziksel sayfayla kurulum 30 hata ve 939 zaman birimi, bedel 450 zaman
  birimidir.
- Fiziksel sayfa sayısı çalışma kümesinin altına düştüğünde çırpınma başlar: 39 erişimin 30'u
  diske gider ve makine hesaptan çok sayfa getirir.
- En az kullanılanı atan yordam ile ilk giren ilk çıkar arasındaki fark hiçbir fiziksel sayfa
  sayısında üç hataya ulaşmıyor; bu iş yükünde ikisi arasında ölçülmüş bir üstünlük yoktur.
- Fiziksel sayfa eklemenin getirisi basamaklıdır: 3'ten 4'e geçiş beş hata kazandırırken 4'ten
  5'e geçiş hiçbir şey kazandırmıyor.

#### Sonraki Adım

Bu ders belleği sabit boyutlu sayfalar hâlinde dağıttı ve her isteğin bir sayfaya sığdığını
varsaydı. Bir sürecin öbeğinde durum böyle değildir: istekler farklı boyutlardadır, sırasız
gelir ve sırasız bırakılır. Sonraki ders aynı makinede öbek yönetimini modeller ve iki ayırıcı
yordamını aynı olay dizisinde karşılaştırır — biri bir isteği reddederken diğeri hepsini
yerleştiriyor, ama geriye kullanılamaz bir kırıntı bırakıyor.

### Bellek Ayırma

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/bellek-ayirma

Öbekte değişken boyutlu isteklerin yerleştirilmesi: ilk uyan ile en iyi uyan yordamlarının aynı olay dizisindeki farkı, dış parçalanmanın bir isteği reddettiği an ve geriye kalan kullanılamaz kırıntı.

Sanal bellek dersinde her istek aynı boyuttaydı: bir sayfa. Sabit boyut, yerleştirme sorununu
ortadan kaldırır — boş bir fiziksel sayfa varsa her sanal sayfa oraya sığar ve hangi boşluğun
seçildiği sonucu değiştirmez. Bir sürecin öbeğinde durum böyle değildir. İstekler farklı
boyutlardadır, sırasız gelir, sırasız bırakılır ve hangi boşluğa yerleştirildikleri sonraki
isteklerin yerleşip yerleşmeyeceğini belirler.

Bilgisayarlar Nasıl Çalışır kursundaki Bellek Düzeni dersi öbeği, ayırıcıyı ve parçalanmayı
tanımlamıştı; bu ders o tanımları tekrarlamaz. Eklediği tek şey ölçümdür: iki yerleştirme
yordamı aynı olay dizisinde koşturulur ve aralarındaki fark yerleşen istek, boşta kalan bayt
ve kullanılamaz kırıntı cinsinden sayılır.

#### Taban, Kurulum ve Ölçülecek Olan

Soyutlamasız taban bir ayırıcı değildir: öbekte tek bir sınır değişkeni tutulur, her istek
sınırı kendi boyutu kadar ileri iter ve bırakma diye bir işlem yoktur. Bu düzenin yönetim
maliyeti bir toplama işlemidir; karşılığında bırakılan bellek geri alınmaz.

Kurulum, bırakmayı ve yeniden kullanmayı ekler. Öbek bölümlere ayrılır, her bölüm ya bir ada
aittir ya boştur, bırakılan bölüm boşa döner ve komşu boşluklar birleştirilir. Ölçülecek olan
şudur: bu defterin tutulması kaç isteği kurtarıyor ve geriye ne bırakıyor.

- **BD8** — Öbek 1024 bayttır ve tek parçadır. Ayırıcı istenen bayt sayısını tam olarak
  verir; hizalama ya da başlık payı yoktur, dolayısıyla bu derste iç parçalanma sıfırdır.
- **BD9** — Olay dizisi ortak tanımdan gelir: altı ayırma, iki bırakma, iki ayırma daha.
  Toplam istenen 1254 bayttır, yani öbekten büyüktür; bırakma olmadan hepsi yerleşemez.
- **BD10** — İki yerleştirme yordamı ölçülür. **İlk uyan (first fit)** listedeki ilk yeterli
  bölümü seçer; **en iyi uyan (best fit)** yeterli bölümlerin en küçüğünü seçer.
- **BD11** — Bırakma anında yalnız **komşu** boşluklar birleşir; uzaktaki iki boşluk ayrı
  kalır. Taşıma ya da sıkıştırma yapılmaz.

```python
"""M01/K05 ortak tanim (kesit): ayirici , dis parcalanma ve uc uca taban."""
OBEK = 1024
OLAY = [("A", "a", 200), ("A", "b", 150), ("A", "c", 200), ("A", "d", 100),
        ("A", "e", 200), ("A", "f", 174),
        ("B", "b"), ("B", "d"),                  # 150 ve 100 baytlik iki bosluk
        ("A", "g", 90), ("A", "h", 140)]


def ayir(olaylar, boyut=OBEK, yordam="ilk_uyan"):
    """olaylar: ("A", ad, boyut) ayir , ("B", ad) birak."""
    bolum = [{"ad": None, "bas": 0, "boy": boyut}]      # ad None ise bos
    yerlesen = reddedilen = 0
    for olay in olaylar:
        if olay[0] == "A":
            _, ad, iste = olay
            adaylar = [b for b in bolum if b["ad"] is None and b["boy"] >= iste]
            if not adaylar:
                reddedilen += 1
                continue
            sec = adaylar[0] if yordam == "ilk_uyan" else min(adaylar, key=lambda b: b["boy"])
            k = bolum.index(sec)
            kalan = sec["boy"] - iste
            bolum[k] = {"ad": ad, "bas": sec["bas"], "boy": iste}
            if kalan:
                bolum.insert(k + 1, {"ad": None, "bas": sec["bas"] + iste, "boy": kalan})
            yerlesen += 1
        else:
            for b in bolum:
                if b["ad"] == olay[1]:
                    b["ad"] = None
            k = 0                                        # komsu bosluklar birlesir
            while k < len(bolum) - 1:
                if bolum[k]["ad"] is None and bolum[k + 1]["ad"] is None:
                    bolum[k]["boy"] += bolum[k + 1]["boy"]
                    bolum.pop(k + 1)
                else:
                    k += 1
    bosluk = [b["boy"] for b in bolum if b["ad"] is None]
    return {"yerlesen": yerlesen, "reddedilen": reddedilen, "bos": sum(bosluk),
            "en_buyuk_bosluk": max(bosluk) if bosluk else 0, "parca": len(bosluk),
            "yerlesim": [(b["ad"] or "-", b["bas"], b["boy"]) for b in bolum]}


def uc_uca(olaylar, boyut=OBEK):
    """Soyutlamasiz taban: birakma yok , yalnizca ileri kayan bir sinir."""
    sinir, yerlesen, reddedilen = 0, 0, 0
    for olay in olaylar:
        if olay[0] == "A":
            if sinir + olay[2] <= boyut:
                sinir, yerlesen = sinir + olay[2], yerlesen + 1
            else:
                reddedilen += 1
    return yerlesen, reddedilen


def uretec(tohum):
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def uzun_dizi(tohum, adet=200):
    """Uzun olay dizisi: her adimda ya ayirma ya rastgele bir birakma."""
    r = uretec(tohum)
    olaylar, canli, sayac = [], [], 0
    for _ in range(adet):
        if canli and r(10) < 5:
            olaylar.append(("B", canli.pop(r(len(canli)))))
        else:
            ad = f"n{sayac}"
            sayac += 1
            olaylar.append(("A", ad, 16 + r(8) * 16))
            canli.append(ad)
    return olaylar


print("obek", OBEK, "bayt | istek", sum(1 for o in OLAY if o[0] == "A"),
      "| toplam istenen", sum(o[2] for o in OLAY if o[0] == "A"))
print("taban (uc uca , birakma yok): yerlesen %d reddedilen %d" % uc_uca(OLAY))
print("yordam        yerlesen  reddedilen  bos  en buyuk bosluk  parca")
for y in ("ilk_uyan", "en_iyi_uyan"):
    a = ayir(OLAY, OBEK, y)
    print(f"  {y:12s} {a['yerlesen']:5d} {a['reddedilen']:10d} {a['bos']:5d}"
          f" {a['en_buyuk_bosluk']:14d} {a['parca']:6d}")
    print("   ", " ".join(f"{ad}@{bas}+{boy}" for ad, bas, boy in a["yerlesim"]))
print()
print("uzun dizi (200 olay)   ayirma  ilk uyan  en iyi uyan")
for tohum in (20260218, 20260219):
    U = uzun_dizi(tohum)
    istek = sum(1 for o in U if o[0] == "A")
    i = ayir(U, OBEK, "ilk_uyan")
    e = ayir(U, OBEK, "en_iyi_uyan")
    print(f"  tohum {tohum}    {istek:4d}  {i['yerlesen']:8d}  {e['yerlesen']:11d}")
```

```
obek 1024 bayt | istek 8 | toplam istenen 1254
taban (uc uca , birakma yok): yerlesen 6 reddedilen 2
yordam        yerlesen  reddedilen  bos  en buyuk bosluk  parca
  ilk_uyan         7          1   160            100      2
    a@0+200 g@200+90 -@290+60 c@350+200 -@550+100 e@650+200 f@850+174
  en_iyi_uyan      8          0    20             10      2
    a@0+200 h@200+140 -@340+10 c@350+200 g@550+90 -@640+10 e@650+200 f@850+174

uzun dizi (200 olay)   ayirma  ilk uyan  en iyi uyan
  tohum 20260218     119        96           98
  tohum 20260219     114       100           99
```

Üç sayı yan yana. **Taban:** uç uca ayırma, 6 yerleşen, 2 reddedilen ve tek bir sınır
değişkeni. **Kurulum:** bölüm listesi tutan ayırıcı, en iyi uyan ile 8 yerleşen. **Bedel:**
her istekte taranan bir bölüm listesi, sekiz bölüme bölünmüş bir öbek ve geriye kalan iki
boşluk.

Bırakmanın kazandırdığı açık: taban 1024 baytı bir kez dağıtıp bitiriyor, ayırıcı ise
bırakılan 250 baytı yeniden kullanarak iki isteği daha karşılayabiliyor. Kazancın tamamı
alınmıyor ama — ilk uyan yordamı sekiz isteğin yalnız yedisini yerleştiriyor.

#### Dış Parçalanma Bir Red Sebebidir

`b` ve `d` bırakıldığında öbekte 150 ve 100 baytlık iki boşluk oluşuyor. İkisi komşu
olmadığı için birleştirme hiç devreye girmiyor; toplam 250 bayt boş, ama tek parça hâlinde en
fazla 150 bayt var. Bu duruma **dış parçalanma (external fragmentation)** denir: boş belleğin
toplamı yeterlidir, tek bir parça hâlinde yeterli değildir.

Sonucu son iki istekte görünüyor. İlk uyan, 90 baytlık `g` isteğini listedeki ilk yeterli
boşluğa — 150 baytlığa — koyuyor ve geriye 60 bayt bırakıyor. Ardından gelen 140 baytlık `h`
isteği için elde 60 ve 100 baytlık iki boşluk kalıyor; toplam 160, yani yeter, ama hiçbiri tek
başına yetmez.

```python
"""Dis parcalanma: bos toplam yeter , tek parca yetmez. Yerlesimler 01. blogun ciktisidir."""
YERLESIM = {
    "ilk_uyan": [("a", 200), ("g", 90), ("-", 60), ("c", 200),
                 ("-", 100), ("e", 200), ("f", 174)],
    "en_iyi_uyan": [("a", 200), ("h", 140), ("-", 10), ("c", 200), ("g", 90),
                    ("-", 10), ("e", 200), ("f", 174)],
}
for ad, yerlesim in YERLESIM.items():
    bosluk = sorted((b for n, b in yerlesim if n == "-"), reverse=True)
    bos, en_buyuk = sum(bosluk), bosluk[0]
    print(f"{ad:12s} bos {bos:4d}  en buyuk {en_buyuk:4d}  parca {len(bosluk)}"
          f"  dis parcalanma orani {1 - en_buyuk / bos:.4f}")
    for iste in (10, 60, 90, 140):
        print(f"    {iste:4d} baytlik istek: bos yeterli {bos >= iste}"
              f" , tek parca yeterli {en_buyuk >= iste}")
```

```
ilk_uyan     bos  160  en buyuk  100  parca 2  dis parcalanma orani 0.3750
      10 baytlik istek: bos yeterli True , tek parca yeterli True
      60 baytlik istek: bos yeterli True , tek parca yeterli True
      90 baytlik istek: bos yeterli True , tek parca yeterli True
     140 baytlik istek: bos yeterli True , tek parca yeterli False
en_iyi_uyan  bos   20  en buyuk   10  parca 2  dis parcalanma orani 0.5000
      10 baytlik istek: bos yeterli True , tek parca yeterli True
      60 baytlik istek: bos yeterli False , tek parca yeterli False
      90 baytlik istek: bos yeterli False , tek parca yeterli False
     140 baytlik istek: bos yeterli False , tek parca yeterli False
```

İlk uyanın reddi bir bellek yetersizliği değildir; 160 bayt boş dururken 140 baytlık istek
geri çevriliyor. Dış parçalanma oranı — boş belleğin en büyük parçanın dışında kalan payı —
burada 0,3750'dir.

En iyi uyan aynı iki isteği de yerleştiriyor. 90 baytlık `g` isteğini 100 baytlık boşluğa
koyup 10 bayt bırakıyor, 140 baytlık `h` isteğini 150 baytlık boşluğa koyup yine 10 bayt
bırakıyor. Sekiz istek de yerleşti, reddedilen yok. Fakat geriye kalan iki boşluk 10'ar
bayttır ve dış parçalanma oranı 0,5000'e çıkmıştır: 20 bayt boş görünüyor, 11 baytlık tek bir
istek bile karşılanamıyor. **Kullanılamaz bellek, kullanılmayan bellekten farklı bir
kalemdir** ve yalnız boş bayt toplamına bakan bir ölçüt onu göstermez.

#### En İyi Uyan Her Zaman Kazandırmaz

Sekiz istekten yedisi ile sekizi arasındaki fark, en iyi uyanı genel bir kural gibi okumaya
yol açar. Çıktının son bloğu bunu sınıyor: aynı ayırıcı, 200 olaylık iki ayrı dizide.

İlk iş yükünde 119 ayırma isteğinden ilk uyan 96'sını, en iyi uyan 98'ini yerleştiriyor. Yani
en iyi uyan iki istek önde. İkinci iş yükünde 114 istekten ilk uyan 100'ünü, en iyi uyan
99'unu yerleştiriyor; bu kez ilk uyan bir istek önde. **Sıralama iş yüküyle birlikte
dönüyor.** Farkların büyüklüğü de küçüktür: iki ve bir istek, yaklaşık yüz isteğin üzerinde
yüzde iki mertebesinde bir oynama.

Bu iki gözlem birlikte tek bir sonuç verir: **bu ölçümde iki yordam arasında bir üstünlük
kurulmamıştır.** Kurulan tek şey, sekiz olaylık dizide en iyi uyanın bir isteği daha
yerleştirdiği ve karşılığında daha kullanılamaz bir kalıntı bıraktığıdır. Bir yordamın
seçimi, iş yükünün boyut dağılımına ve bırakma düzenine bağlıdır; tek dizide ölçülmüş bir
sıralama ölçülmemiş sayılır.

Bir de bu modelin **saymadığı** bir bedel var. Buradaki gerçekleştirim her istekte bölüm
listesinin tamamını tarıyor, bu yüzden iki yordamın arama maliyeti eşit çıkıyor. Gerçek bir
ilk uyan gerçekleştirimi ilk yeterli bölümü bulunca durabilir; en iyi uyan duramaz, çünkü daha
küçük bir yeterli bölüm olup olmadığını ancak listeyi bitirince bilir. Bu fark bu derste
ölçülmedi ve ölçülmediği için de bir üstünlük gerekçesi olarak yazılmıyor.

#### Sıkıştırma Neden Bedava Değil

Dış parçalanmanın kesin bir çözümü vardır: yerleşik bölümleri öbeğin başına doğru kaydırıp
bütün boşlukları tek parçada toplamak. Buna **sıkıştırma (compaction)** denir ve ilk uyanın
reddettiği isteği kurtarır.

- **BD12** — Sıkıştırma yalnız bir ölçüm olarak yapılır; bir bloğun taşınması, o bloğun
  baytlarının yeni yerine kopyalanması sayılır.

```python
"""Sikistirmanin bedeli: bosluklari tek parcaya toplamak kac bayt tasitir."""
YERLESIM = [("a", 0, 200), ("g", 200, 90), ("-", 290, 60), ("c", 350, 200),
            ("-", 550, 100), ("e", 650, 200), ("f", 850, 174)]     # ilk uyan sonucu

sinir, tasinan, tasinan_blok = 0, 0, 0
for ad, bas, boy in YERLESIM:
    if ad == "-":
        continue
    if bas != sinir:
        tasinan += boy
        tasinan_blok += 1
    sinir += boy
print("yerlesik bayt:", sinir, "| tek parcaya inen bosluk:", 1024 - sinir)
print("tasinan blok:", tasinan_blok, "| tasinan bayt:", tasinan)
print("140 baytlik istek sikistirmadan sonra yerlesir mi:", 1024 - sinir >= 140)
```

```
yerlesik bayt: 864 | tek parcaya inen bosluk: 160
tasinan blok: 3 | tasinan bayt: 574
140 baytlik istek sikistirmadan sonra yerlesir mi: True
```

Reddedilen istek kurtuluyor, ama bedeli 574 baytın kopyalanmasıdır — öbekte yerleşik duran
864 baytın üçte ikisi. Bu, ayırmanın kendisiyle kıyaslanamayacak kadar büyük bir iştir ve tek
bir istek için ödenir.

İkinci ve daha ağır bedel kopyalama değildir. Taşınan her bloğun adresi değişir, dolayısıyla o
bloğu gösteren her başvurunun da değişmesi gerekir. Adreslerin doğrudan sayı olarak tutulduğu
bir düzende hangi sayının bir işaretçi hangisinin veri olduğu bilinemez, bu yüzden sıkıştırma
uygulanamaz. Uygulanabildiği düzenlerde ise bloğa doğrudan değil bir dolaylı tablo üzerinden
erişilir; o zaman taşıma tabloda tek satır değiştirmeye iner, ama bu kez **her erişim** bir
tablo okuması daha ödemeye başlar. Soyutlamanın ödettiği adım yine ortadan kalkmıyor, yalnız
yer değiştiriyor.

#### İç Parçalanma Buraya Girmiyor

Ayırıcı istenen bayt sayısını tam veriyor: 90 istendiğinde 90 bayt ayrılıyor. Bu yüzden bu
derste iç parçalanma sıfırdır ve tabloda böyle bir sütun yoktur. Sabit boyutlu birimlerle
çalışan her düzende ise vardır — sanal bellek dersinde bir sayfanın son baytına kadar
dolmaması iç parçalanmadır, dosya sisteminde bir bloğun yarısının boş kalması da öyle. Bu iki
parçalanma birbirinin karşılığıdır: sabit boyut dış parçalanmayı ortadan kaldırır ve yerine iç
parçalanma koyar; değişken boyut iç parçalanmayı ortadan kaldırır ve yerine dış parçalanma
koyar. Dördüncü ders bu ödünleşimin diğer ucunu bayt cinsinden ölçecek.

#### Özet

- Uç uca ayıran taban 8 isteğin 6'sını yerleştiriyor ve bırakmayı hiç kullanmıyor; bölüm
  listesi tutan ayırıcı bırakılan 250 baytı yeniden kullanarak bir ya da iki isteği daha
  karşılıyor.
- İlk uyan 7 istek yerleştirip birini reddediyor: 160 bayt boş dururken 140 baytlık istek
  geri çevriliyor, çünkü en büyük tek parça 100 bayttır. Dış parçalanma budur.
- En iyi uyan 8 isteğin hepsini yerleştiriyor ama geriye 10'ar baytlık iki boşluk bırakıyor;
  dış parçalanma oranı 0,3750'den 0,5000'e çıkıyor ve kalan 20 bayt kullanılamaz.
- İki yüz olaylık iki ayrı dizide sıralama dönüyor: ilk iş yükünde en iyi uyan iki istek önde,
  ikincisinde ilk uyan bir istek önde. İki yordam arasında bir üstünlük ölçülmemiştir.
- Bu modelde iki yordam da bölüm listesinin tamamını tarıyor; arama maliyeti farkı ölçülmedi
  ve gerekçe olarak kullanılmıyor.

#### Sonraki Adım

Bu derste bırakma işlemini programın kendisi yaptı: `b` ve `d` bırakıldı, yerleri geri alındı.
Bırakmayı unutan bir program belleği sızdırır, erken bırakan bir program serbest bırakılmış
adresi kullanır. Sonraki ders bu kararı programdan alıp çalışma zamanına devreden düzeni
modeller: erişilebilirlik ölçütüyle çalışan bir toplayıcı, 40 nesnenin hangilerini yaşattığını
belirler ve bunun bedeli bir duraklatmadır. Ölçülecek soru şudur — duraklatmanın uzunluğu
toplanan çöple mi, yoksa yaşayan nesneyle mi orantılıdır.

### Çöp Toplama

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/cop-toplama

Bırakma kararının çalışma zamanına devredilmesi ve bunun bedeli: 40 nesnelik bir çizgede erişilebilirlik taraması, duraklatmanın yaşayan nesneyle orantılı oluşu ve toplanan çöp arttıkça duraklatmanın küçülmesi.

Önceki derste bırakma kararını programın kendisi verdi: `b` ve `d` bırakıldı ve yerleri geri
alındı. Bu kararın iki yanlış biçimi vardır. Bırakılmayan bir blok, program bitene kadar
kullanılamaz kalır. Bırakıldıktan sonra kullanılan bir adres ise başka bir isteğe verilmiş
belleği okur ya da yazar. İkisi de kaynak metinde görünmez; ancak koşum sırasında ortaya
çıkar.

Bu ders kararı programdan alıp çalışma zamanına devreden düzeni modeller. Devir bir ölçüt
gerektirir — hangi nesnenin hâlâ gerekli olduğuna bakılarak değil, hangisine **ulaşılabildiğine**
bakılarak karar verilir. İşaretle ve süpür yönteminin kendisi ile erişilebilirlik ölçütü bu
katalogda daha önce, **Eşzamansız JavaScript ve Çalışma Zamanı** kursunun Bellek ve Başarım
konusundaki Çöp Toplama dersinde kurulmuştur ve burada tekrarlanmaz. Bu dersin eklediği tek şey ölçümdür:
devir kaç adıma mal oluyor ve o adım sayısı neye bağlı.

#### Taban, Kurulum ve Bedelin Adı

Soyutlamasız taban elle bırakmadır. Toplayıcı yoktur, tarama yoktur, duraklatma yoktur; bir
bloğun ömrü program metninde yazılıdır. Bedeli koşumda değil, doğrulukta ödenir.

Kurulum bir **izlemeli çöp toplayıcıdır**. Kök kümesinden başlanır, başvurular izlenir,
ulaşılan her nesne işaretlenir; işaretlenmemiş nesnelerin yeri geri alınır. Bedelin adı
**duraklatmadır (pause)** ve bu derste duraklatma bir süre değil, bir adım sayısıdır: kaç
nesne tarandı.

- **BD13** — Nesne çizgesi ortak tanımın modelidir: 40 nesne, tohum 20260218, kök kümesi ilk
  üç nesne. Bağlantılar üreteçten gelir ve her koşumda aynıdır.
- **BD14** — Bir nesnenin taranması bir adımdır; nesnenin boyutu ve taşıdığı bağlantı sayısı
  adım sayısını değiştirmez.
- **BD15** — Toplama, program tamamen durdurulmuşken tek parça hâlinde yapılır. Artımlı ya da
  eşzamanlı toplama bu modelde yoktur.
- **BD16** — Süpürme, işaretlenmemiş nesnelerin yerini geri almak için bütün nesne listesini
  bir kez dolaşır; bu da adım başına bir nesnedir.

```python
"""M01/K05 ortak tanim (kesit): nesne cizgesi , erisilebilirlik ve isaretle-supur."""
TOHUM = 20260218


def uretec(tohum):
    d = tohum

    def sonraki(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return sonraki


def nesne_cizgesi(tohum=TOHUM, n=40, kok=3):
    r = uretec(tohum)
    baglanti = {i: [] for i in range(n)}
    for i in range(n):
        for _ in range(r(3)):
            h = r(n)
            if h != i and h not in baglanti[i]:
                baglanti[i].append(h)
    return baglanti, list(range(kok))


def isaretle_supur(baglanti, kokler):
    goruldu, yigit, tarama = set(), list(kokler), 0
    while yigit:
        d = yigit.pop()
        if d in goruldu:
            continue
        goruldu.add(d)
        tarama += 1
        for h in baglanti[d]:
            yigit.append(h)
    return {"erisilebilir": len(goruldu), "toplanan": len(baglanti) - len(goruldu),
            "tarama": tarama}


G, K = nesne_cizgesi()
print("nesne:", len(G), "| baglanti:", sum(len(v) for v in G.values()), "| kok:", len(K))
print("taban olcumu:", isaretle_supur(G, K))
print()
print("kok  erisilebilir  toplanan  isaretleme  supurme  toplam is  toplanan basina")
for kok in (1, 2, 3, 4, 6, 8, 12):
    g, k = nesne_cizgesi(kok=kok)
    r = isaretle_supur(g, k)
    toplam = r["tarama"] + len(g)
    print(f"{kok:3d}  {r['erisilebilir']:12d}  {r['toplanan']:8d}  {r['tarama']:10d}"
          f"  {len(g):7d}  {toplam:9d}  {toplam / r['toplanan']:15.2f}")
```

```
nesne: 40 | baglanti: 44 | kok: 3
taban olcumu: {'erisilebilir': 17, 'toplanan': 23, 'tarama': 17}

kok  erisilebilir  toplanan  isaretleme  supurme  toplam is  toplanan basina
  1             3        37           3       40         43             1.16
  2             7        33           7       40         47             1.42
  3            17        23          17       40         57             2.48
  4            19        21          19       40         59             2.81
  6            21        19          21       40         61             3.21
  8            23        17          23       40         63             3.71
 12            25        15          25       40         65             4.33
```

Üç sayı yan yana. **Taban:** elle bırakma, 0 tarama adımı, 0 duraklatma. **Kurulum:** üç
köklü izlemeli toplayıcı, 40 nesnenin **17'si erişilebilir**, 23'ü toplanıyor. **Bedel:**
**17 adımlık duraklatma** ve onu izleyen 40 adımlık süpürme geçişi, toplam 57 adım.

Erişilebilirliğin bir gezinme olduğuna dikkat edin: kök kümesinden başlayıp bağlantıları
izleyen, gördüğünü bir daha ziyaret etmeyen bir dolaşma. Veri Yapıları kursundaki çizge
gezinmesi burada yeniden tanımlanmıyor, doğrudan çağrılıyor; tek yenilik, ziyaret sayısının
bir maliyet olarak sayılmasıdır.

Kök kümesinin ne olduğu da bu kursun kurduğu düzenden okunur. Bellek Düzeni dersindeki adres
uzayında toplayıcının "başlangıç noktası" sayacağı yerler bellidir: her iş parçacığının kendi
yığıtında duran yerel değişkenler, başlatılmış ve başlatılmamış veri bölmelerindeki küresel
değişkenler ve o anda yazmaçlarda tutulan başvurular. Bu üçü, sürecin kendi kodunun elle
verdiği bir liste değildir; adres uzayının yapısından okunur. Modelde aynı küme
`kok` parametresidir ve büyüklüğü doğrudan bir maliyet kalemidir — kök sayısı arttıkça
gezinme derinleşir. Kökler dışındaki hiçbir yerden başlanmaz; bu yüzden çizgede birbirine
başvuran ama kökten kesilmiş bir nesne kümesi, kaç bağlantı taşırsa taşısın toplanır.

#### Duraklatma Neyle Orantılı

Tablonun ilk iki sütunu bir arada okunduğunda beklenmedik bir düzen çıkıyor. Kök sayısı 1
iken 37 nesne toplanıyor ve duraklatma 3 adım. Kök sayısı 12 iken yalnız 15 nesne toplanıyor
ve duraklatma 25 adım. **Toplanan çöp azaldıkça duraklatma büyüyor.**

Bu ters ilişki bir tesadüf değildir, yöntemin tanımından çıkar: işaretleme yalnız
erişilebilen nesneleri ziyaret eder. Ölü bir nesne hiç ziyaret edilmez — ona zaten
ulaşılamıyordur. Aşağıdaki kurgu, ortak tanımın çizgesinden bağımsız olarak aynı ilişkiyi
yalıtıyor: yaşayan nesne sayısı sabit tutulup ölü nesne sayısı otuz kat büyütülüyor.

```python
"""Duraklatma neyle orantili: yasayan nesne sabit , cop degisken."""


def isaretle_supur(baglanti, kokler):
    goruldu, yigit, tarama = set(), list(kokler), 0
    while yigit:
        d = yigit.pop()
        if d in goruldu:
            continue
        goruldu.add(d)
        tarama += 1
        for h in baglanti[d]:
            yigit.append(h)
    return {"erisilebilir": len(goruldu), "toplanan": len(baglanti) - len(goruldu),
            "tarama": tarama}


def kurgu(yasayan, olu):
    """Yasayanlar kokten zincirle erisilir , olu nesneler kendi aralarinda baglidir."""
    baglanti = {i: [] for i in range(yasayan + olu)}
    for i in range(yasayan - 1):
        baglanti[i].append(i + 1)
    for j in range(yasayan, yasayan + olu):
        baglanti[j].append(yasayan + (j - yasayan + 1) % olu)
    return baglanti, [0]


print("yasayan  olu  toplam  isaretleme  supurme  toplanan")
for yasayan, olu in ((8, 32), (8, 152), (8, 992), (32, 8), (200, 8)):
    g, k = kurgu(yasayan, olu)
    r = isaretle_supur(g, k)
    print(f"  {yasayan:5d}  {olu:4d}  {len(g):6d}  {r['tarama']:10d}"
          f"  {len(g):7d}  {r['toplanan']:8d}")
```

```
yasayan  olu  toplam  isaretleme  supurme  toplanan
      8    32      40           8       40        32
      8   152     160           8      160       152
      8   992    1000           8     1000       992
     32     8      40          32       40         8
    200     8     208         200      208         8
```

İlk üç satırda yaşayan nesne sayısı 8'de sabit ve ölü nesne sayısı 32'den 992'ye çıkıyor;
işaretleme adımı **8'de kalıyor**. Son iki satırda ölü nesne sayısı 8'de sabit ve yaşayan
nesne sayısı 32'den 200'e çıkıyor; işaretleme adımı **32'den 200'e** çıkıyor. Dersin cümlesi
budur: **duraklatmanın uzunluğu toplanan çöple değil, yaşayan nesneyle orantılıdır.**

Bunun pratik sonucu doğrudandır. Çok sayıda kısa ömürlü nesne üreten bir program toplayıcıya
pahalıya mal olmaz; bu nesneler hiç ziyaret edilmez. Pahalı olan, uzun süre yaşayan büyük bir
nesne yapısıdır — her toplamada baştan sona yeniden taranır ve hiçbir zaman toplanmaz.

#### Süpürmenin Payı

İşaretleme yaşayanla orantılıysa, süpürme de toplamla orantılıdır: işaretlenmemiş nesnelerin
yerini geri almak için listenin tamamı bir kez dolaşılır. Tabloda süpürme sütunu her satırda
40'tır, çünkü nesne sayısı değişmiyor.

Toplam iş bu ikisinin toplamıdır ve son sütun onu toplanan nesne başına indirger. Kök sayısı
1 iken toplanan nesne başına 1,16 adım harcanıyor, kök sayısı 12 iken 4,33 adım. Aradaki fark
3,7 kattır ve tamamı yaşayan nesne sayısındaki artıştan gelir. Toplayıcının verimi, öbekte ne
kadar çöp olduğuna bağlıdır — çöp ne kadar çoksa toplama o kadar ucuzdur.

Süpürme adımının bu modeldeki payı da not edilmelidir: üç köklü koşumda toplam işin 40'ı
süpürme, 17'si işaretleme. Yani duraklatmanın büyük parçası taramadan değil, listeyi
dolaşmaktan geliyor. Yeri geri alırken nesneleri yaşayanların yanına kopyalayan bir toplayıcı
bu geçişi tamamen kaldırır ve yalnız 17 adım öder; karşılığında yaşayan her nesneyi taşımak ve
ona giden her başvuruyu güncellemek zorunda kalır. Bu ödünleşim, önceki dersteki sıkıştırmanın
aynısıdır.

Süpürmenin bir de görünmeyen ucu vardır. Yeri geri alınan 23 nesnenin baytları öbeğe geri
döner, yani önceki dersin ayırıcısına teslim edilir; oradan sonra dış parçalanma kuralları
işlemeye başlar. Toplayıcı hangi nesnenin öleceğini belirler, nereye hangi boyutta boşluk
açılacağını ise ölüm sırası belirler. Bu yüzden çöp toplama, bir ayırıcının yerini almaz;
onun üzerine kurulur ve önceki derste ölçülen kırıntı sorununu miras alır. Kopyalayan bir
toplayıcının taşımayı göze almasının nedeni de budur: taşıdığı nesneleri uç uca dizerek
ayırmayı yeniden tek bir sınır kaydırmasına indirger.

#### Duraklatmayı Bölmek

Tek parça hâlinde 17 adım süren bir duraklatma, işaretlemeyi parçalara bölüp aralarında
programa sıra vererek küçültülebilir. Bedelsiz değildir: iki parça arasında program çalıştığı
için nesne çizgesi değişebilir, dolayısıyla değişen bölgenin yeniden taranması gerekir.

- **BD17** — Her kesintiden sonra 2 nesnenin yeniden taranması gerekir. Bu sayı modelin
  parametresidir; gerçek bir gerçekleştirimde programın kesinti sırasında ne kadar başvuru
  değiştirdiğine bağlı olurdu.

```python
"""Artimli toplama: duraklatmayi bolmenin bedeli. Yasayan nesne 17 , toplam 40."""
YASAYAN = 17          # 01. blogun olctugu erisilebilir nesne sayisi
TOPLAM = 40
YENIDEN = 2           # her kesintiden sonra yeniden taranmasi gereken nesne

print("parca  duraklatma sayisi  en uzun duraklatma  isaretleme isi  toplam is")
for parca in (17, 8, 4, 2, 1):
    sayi = -(-YASAYAN // parca)
    isaretleme = YASAYAN + (sayi - 1) * YENIDEN
    print(f"{parca:5d}  {sayi:17d}  {min(parca, YASAYAN):18d}"
          f"  {isaretleme:14d}  {isaretleme + TOPLAM:9d}")
```

```
parca  duraklatma sayisi  en uzun duraklatma  isaretleme isi  toplam is
   17                  1                  17              17         57
    8                  3                   8              21         61
    4                  5                   4              25         65
    2                  9                   2              33         73
    1                 17                   1              49         89
```

En uzun duraklatma 17 adımdan 1 adıma iniyor, toplam iş 57 adımdan **89 adıma** çıkıyor.
Kazanılan şey toplam maliyet değil, maliyetin dağılımıdır: bir kerede 17 adım durmak yerine
17 kez birer adım duruluyor ve program aralarda ilerliyor. Yanıt süresine bakan bir ölçüt bunu
iyileşme sayar; toplam işe bakan bir ölçüt 1,56 kat kötüleşme sayar. **İkisi de doğrudur ve
hangisinin önemli olduğu ölçüte bağlıdır** — bu, kursun ikinci iddiasının bu derste ödendiği
yerdir.

#### Ne Sıklıkta Toplanmalı

Toplayıcının ne zaman çalışacağı da bir karardır ve iki maliyeti karşı karşıya getirir. Erken
toplamak öbeği küçük tutar ama toplayıcıyı sık çalıştırır; geç toplamak toplayıcıyı seyrek
çalıştırır ama öbeğin en yüksek noktasını yükseltir.

- **BD18** — Program 200 nesne üretiyor ve her nesne üretildikten 30 adım sonra erişilemez
  hâle geliyor. Ömür sabittir; gerçek bir programda dağılım olurdu.

```python
"""Toplama sikligi: bellek tepesi ile toplayici isi arasindaki odunlesim."""
NESNE = 200           # kac nesne uretiliyor
OMUR = 30             # bir nesne uretildikten kac adim sonra erisilemez oluyor

print("her N ayirmada bir  toplama  isaretleme isi  supurme isi  en yuksek yerlesik")
for n in (10, 25, 50, 100, 200):
    yerlesik, tepe, toplama, isaretleme, supurme = [], 0, 0, 0, 0
    for t in range(NESNE):
        yerlesik.append(t)
        tepe = max(tepe, len(yerlesik))
        if (t + 1) % n == 0:
            yasayan = [i for i in yerlesik if t < i + OMUR]
            toplama += 1
            isaretleme += len(yasayan)
            supurme += len(yerlesik)
            yerlesik = yasayan
    print(f"{n:18d}  {toplama:7d}  {isaretleme:14d}  {supurme:11d}  {tepe:18d}")
```

```
her N ayirmada bir  toplama  isaretleme isi  supurme isi  en yuksek yerlesik
                10       20             570          740                  40
                25        8             235          405                  55
                50        4             120          290                  80
               100        2              60          230                 130
               200        1              30          200                 200
```

On ayırmada bir toplayan kurulum öbeği en fazla 40 nesnede tutuyor ve toplayıcıya 1310 adım
ödetiyor. Yalnız sonda toplayan kurulum 230 adım ödüyor ama öbek 200 nesneye kadar büyüyor.
Beş kat düşük bellek tepesi, 5,7 kat toplayıcı işi karşılığında alınıyor. Bu iki uç arasındaki
her nokta geçerli bir seçimdir ve seçimi yapan şey, makinede belleğin mi yoksa işlemcinin mi
kıt olduğudur.

#### Erişilebilir Olan Toplanmaz

Ölçüt "gerekli mi" değil, "ulaşılabiliyor mu" sorusudur ve bu ayrım toplayıcının çözmediği
sorunu tanımlar. Kök sayısı 3'ten 12'ye çıktığında toplanan nesne 23'ten 15'e düşüyor;
çizgede hiçbir şey değişmedi, yalnız köklerden ulaşılan bölge büyüdü. Bir programda bu, artık
kullanılmayan bir nesneyi bir listede ya da bir eşlemede tutmaya karşılık gelir: nesne
gereksizdir, erişilebilirdir ve toplanmaz.

Bu yüzden çöp toplama bellek sızıntısını ortadan kaldırmaz, sızıntının **biçimini**
değiştirir. Elle bırakmada sızıntı unutulan bir bırakma çağrısıdır; izlemeli toplamada
sızıntı, kökten kesilmeyen bir başvurudur. İkisinde de sızıntı bir program kusurudur ve
tanısı programda aranır.

#### Özet

- İzlemeli toplayıcı, 40 nesnelik çizgede üç kökten 17 nesneye ulaşıyor ve 23 nesneyi
  topluyor; duraklatma 17 tarama adımı, süpürme 40 adım, toplam iş 57 adımdır.
- Yaşayan nesne sayısı sabitken ölü nesne sayısı 32'den 992'ye çıkarıldığında işaretleme
  adımı 8'de kalıyor; ölü sayısı sabitken yaşayan sayısı 32'den 200'e çıkarıldığında adım
  32'den 200'e çıkıyor.
- Duraklatma toplanan çöple değil yaşayan nesneyle orantılıdır; toplanan nesne başına maliyet
  1,16 adımdan 4,33 adıma çıkarken değişen tek şey yaşayan nesne sayısıdır.
- Süpürme geçişi toplam nesne sayısıyla orantılıdır ve bu modelde toplam işin büyük payıdır;
  kopyalayan bir toplayıcı bu geçişi kaldırır, karşılığında taşıma ve başvuru güncelleme öder.
- İşaretlemeyi parçalara bölmek en uzun duraklatmayı 17 adımdan 1 adıma indiriyor ama toplam
  işi 57'den 89 adıma çıkarıyor; toplama sıklığını artırmak bellek tepesini 200'den 40 nesneye
  indirirken toplayıcı işini 230 adımdan 1310 adıma çıkarıyor.
- Ölçüt gereklilik değil erişilebilirliktir; kökten kesilmeyen gereksiz bir nesne toplanmaz,
  bu yüzden toplayıcı sızıntıyı ortadan kaldırmaz.

#### Sonraki Adım

Bu üç ders belleğin üç ayrı sorusunu ölçtü ve üçünde de veri koşum bitince yok oldu. Bir
sürecin ürettiği verinin çoğu ise koşumdan uzun yaşamak zorundadır ve bunun için baytların
adlandırıldığı, dizinlere yerleştirildiği ve sabit boyutlu bloklara bölündüğü ikinci bir
soyutlama gerekir. Sonraki ders dosya sistemini modeller: altı dosyanın 4741 baytı 512
baytlık bloklara yerleştirilir, boşa giden bayt sayılır ve blok boyu süpürülerek iç
parçalanma ile dosya düğümünün taşıması gereken blok listesi arasındaki ödünleşim ölçülür.

### Dosya Sistemi Soyutlaması

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/dosya-sistemi-soyutlamasi

Baytın ada, adın bloğa çevrilmesi: altı dosyanın 4741 baytı 512 baytlık bloklara yerleşince 1403 bayt iç parçalanma çıkıyor, blok küçüldükçe bu bedel düşerken dolaylı blok ve okuma adımı büyüyor.

Önceki üç ders belleği ölçtü ve üçünde de veri koşum bitince yok oldu. Bir sanal sayfa
atıldığında içeriği geri getirilebilirdi, ama süreç sonlandığında adres uzayı tümüyle
kayboluyordu. Üretilen verinin çoğu ise koşumdan uzun yaşamak zorundadır ve bunun için ikinci
bir soyutlama gerekir: baytların adlandırıldığı, adların bir hiyerarşiye yerleştirildiği ve
içeriğin sabit boyutlu bloklara bölündüğü bir düzen.

Bu ders o düzeni modeller ve iki bedelini sayar. Dosya oluşturma, izin verme ve dizinde
gezinme komutları bu katalogda çoktan kullanıldı; burada hiçbir komut öğretilmez. Ölçülen şey
mekanizmadır — bir baytın bloğa, bir adın numaraya çevrilmesi kaç adıma mal oluyor.

#### Üç Ayrı Nesne

Soyutlama üç nesneyi birbirinden ayırır ve ayrımın kendisi bir tasarım kararıdır.

**Dosya**, sıralı bir bayt dizisidir ve tek başına adı yoktur. **Dosya düğümü (inode)**, bir
dosyanın adı dışındaki her şeyini taşır: boyutu, izinleri, zaman damgalarını ve içeriğin
hangi bloklarda durduğunu veren blok listesini. **Dizin**, kendisi de bir dosyadır; içeriği
ad ile dosya düğümü numarasını eşleyen bir tablodur.

Adın dosyada değil dizinde durması, iki sonuç doğurur. Aynı dosya düğümüne birden çok dizin
girdisi işaret edebilir, yani bir dosyanın birden çok adı olabilir. Ve bir adı silmek dosyayı
silmek değildir — dosya, kendisine işaret eden son girdi de kalktığında ortadan kalkar.

- **BD19** — Altı dosya ortak tanımdan gelir: 120, 700, 1500, 40, 2048 ve 333 bayt, toplam
  **4741 bayt**. Varsayılan blok boyu 512 bayttır.
- **BD20** — Bir dosya düğümü **8 doğrudan blok numarası** taşır. Daha fazla bloğa ihtiyaç
  duyan dosya, numaraları bir **dolaylı blokta** tutar; bir blok numarası 4 bayttır, yani bir
  dolaylı blok `blok / 4` tane numara taşır.
- **BD21** — Dolaylı blok tek düzeylidir. Bu modeldeki dosyaların hiçbiri tek düzeyin
  kapasitesini aşmıyor.
- **BD22** — Okuma adımı blok başına birdir; dolaylı bloktaki bir numaraya bakmak bir okuma
  daha ekler. Diskin kendi gecikmesi bu derste sayılmaz.

```python
"""M01/K05 ortak tanim (kesit): dosya dugumu , blok ve ic parcalanma."""
BLOK = 512
DOSYA = [120, 700, 1500, 40, 2048, 333]
DOGRUDAN = 8          # dosya dugumunun tasidigi dogrudan blok numarasi
NUMARA = 4            # bir blok numarasi kac bayt


def dosya_dugumu(boyutlar, blok=BLOK):
    kullanilan = sum(-(-b // blok) for b in boyutlar)
    return {"dosya": len(boyutlar), "bayt": sum(boyutlar), "blok": kullanilan,
            "ic_parcalanma": kullanilan * blok - sum(boyutlar)}


def dolayli(boyutlar, blok):
    """Dogrudan numaralara sigmayan bloklar icin gereken dolayli blok ve okuma adimi."""
    ek, okuma = 0, 0
    for b in boyutlar:
        veri = -(-b // blok)
        okuma += veri
        if veri > DOGRUDAN:
            ek += -(-(veri - DOGRUDAN) // (blok // NUMARA))
            okuma += veri - DOGRUDAN          # her dolayli numara bir okuma daha
    return ek, okuma


print("ortak tanim:", dosya_dugumu(DOSYA))
print()
print("blok  veri blogu  dolayli  toplam blok  disk bayti  ic parcalanma  okuma adimi")
for blok in (64, 128, 256, 512, 1024, 4096):
    d = dosya_dugumu(DOSYA, blok)
    ek, okuma = dolayli(DOSYA, blok)
    toplam = d["blok"] + ek
    print(f"{blok:4d}  {d['blok']:10d}  {ek:7d}  {toplam:11d}  {toplam * blok:10d}"
          f"  {d['ic_parcalanma']:13d}  {okuma:11d}")
```

```
ortak tanim: {'dosya': 6, 'bayt': 4741, 'blok': 12, 'ic_parcalanma': 1403}

blok  veri blogu  dolayli  toplam blok  disk bayti  ic parcalanma  okuma adimi
  64          76        4           80        5120            123          119
 128          39        2           41        5248            251           51
 256          21        0           21        5376            635           21
 512          12        0           12        6144           1403           12
1024           8        0            8        8192           3451            8
4096           6        0            6       24576          19835            6
```

Üç sayı yan yana. **Taban:** altı dosyanın içeriği 4741 bayttır ve bitişik yazılsaydı diskte
4741 bayt tutardı; dosya düğümü tek bir başlangıç adresi ile uzunluk taşırdı. **Kurulum:**
512 baytlık bloklar, **12 blok**, diskte 6144 bayt. **Bedel:** **1403 bayt iç parçalanma** —
diske yazılanın yüzde 22,8'i hiçbir veri taşımıyor — ve dosya düğümünde tek bir çift yerine
12 blok numarası.

Bitişik yerleşimin neden terk edildiği önceki dersten okunur. Bir dosyanın büyümesi, ardındaki
baytların boş olmasını gerektirir; olmadığında dosyanın tamamı taşınmalıdır. Blok listesi bu
zorunluluğu kaldırır: yeni blok diskte nereye düşerse düşsün listeye eklenir. Yani iç
parçalanma, dış parçalanmadan kaçmak için ödenen bedeldir.

#### Blok Küçüldükçe Ne Oluyor

Tablo blok boyunu süpürüyor ve iki sütun ters yönde hareket ediyor.

İç parçalanma blokla birlikte küçülüyor: 4096 baytlık blokta 19.835 bayt, 512'de 1403, 64'te
yalnız **123 bayt**. Sebebi doğrudan — her dosyanın son bloğunda ortalama yarım blok boşa
gider ve altı dosya için bu, blok boyunun yaklaşık üç katıdır. Blok küçüldükçe boşa giden pay
da küçülür.

Okuma adımı ise ters yönde gidiyor: 4096 baytlık blokta 6 okuma, 512'de 12, 64'te **119
okuma**. Aynı 4741 baytı okumak için yirmi kat daha çok istek yapılıyor. Buna dolaylı blok da
ekleniyor: 128 bayt ve altında bazı dosyalar sekiz doğrudan numaraya sığmıyor ve blok
listesinin bir bölümü ayrı bir bloğa taşınıyor. O bloktaki her numaraya bakmak fazladan bir
okumadır, tabloda okuma sütununun 39 veri bloğu için 51'e çıkması bundandır.

Diskte tutulan toplam bayt da düz gitmiyor. 64 baytlık blokta dolaylı bloklarla birlikte 5120
bayt, 512'de 6144 bayt, 4096'da 24.576 bayt. En küçük blok yeri en verimli kullanıyor ve en
pahalı okumayı yaptırıyor. **Blok boyu seçimi bir doğruluk kararı değil, bir bütçe kararıdır**
ve bütçenin iki kalemi ters yönde hareket eder.

#### Adı Numaraya Çevirmek

Bir programın kullandığı ad bir yoldur; dosya sisteminin kullandığı şey bir dosya düğümü
numarasıdır. İkisi arasındaki çeviri, yolun her bileşeninde bir dizin okumasıdır.

- **BD23** — Altı dosya üç dizine dağıtılmıştır; adlar ölçüm ve günlük verisini temsil eder ve
  boyutları yukarıdaki listeyle aynıdır.
- **BD24** — Bir dizini okumak bir adımdır; dizinin kaç girdi taşıdığı adım sayısını
  değiştirmez.

```python
"""Dizin cozumlemesi: bir ad kac okumaya cevriliyor."""
AGAC = {                                   # dizin -> ad: (tur , dosya dugumu no)
    "/":           {"olcum": ("D", 1), "gunluk": ("D", 2)},
    "/olcum":      {"ham.veri": ("F", 10), "ozet.veri": ("F", 11), "eski": ("D", 3)},
    "/olcum/eski": {"gecmis.veri": ("F", 12)},
    "/gunluk":     {"gun.kaydi": ("F", 13), "hafta.kaydi": ("F", 14),
                    "ay.kaydi": ("F", 15)},
}
YOL = ["/olcum/ham.veri", "/olcum/ozet.veri", "/olcum/eski/gecmis.veri",
       "/gunluk/gun.kaydi", "/gunluk/hafta.kaydi", "/gunluk/ay.kaydi"]


def coz(yol):
    """Doner: dosya dugumu numarasi , okuma adimi.
    Her yol bileseninde bir dizin , sonda bir dosya dugumu okunur."""
    simdi, adim, no = "/", 0, None
    for parca in yol.strip("/").split("/"):
        adim += 1                          # bileseni tasiyan dizin okunur
        tur, no = AGAC[simdi][parca]
        if tur == "D":
            simdi = ("" if simdi == "/" else simdi) + "/" + parca
    return no, adim + 1                    # sonda dosya dugumu okunur


toplam = 0
for y in YOL:
    no, adim = coz(y)
    toplam += adim
    print(f"{y:26s} dosya dugumu {no:3d}  cozumleme adimi {adim}")
print("alti dosya icin toplam cozumleme adimi:", toplam)
print()
ERISIM = 20
y = "/olcum/eski/gecmis.veri"
_, adim = coz(y)
print(f"{y} , {ERISIM} erisim")
print("  her erisimde yeniden cozumleme:", ERISIM * adim, "adim")
print("  bir kez cozup tanitici uzerinden:", adim + ERISIM, "adim")
```

```
/olcum/ham.veri            dosya dugumu  10  cozumleme adimi 3
/olcum/ozet.veri           dosya dugumu  11  cozumleme adimi 3
/olcum/eski/gecmis.veri    dosya dugumu  12  cozumleme adimi 4
/gunluk/gun.kaydi          dosya dugumu  13  cozumleme adimi 3
/gunluk/hafta.kaydi        dosya dugumu  14  cozumleme adimi 3
/gunluk/ay.kaydi           dosya dugumu  15  cozumleme adimi 3
alti dosya icin toplam cozumleme adimi: 19

/olcum/eski/gecmis.veri , 20 erisim
  her erisimde yeniden cozumleme: 80 adim
  bir kez cozup tanitici uzerinden: 24 adim
```

Çözümleme adımı yolun derinliğiyle doğrusal büyüyor: iki bileşenli bir yol 3 adım, üç
bileşenli bir yol 4 adım. Altı dosyanın tamamı için 19 adım harcanıyor ve bunların hiçbiri
veri okumaz — hepsi ad aramasıdır.

Son iki satır, **dosya tanıtıcısının (file descriptor)** neyi satın aldığını gösteriyor. Aynı
dosyaya 20 erişim, her seferinde yol çözülerek yapılırsa 80 adım tutuyor; yol bir kez çözülüp
sonuçta elde edilen tanıtıcı üzerinden erişilirse 24 adım. Tanıtıcı, çözümlemenin sonucunu
saklayan bir kısayoldur ve maliyeti bir kez ödetir. Bedeli de vardır: tanıtıcı bir adı değil
bir dosya düğümünü tutar, bu yüzden dosya arada yeniden adlandırılsa ya da adı silinse bile
aynı içeriği okumaya devam eder.

#### Tek Güncelleme, Üç Yazma

Bir dosyaya blok eklemek tek bir yazma değildir. Veri bloğunun kendisi yazılır, bloğun artık
dolu olduğu blok eşleminde işaretlenir ve dosya düğümündeki boyut ile blok listesi
güncellenir. Üç yazma arasında makine durursa diskte hangi durum kalır sorusu, dosya
sisteminin en pahalı sorusudur.

- **BD25** — Bir güncelleme üç yazma taşır ve çökme herhangi iki yazmanın arasında olabilir.
  Yazma sırası gerçekleştirime bağlı olduğundan altı sıralamanın hepsi sayılır.
- **BD26** — Değişmez şudur: dosya düğümü bir bloğu gösteriyorsa o blok eşlemde dolu
  işaretlenmiş ve verisi yazılmış olmalıdır. Eşlemde dolu görünüp hiçbir dosya düğümünün
  göstermediği blok **kayıp bloktur** — tutarsız değildir, geri alınamaz alandır.

```python
"""Cokme tutarliligi: gunluksuz ve gunluklu duzen , butun cokme noktalari."""
from itertools import permutations

YAZMA = ("veri", "eslem", "dugum")        # veri blogu , blok eslemi , dosya dugumu


def denetle(diskteki):
    """Degismez: dosya dugumu blogu gosteriyorsa eslem dolu ve veri yazilmis olmali."""
    tutarli = ("dugum" not in diskteki) or {"eslem", "veri"} <= diskteki
    kayip = "eslem" in diskteki and "dugum" not in diskteki
    return tutarli, kayip


durum = tutarsiz = kayipli = 0
for sira in permutations(YAZMA):
    for k in range(len(YAZMA) + 1):
        t, kayip = denetle(set(sira[:k]))
        durum += 1
        tutarsiz += not t
        kayipli += kayip
print("gunluksuz: yazma", len(YAZMA), "| sira x cokme noktasi", durum,
      "| tutarsiz", tutarsiz, "| kayip blok", kayipli)

# Gunluklu duzen: once gunluge , sonra kesinlestirme kaydi , en son yerine.
ADIM = [("gunluge", a) for a in YAZMA] + [("kesinlestirme", None)] \
       + [("yerine", a) for a in YAZMA]
durum = tutarsiz = 0
for k in range(len(ADIM) + 1):
    kesin = ("kesinlestirme", None) in ADIM[:k]
    # kurtarma: kesinlestirme yoksa gunluk atilir (eski durum) , varsa yeniden oynatilir
    diskteki = set(YAZMA) if kesin else set()
    t, _ = denetle(diskteki)
    durum += 1
    tutarsiz += not t
print("gunluklu:  yazma", len(ADIM), "| cokme noktasi", durum,
      "| tutarsiz", tutarsiz, "| yazma carpani", round(len(ADIM) / len(YAZMA), 2))
```

```
gunluksuz: yazma 3 | sira x cokme noktasi 24 | tutarsiz 6 | kayip blok 4
gunluklu:  yazma 7 | cokme noktasi 8 | tutarsiz 0 | yazma carpani 2.33
```

Altı sıralama ile dört çökme noktası 24 olası son durum veriyor. Bunların **6'sı tutarsız**:
dosya düğümü bir bloğu gösteriyor ama blok ya eşlemde boş görünüyor ya da içine hiçbir şey
yazılmamış. Dördünde ise kayıp blok var — alan ayrılmış, kimse göstermiyor, geri de
alınamıyor. Yalnız 14 durumda disk sağlam.

**Günlüklü dosya sistemi (journaling file system)** bu sayıyı sıfıra indirir. Üç yazma önce
ayrı bir günlük alanına yazılır, ardından tek bir kesinleştirme kaydı düşülür ve ancak ondan
sonra veriler yerine uygulanır. Kurtarma tek kurala bakar: kesinleştirme kaydı yoksa günlük
atılır ve disk eski hâlinde kalır; varsa günlükteki yazmalar yeniden uygulanır ve disk yeni
hâline geçer. Sekiz çökme noktasının hepsi bu iki tutarlı durumdan birine düşüyor.

Bedeli tabloda yazıyor: 3 yazma yerine 7 yazma, yani **2,33 kat**. Her bayt diske iki kez
gidiyor. Bunun bir bölümü geri kazanılabilir — yalnız dosya düğümü ve eşlem gibi yapıların
günlüğe alınıp veri bloklarının doğrudan yazıldığı bir kurulum çarpanı düşürür, karşılığında
veri kaybı olasılığını geri getirir. Diskin sağlam kalması ile yazma miktarı arasındaki bu
ödünleşim kaldırılamaz; yalnız nereye konulacağı seçilir.

Günlüğün kaldırdığı şeyin ne olmadığına da dikkat etmek gerekir. Günlük, çökme anında
diskteki yapıların birbiriyle uyumlu kalmasını sağlar; yazılmakta olan verinin kaybolmasını
engellemez. Kesinleştirme kaydından önce çöken bir güncelleme, kurtarmadan sonra hiç
yapılmamış gibi görünür ve bu, tanım gereği doğru sonuçtur. Bir programın "yazdım" demesi ile
verinin diskte olması arasındaki fark, bu dersin modelinin dışında kalan ama aynı yerden
doğan bir konudur: yazma isteği kabul edildiğinde bayt henüz diskte olmayabilir.

#### Özet

- Dosya sıralı bir bayt dizisidir, dosya düğümü onun adı dışındaki her şeyini ve blok
  listesini taşır, dizin ise adı dosya düğümü numarasına eşleyen bir tablodur; ad dosyanın
  değil dizinin özelliğidir.
- Altı dosyanın 4741 baytı 512 baytlık 12 bloğa yerleşiyor ve diskte 6144 bayt tutuyor;
  1403 bayt, yani yazılanın yüzde 22,8'i iç parçalanmadır.
- Blok boyu süpürüldüğünde iki maliyet ters yönde hareket ediyor: 64 baytlık blokta iç
  parçalanma 123 bayta iniyor ama okuma adımı 119'a çıkıyor; 4096 baytlık blokta okuma 6
  adıma iniyor ama iç parçalanma 19.835 bayta çıkıyor.
- Dosya düğümü sekiz doğrudan numara taşıyor; blok küçüldükçe bazı dosyalar bu sınırı aşıyor
  ve blok listesi dolaylı bloğa taşınıyor, bu da her erişime bir okuma daha ekliyor.
- Bir yol adını dosya düğümü numarasına çevirmek yolun her bileşeninde bir dizin okumasıdır;
  aynı dosyaya 20 erişim, her seferinde çözümlenirse 80 adım, bir kez çözülüp tanıtıcı
  üzerinden yapılırsa 24 adım tutuyor.
- Tek bir blok eklemesi üç yazma taşır; altı sıralama ile dört çökme noktasının verdiği 24
  durumun 6'sı tutarsız, 4'ünde kayıp blok vardır. Günlüklü düzen bu sayıyı sıfıra indirir ve
  karşılığında yazma sayısını 3'ten 7'ye, yani 2,33 katına çıkarır.

#### Sonraki Adım

Bu ders bir bloğu okumanın bir adım olduğunu varsaydı ve o adımın ne kadar sürdüğünü hiç
sormadı. Oysa bir disk isteği, işlemcinin bir hesap adımının otuz katı sürer ve bu süre
boyunca işlemci ya bekler, ya sorar, ya da başka bir iş yapar. Kursun son dersi bu üç seçeneği
aynı iş yükünde sayar ve şaşırtıcı bir sonuçla biter: işlemciyi yüzde yüz dolduran model,
işini hiç de daha erken bitirmiyor.

### Girdi/Çıktı Modelleri

Source: https://academia.sh/tr/kurslar/isletim-sistemi-kavramlari/girdi-cikti-modelleri

Engelleyen, engellemeyen ve eşzamansız girdi/çıktının aynı iş yükünde sayılması: yoklamalı model işlemci kullanımını 1,0000 yapıyor ve süreyi hiç değiştirmiyor, eşzamansız model süreyi 248'den 38'e indiriyor.

Önceki ders bir bloğu okumayı bir adım saydı ve o adımın ne kadar sürdüğünü sormadı. Ortak
tanımda bir girdi/çıktı işlemi **30 zaman birimi** sürer; bir hesap adımı ise 1 zaman
birimidir. Aradaki otuz kat, bir sürecin zamanının nerede geçtiğini belirleyen tek sayıdır.

Bu ders, o otuz katın karşısında işlemcinin ne yaptığına dair üç ayrı kararı aynı iş yükünde
sayar. Kararlar şunlardır: bekle, sor, ya da başka iş yap. Sonuç, kursun en aykırı ölçümünü
üretir — üç modelden biri işlemciyi tamamen doldurur ve işini bir zaman birimi bile erken
bitirmez.

#### Üç Model

**Engelleyen (blocking)** çağrıda süreç isteği verir ve tamamlanana kadar hazır kuyruğundan
çıkar; işlemci bu süre boyunca başka bir sürece verilebilir, ama isteği veren süreç hiçbir şey
yapmaz. **Yoklamalı (polling)** kurulumda süreç engellenmez, tamamlandı mı diye sormaya devam
eder; bu bir **meşgul beklemedir** ve işlemciyi elinde tutar. **Eşzamansız (asynchronous)**
kurulumda süreç isteği verir, hemen döner, başka iş yapar ve tamamlanma bir bildirimle
ulaşır. Bildirimin donanım tarafındaki karşılığı bir kesmedir; kesmenin kendisi
Bilgisayarlar Nasıl Çalışır kursunun Kesmeler dersinde tanımlandı ve burada yalnız
tamamlanma bildirimi olarak kullanılıyor.

- **BD27** — Bir girdi/çıktı işleminin gecikmesi 30 zaman birimi, istek başına işlemcide
  yapılan iş 1 zaman birimidir. Sekiz istek ölçülür.
- **BD28** — Eşzamansız modelde sekiz istek aynı anda uçuşta olabilir ve gecikmeleri tümüyle
  örtüşür; disk sekiz isteği paralel karşılar.
- **BD29** — İşlemci kullanımı, işlemcinin dolu geçirdiği adımın toplam süreye oranıdır. Meşgul
  bekleme dolu sayılır — işlemci gerçekten bir komut yürütüyordur.
- **BD30** — Bu ders tek makinedir. Hiçbir ağ bedeli, uzak çağrı ya da veri taşıma maliyeti
  yoktur.

```python
"""M01/K05 ortak tanim (kesit): girdi/cikti modelleri."""
BEKLEME_SURESI = 30       # bir girdi/cikti isleminin gecikmesi , zaman birimi


def gc_modeli(istek, yordam="engelleyen", gecikme=BEKLEME_SURESI, is_basina=1):
    """istek: kac girdi/cikti islemi. Doner: sure , mesgul adim , islemci kullanimi."""
    if yordam == "engelleyen":
        sure = istek * (gecikme + is_basina)
        mesgul = istek * is_basina
    elif yordam == "yoklamali":
        sure = istek * (gecikme + is_basina)
        mesgul = sure                      # mesgul bekleme: islemci hep dolu
    else:
        sure = gecikme + istek * is_basina
        mesgul = istek * is_basina
    return {"sure": sure, "mesgul": mesgul, "kullanim": round(mesgul / sure, 4)}


print("sekiz istek , ortak tanimin olcumu")
for y in ("engelleyen", "yoklamali", "eszamansiz"):
    print(f"  {y:12s}", gc_modeli(8, y))
print()
print("istek   engelleyen        yoklamali        eszamansiz")
print("        sure  kullanim    sure  kullanim    sure  kullanim")
for istek in (1, 2, 4, 8, 16, 32):
    e = gc_modeli(istek, "engelleyen")
    y = gc_modeli(istek, "yoklamali")
    a = gc_modeli(istek, "eszamansiz")
    print(f"{istek:5d}  {e['sure']:4d}  {e['kullanim']:8.4f}  {y['sure']:6d}"
          f"  {y['kullanim']:8.4f}  {a['sure']:6d}  {a['kullanim']:8.4f}")
```

```
sekiz istek , ortak tanimin olcumu
  engelleyen   {'sure': 248, 'mesgul': 8, 'kullanim': 0.0323}
  yoklamali    {'sure': 248, 'mesgul': 248, 'kullanim': 1.0}
  eszamansiz   {'sure': 38, 'mesgul': 8, 'kullanim': 0.2105}

istek   engelleyen        yoklamali        eszamansiz
        sure  kullanim    sure  kullanim    sure  kullanim
    1    31    0.0323      31    1.0000      31    0.0323
    2    62    0.0323      62    1.0000      32    0.0625
    4   124    0.0323     124    1.0000      34    0.1176
    8   248    0.0323     248    1.0000      38    0.2105
   16   496    0.0323     496    1.0000      46    0.3478
   32   992    0.0323     992    1.0000      62    0.5161
```

Üç sayı yan yana. **Taban:** engelleyen çağrı, sekiz istek, **248 zaman birimi**, işlemci
kullanımı 0,0323. **Kurulum:** eşzamansız model, **38 zaman birimi**, kullanım 0,2105.
**Bedel:** sekiz tamamlanma bildirimini işlemek ve aynı anda uçuşta olan sekiz isteğin
defterini tutmak — birazdan ikisi de sayılacak.

#### Yüksek Kullanım Bir Başarı Ölçüsü Değildir

Tablonun ortasındaki sütun kursun en aykırı sonucudur. Yoklamalı model işlemci kullanımını
**1,0000** yapıyor: işlemci hiç boş kalmıyor, her zaman biriminde bir komut yürütülüyor. Aynı
satırdaki süre ise **248** — engelleyen modelle **birebir aynı**. İşlemciyi tamamen doldurmak
işi bir zaman birimi bile erkene almadı.

Sebebi tanımdan okunur. Yoklamalı modelde işlemcinin yürüttüğü komutlar "tamamlandı mı" diye
soran komutlardır; iş üretmezler. Dolu geçen 248 adımın yalnız 8'i gerçek iştir, kalan 240'ı
bekleme adımının farklı bir biçimde harcanmasıdır. Engelleyen model aynı 240 adımı boş
bırakır; yoklamalı model onları doldurur ve **hiçbir şeyi hızlandırmaz.**

Buradan çıkan kural ölçüt seçimine dairdir. İşlemci kullanımı bir **vekil ölçüttür**: gerçekten
sorulan soru "iş ne zaman bitiyor" olduğu halde, ölçülmesi kolay olduğu için onun yerine
kullanılır. Bu iş yükünde vekil ölçüt tersine dönüyor — en yüksek kullanıma sahip model en iyi
süreyi vermiyor, en iyi süreyi veren eşzamansız modelin kullanımı ise yalnız 0,2105. Bir
kurulumun kullanımı yüksekse **neyle dolu olduğu** sorulmadan hiçbir şey söylenemez.

Eşzamansız modelin nasıl kazandığı da aynı tablodadır. Süresi istek sayısıyla neredeyse
değişmiyor: bir istekte 31, otuz iki istekte 62. Engelleyen model 31'den 992'ye çıkarken
eşzamansız model iki katına bile ulaşmıyor, çünkü otuz birim gecikme bir kez ödeniyor ve
bütün istekler onun içinde örtüşüyor. Kazanç istek sayısıyla büyüyor: sekiz istekte 6,5 kat,
otuz iki istekte 16 kat.

#### Engellemeyen Çağrı ve Yoklama Aralığı

Yoklamalı kurulumun altındaki mekanizma ayrı bir kavramdır ve ayrı adlandırılır.
**Engellemeyen (non-blocking)** çağrı, sonuç hazır değilse süreci uyutmaz; "henüz hazır değil"
diyerek hemen döner. Yoklama, bu çağrının bir döngü içinde tekrarlanmasıdır. İkisi aynı şey
değildir: engellemeyen çağrı bir arayüz özelliğidir, yoklama onu kullanma biçimlerinden
yalnız biridir — ve en pahalısıdır.

Yukarıdaki ölçümde yoklama her zaman biriminde bir yapıldı. Aralık büyütülebilir; o zaman
işlemci daha az sorar, ama tamamlanmayı daha geç fark eder.

- **BD31** — Yoklama sabit aralıklarla yapılır ve her soru bir zaman birimi tutar. Tamamlanma,
  gecikmeden sonraki ilk soruda fark edilir.

```python
"""Engellemeyen cagri: yoklama araligi hangi iki maliyeti karsi karsiya getiriyor."""
GECIKME = 30
ISTEK = 8

print("aralik  yoklama  fark edilme  fazladan gecikme  toplam sure  islemci adimi")
for aralik in (1, 2, 4, 7, 10, 30, 45):
    sorma = -(-GECIKME // aralik)          # tamamlanma fark edilene kadar kac soru
    fark = sorma * aralik                  # fark edilme ani
    print(f"{aralik:6d}  {sorma:7d}  {fark:11d}  {fark - GECIKME:16d}"
          f"  {ISTEK * (fark + 1):11d}  {ISTEK * (sorma + 1):13d}")
```

```
aralik  yoklama  fark edilme  fazladan gecikme  toplam sure  islemci adimi
     1       30           30                 0          248            248
     2       15           30                 0          248            128
     4        8           32                 2          264             72
     7        5           35                 5          288             48
    10        3           30                 0          248             32
    30        1           30                 0          248             16
    45        1           45                15          368             16
```

Aralık 1'den 30'a çıkarıldığında işlemci adımı **248'den 16'ya** iniyor ve süre 248'de
kalıyor: bu iş yükünde yoklamanın 240 adımı tümüyle boşa harcanmış işti. Ama tablo düz
gitmiyor. Aralık 4 ve 7'de fark edilme gecikmesi 2 ve 5 birim uzuyor, süre 264 ile 288'e
çıkıyor; aralık 45'te tamamlanma 15 birim geç fark ediliyor ve süre 368'e fırlıyor. Aralığın
gecikmeyi tam bölmediği her yerde bir artık ödeniyor ve o artığın büyüklüğü, tam olarak ne
zaman tamamlanacağını bilmeyen bir sürecin ödediği bedeldir.

İki uç arasındaki fark şudur: **aralık küçükse işlemci, büyükse yanıt süresi ödüyor.** Ortada
bir en iyi nokta yoktur — 30 birimlik aralık burada iki maliyeti de en aza indiriyor, ama
yalnız gecikmenin tam olarak 30 olduğu bilindiği için. Gecikme değişkense o bilgi yoktur ve
seçim yeniden bir tahmine dönüşür. Tamamlanma bildirimi kullanan eşzamansız model bu tahmini
tümüyle ortadan kaldırır; karşılığında bildirimi işlemek için bir mekanizma ister.

#### Eşzamansızlığın İki Sınırı

Yukarıdaki ölçüm iki şeyi ücretsiz saydı: tamamlanma bildirimini işlemeyi ve isteklerin
birbirinden bağımsız olmasını. İkisi de her zaman doğru değildir.

- **BD32** — Bir tamamlanma bildirimini işlemek `bildirim` kadar zaman birimi tutar; bu, kesme
  işleyicisinin çalışması ve isteğin defterden düşülmesidir. Süpürülen değer 0 ile 16
  arasındadır.
- **BD33** — Bağımlı zincirde her istek bir öncekinin sonucunu kullanır; bu yüzden gecikmeler
  örtüşemez.

```python
"""Eszamansiz modelin iki siniri: tamamlanma bildiriminin bedeli ve bagimlilik."""
GECIKME = 30
ISTEK = 8


def eszamansiz(istek, bildirim=0, bagimli=False):
    """bildirim: her tamamlanma bildirimini islemek kac zaman birimi.
    bagimli: her istek bir oncekinin sonucunu bekliyorsa ortusme olmaz."""
    is_basina = 1 + bildirim
    sure = istek * (GECIKME + is_basina) if bagimli else GECIKME + istek * is_basina
    mesgul = istek * is_basina
    return {"sure": sure, "mesgul": mesgul, "kullanim": round(mesgul / sure, 4)}


print("bagimsiz istek , bildirim bedeli supuruluyor")
print("bildirim  sure  mesgul  kullanim  engelleyene gore kazanc")
for b in (0, 1, 2, 4, 8, 16):
    a = eszamansiz(ISTEK, b)
    print(f"{b:8d}  {a['sure']:4d}  {a['mesgul']:6d}  {a['kullanim']:8.4f}"
          f"  {ISTEK * (GECIKME + 1) / a['sure']:22.2f}")
print()
print("bagimli zincir (her istek bir oncekini bekliyor)")
for b in (0, 1):
    a = eszamansiz(ISTEK, b, bagimli=True)
    print(f"  bildirim {b}: sure {a['sure']} kullanim {a['kullanim']}")
print("  engelleyen modelin suresi:", ISTEK * (GECIKME + 1))
```

```
bagimsiz istek , bildirim bedeli supuruluyor
bildirim  sure  mesgul  kullanim  engelleyene gore kazanc
       0    38       8    0.2105                    6.53
       1    46      16    0.3478                    5.39
       2    54      24    0.4444                    4.59
       4    70      40    0.5714                    3.54
       8   102      72    0.7059                    2.43
      16   166     136    0.8193                    1.49

bagimli zincir (her istek bir oncekini bekliyor)
  bildirim 0: sure 248 kullanim 0.0323
  bildirim 1: sure 256 kullanim 0.0625
  engelleyen modelin suresi: 248
```

Birinci sınır bildirimin bedelidir. Her tamamlanmaya 16 zaman birimlik bir işleme eklendiğinde
süre 38'den **166'ya** çıkıyor ve engelleyen modele göre kazanç 6,53 kattan **1,49 kata**
düşüyor. Dikkat çekici olan, kullanımın bu süpürme boyunca **0,2105'ten 0,8193'e yükselmesi**:
model her adımda yavaşlarken ölçütü sürekli iyileşiyor. Kullanımın neden bir başarı ölçüsü
olmadığının ikinci kanıtı budur.

İkinci sınır bağımlılıktır. İstekler birbirinin sonucunu bekliyorsa örtüşecek bir şey yoktur;
eşzamansız model **248 zaman biriminde** bitiyor, yani engelleyen modelle tam olarak aynı.
Bildirim işlemenin bedeli eklendiğinde ise **256**'ya çıkıyor ve eşzamansız kurulum engelleyen
kurulumdan **daha kötü** hâle geliyor. Eşzamansızlığın sattığı şey hız değildir, **örtüşmedir**;
örtüşecek iş yoksa geriye yalnız defter tutma maliyeti kalır.

#### Özet

- Sekiz girdi/çıktı isteğinde engelleyen model 248 zaman biriminde bitiyor ve işlemci
  kullanımı 0,0323; yoklamalı model aynı 248'de bitiyor ve kullanımı 1,0000; eşzamansız model
  38'de bitiyor ve kullanımı 0,2105.
- Meşgul bekleme işlemciyi doldurur ve hiçbir şeyi hızlandırmaz: dolu geçen 248 adımın yalnız
  8'i iş üretir, 240'ı "tamamlandı mı" sorusudur.
- İşlemci kullanımı bir vekil ölçüttür ve bu iş yükünde ters yönde hareket eder; bir kurulumun
  kullanımı yüksekse neyle dolu olduğu sorulmadan hiçbir sonuç çıkarılamaz.
- Engellemeyen çağrı bir arayüz özelliğidir, yoklama onu kullanma biçimidir; yoklama aralığı
  1'den 30'a çıkarıldığında işlemci adımı 248'den 16'ya iniyor, ama aralık gecikmeyi tam
  bölmediğinde süre 288'e ve 368'e kadar uzuyor.
- Eşzamansız modelin kazancı örtüşmeden gelir ve istek sayısıyla büyür: sekiz istekte 6,5 kat,
  otuz iki istekte 16 kat.
- Kazanç iki koşulda erir: tamamlanma bildirimi başına 16 zaman birimi ödendiğinde 1,49 kata
  düşer, istekler birbirine bağımlı olduğunda ise sıfırlanır ve bildirim bedeliyle birlikte
  engelleyen modelin altına iner.

#### Kurs Kapanışı

On beş ders tek bir makineyi modelledi ve tek bir soruyu sordu: bir soyutlama kolaylık
sağlarken kaç adım ödetiyor. Her derste üç sayı yan yana durdu — soyutlamasız taban,
soyutlamalı kurulum ve aradaki bedel. Kursun kuralı buydu: **bedeli sayılmayan soyutlama
ölçülmemiş sayılır.**

| Ders | Soyutlamasız taban | Soyutlamalı kurulum | Bedel |
|---|---|---|---|
| Süreç Kavramı | beş süreç peş peşe: **669** zaman birimi | tek çekirdeği paylaşan beş süreç: **194** | **22 bağlam değiştirme = 44 zaman birimi**, gerçek işin (39) üstünde; 111 boş çekirdek adımı |
| Süreç Çatallama | çatallama yok: **0** kopyalanan sayfa | dört çatallama, hevesli kopyalama: **64 sanal sayfa** | yazarken kopyalama **15 sayfaya** indiriyor, 49'u boşaydı; sayfa hatası bedeli **4**'te üstünlük kayboluyor |
| İş Parçacıkları | beş ayrı süreç: **194** zaman birimi, **15** ek sanal sayfa | tek süreçte beş iş parçacığı: **186**, **4** yığıt sayfası | bellekte yüzde **73,33** kazanç, sürede yalnız **8** birim; bedeli **yalıtımın tümüyle kalkması** |
| Süreç ve İş Parçacığı Seçimi | tek adres uzayı: **0** kopyalanan sayfa, yayılma oranı **5,0** | beş ayrı adres uzayı: etkilenen birim **1** | **15** kopyalanan sayfa (hevesliyle 64); kısmi yalıtım doğrusal değil — 6 sayfa 5'i 3'e indiriyor, son 5 sayfa yalnız 1 kazandırıyor |
| Çizelgeleme Algoritmaları | önalımsız sıra: **194** (ikinci iş yükünde 189) | dilimli çizelgeleme: **197** (ikinci iş yükünde 202) | dilim 1'de **206**, **36** bağlam değiştirme, bekleme **33,00**; iki iş yükünde de en iyi yordam **yok** |
| Yarış Koşulları | tek yürütme sırası: **1** sonuç | iki iş parçacığı, bölünebilir üç adım: **20** serpiştirme | **18'i yanlış** (0,9000); dilim 3'te erişilebilen **2** serpiştirmenin **hiçbiri** yanlış değil — hata yok olmuyor, görünmez oluyor |
| Kilitler, Dışlama Kilitleri ve Sayan Semaforlar | tek iş parçacığı: **20** zaman birimi, **0** bekleme adımı | sekiz iş parçacığı, dışlama kilidi: **83** | **252** bekleme adımı, kullanım **1,0000'den 0,2410'a**; bekleme $n(n-1)$'in **4,5** katı |
| Kilitlenme ve Açlık | serbest edinim: 648 durumun **264'ü kilitli** (0,4074) | sıralı edinim: durum uzayı **36**, kilitlenen **0** | sekiz iş parçacığında **+144** bekleme adımı; açlıkta verim düşmüyor, arıza yalnız **dağılımda** görünüyor |
| Çok Çekirdekte Eşzamanlılık | tek çekirdek: **197**, 108 boş çekirdek adımı | dört çekirdek: **179** (alt sınır 173) | **639** boş çekirdek adımı, toplam iş **39'da sabit**; dilim koruması kalkıyor, erişilebilen serpiştirme 2'den **20'ye** |
| Eşzamanlılık Modelleri | iş parçacığı modeli: **197** (ikinci iş yükünde 202) | olay döngüsü **178**, ileti geçişi **179** | aradaki 1 zaman birimi **ölçüm bandının altında**; fark boş çekirdek adımında: **139'a karşı 639** |
| Sanal Bellek | 15 fiziksel sayfa, 15 zorunlu hata, 489 zaman birimi | 2 fiziksel sayfa, sayfalı bellek, ilk giren ilk çıkar | 15 fazladan sayfa hatası, 450 zaman birimi |
| Bellek Ayırma | uç uca ayırma, 6 istek yerleşiyor | bölüm listeli ayırıcı, en iyi uyan, 8 istek yerleşiyor | 10'ar baytlık iki kırıntı, dış parçalanma oranı 0,5000 |
| Çöp Toplama | elle bırakma, 0 tarama adımı | izlemeli toplayıcı, 40 nesnenin 17'si erişilebilir | 17 adım duraklatma, 40 adım süpürme, toplam 57 adım |
| Dosya Sistemi Soyutlaması | bitişik yerleşim, 4741 bayt | 512 baytlık 12 blok, diskte 6144 bayt | 1403 bayt iç parçalanma, 12 blok numarası |
| Girdi/Çıktı Modelleri | engelleyen çağrı, 248 zaman birimi | eşzamansız model, 38 zaman birimi | 8 tamamlanma bildirimi ve uçuştaki 8 isteğin defteri |

Tablonun ikinci okuması birincisinden değerlidir: **soyutlama bazen kötüleştirir.** Daha akıllı
sayfa değiştirme daha çok hata verebilir, en iyi uyan daha kullanılamaz bir kalıntı bırakır,
duraklatmayı bölmek toplam işi büyütür, günlüklü düzen her baytı iki kez yazar, meşgul bekleme
işlemciyi doldurur ve hiçbir şeyi hızlandırmaz. Bu sonuçların hiçbiri saklanmadı, çünkü bir
soyutlamanın ne zaman kötüleştirdiğini bilmeyen biri onu ne zaman seçeceğini de bilemez.

Üçüncü ve en pahalı sonuç eşzamanlılık konusundan geldi: **doğru çıktı doğru program demek
değildir.** Aynı hata, çizelgeleyicinin dilimi yeterince büyük olduğunda hiç görünmez. Hata
yok olmaz, yalnız görünmez olur — ve görünmeyen bir hatayı gösteren tek şey, mekanizmayı
modelleyip bütün durumları saymaktır.

Kurs boyunca ölçülen her şey **tek bir makinede** ölçüldü. Ağ bedeli, uzak çağrı gecikmesi ve
makineler arası veri taşıma bu ölçümlerin hiçbirinde yoktur; buradaki sayılar dağıtık bir
kuruluma taşındığında geçerliliğini yitirir.

Bir sonraki kurs, **Modelleme ve Gösterim**, modelin kendisini konu edinir. Bu kursta model
çalıştırılabilir bir metindi ve çıktısı sayıydı; orada model bir diyagram olacak ve çıktısı
bir yanıt. Ortak olan disiplin değişmiyor: bir diyagram hangi soruyu yanıtladığı belliyse
işe yarar, tıpkı bir soyutlamanın ancak bedeli sayıldığında ölçülmüş sayılması gibi. Bir
sistemi sınıf, bileşen, sıra ve durum diyagramlarıyla göstermek, bu kursta sayılarla yapılan
şeyin başka bir gösterimle yapılmasıdır — ve orada da gereksiz gösterimden kaçınmanın ölçütü
aynı sorudur: bu neyi ölçüyor.

