İçeriğe geç
academia.sh

Ders 04 / 22

Süreç Öncelikleri

En çok işleyen sürecin öncelik değeri en düşükten en yükseğe çekildiğinde payı 41.878 birimden 0'a iniyor, ama toplam iş 237.906'dan 238.244'e ve yük 3,97'den 3,97'ye gidiyor: boşalan kapasitenin yüzde 94'ü öteki ağır süreçlere, hafif süreçlere yüzde 0'ı gidiyor.

İçindekiler

Önceki ders süreci durdurmayı ölçtü. Durdurmak her zaman doğru yanıt değildir: ağır bir süreç gerekli bir iş yapıyor olabilir ve istenen şey onu bitirmek değil, yavaşlatmaktır. Bunun için tasarlanmış bir düğme vardır ve süreç tablosunun bir sütununda durur.

Bu ders o düğmeyi ölçer. Soru şudur: bir sürecin önceliğini değiştirmenin görülebilir etkisi ne kadardır, ve değişen sütun ile değişen davranış aynı şey midir.

Öncelik Değeri

Süreç tablosundaki NI sütunu öncelik değerini (nice value) taşır. Ölçeği terstir: değer büyüdükçe sürecin önceliği düşer. Aralık -20 ile 19 arasındadır; -20 en saldırgan, 19 en geri çekilmiş süreçtir. Varsayılan 0’dır.

Yanındaki PRI sütunu farklı bir şeydir. NI operatörün girdisidir; PRI çizelgeleyicinin o girdiden ve sürecin geçmiş davranışından hesapladığı değerdir. Operatör PRI sütununu doğrudan yazamaz. İkisini karıştırmak, girdi ile sonucu karıştırmaktır.

$ nice -n 10 ./yedek-gorevi --tam &
$ renice -n 5 -p 1014
1014 (process ID) old priority -5, new priority 5
$ renice -n 5 -u yedek
$ ps -eo pid,ni,pri,comm --sort=ni
    PID  NI PRI COMMAND
   1014  -5  24 kuyruk-isleyici
   1128   0  19 rapor-uretici
   1043   5  14 olcum-toplayici
   1071  10   9 yedek-gorevi

Bu döküm örnektir ve çalıştırılmamıştır. nice değeri süreç başlarken verir, renice çalışan bir süreci değiştirir; -u seçeneği bir kullanıcının bütün süreçlerini kapsar. Bir kural burada gizlidir: yetkisiz bir kullanıcı öncelik değerini yalnız artırabilir, yani kendi sürecini yalnız geri çekebilir. Geri alma yetkisi yoktur — 10’a çekilen bir süreç, yetkisiz kullanıcı tarafından 5’e döndürülemez. Bu, dikkatsiz bir renice çağrısının kalıcı olabileceği anlamına gelir.

İşletim Sistemi Kavramları kursu çizelgeleyici yordamlarını birbiriyle karşılaştırmıştı; o karşılaştırma burada tekrarlanmaz. Buradaki soru yordamın hangisi olduğu değil, operatörün elindeki tek düğmenin çıktıda ne kadar göründüğüdür.

  • SY22. Kurgu sunucunun 4 çekirdeği vardır; saniyelik kapasite 400 birimdir.
  • SY23. Süreçlerin toplam talebi kapasiteyi aşar; sistem doymuştur.
  • SY24. Öncelik değeri bir ağırlığa çevrilir: her birim ağırlığı yaklaşık dörtte bir oranında değiştirir. 0 değerinin ağırlığı 1024’tür.
  • SY25. Kapasite ağırlığa göre bölüşülür, ama hiçbir süreç istediğinden fazlasını almaz. Doyan süreçlerin artığı kalanlar arasında yeniden bölüşülür.
  • SY26. Kâhin, pencere boyunca gerçekten yapılan toplam iştir. Kapasite sabit olduğu için bu sayının üst sınırı da sabittir.
  • SY27. Çözünürlük toplam işin yüzde 1’idir. Bunun altındaki fark ölçülmemiş sayılır.
  • SY28. Tanı şudur: “en çok işleyen sürecin önceliğini düşürmek sistemin yükünü azaltır.” Yanlış tanı, bu cümlenin ayakta kalmadığı deneme sayısıdır.
  • SY29. İkinci tohum 20260219’dur.
# --- ortak tanim, birinci dersteki bicimiyle
TOHUM, IKINCI_TOHUM, DONEM = 20260218, 20260219, 600
KULLANICI = ("kok", "uygulama", "yedek", "izleme")
KOMUT = ("veri-alici", "rapor-uretici", "yedek-gorevi", "olcum-toplayici",
         "kuyruk-isleyici", "onbellek-temizleyici")


def uretec(tohum):
    d = tohum

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


def surecler(tohum=TOHUM, sayi=24):
    r = uretec(tohum)
    liste = []
    for i in range(sayi):
        tur = r(10)
        desen = "duz" if tur < 5 else ("ani" if tur < 8 else "agir")
        liste.append({"pid": 1000 + i * 7 + r(5),
                      "kullanici": KULLANICI[r(len(KULLANICI))],
                      "komut": KOMUT[r(len(KOMUT))], "desen": desen,
                      "oncelik": r(11) - 5, "bellek": 20 + r(400),
                      "sinyal_yok_sayar": r(10) < 2})
    return liste


def kullanim(surec, saniye):
    d = surec["desen"]
    if d == "duz":
        return 3 + (surec["pid"] + saniye) % 4
    if d == "agir":
        return 60 + (surec["pid"] + saniye) % 25
    faz = (saniye + surec["pid"]) % 60
    return 88 + faz if faz < 5 else 2 + faz % 3


# --- bu dersin katmani: oncelik degeri kapasiteyi nasil bolusturur
CEKIRDEK, KAPASITE = 4, 400


def agirlik(oncelik):
    """Oncelik degeri BUYUDUKCE pay KUCULUR. Her birim yaklasik dortte bir."""
    a = 1024
    for _ in range(abs(oncelik)):
        a = a * 5 // 4 if oncelik < 0 else a * 4 // 5
    return a


def pay_dagit(surecler_, saniye, kapasite=KAPASITE):
    """Talep kapasiteyi asarsa pay AGIRLIGA gore bolusulur; kimse istediginden
    fazlasini almaz. Cizelgeleyicinin kendisi baska bir kursta olculdu."""
    talep = {s["pid"]: kullanim(s, saniye) for s in surecler_}
    pay = {p: 0 for p in talep}
    bekleyen, kalan = list(surecler_), kapasite
    while bekleyen and kalan > 0:
        toplam = sum(agirlik(s["oncelik"]) for s in bekleyen)
        doyan = [s for s in bekleyen
                 if kalan * agirlik(s["oncelik"]) // toplam >= talep[s["pid"]]]
        if not doyan:
            for s in bekleyen:
                pay[s["pid"]] = kalan * agirlik(s["oncelik"]) // toplam
            break
        for s in doyan:
            pay[s["pid"]] = talep[s["pid"]]
            kalan -= talep[s["pid"]]
            bekleyen.remove(s)
    return pay


def pencere_payi(surecler_, pencere=DONEM):
    toplam = {s["pid"]: 0 for s in surecler_}
    for t in range(pencere):
        for p, v in pay_dagit(surecler_, t).items():
            toplam[p] += v
    return toplam


S = surecler()
print("cekirdek:", CEKIRDEK, "| kapasite:", KAPASITE,
      "| t=0 talebi:", sum(kullanim(s, 0) for s in S))
print("oncelik  ", "  ".join(f"{o:4d}" for o in (-5, -3, 0, 3, 5, 10, 19)))
print("agirlik  ", "  ".join(f"{agirlik(o):4d}" for o in (-5, -3, 0, 3, 5, 10, 19)))
EN_AGIR = max(S, key=lambda s: sum(kullanim(s, t) for t in range(DONEM)))
print("en cok isleyen surec:", EN_AGIR["pid"], EN_AGIR["komut"],
      "| oncelik degeri", EN_AGIR["oncelik"])
cekirdek: 4 | kapasite: 400 | t=0 talebi: 736
oncelik     -5    -3     0     3     5    10    19
agirlik   3125  2000  1024   524   335   108    12
en cok isleyen surec: 1014 kuyruk-isleyici | oncelik degeri -5

Ağırlık tablosu ölçeğin ne kadar sert olduğunu gösteriyor: -5 ile 19 arasında ağırlık 3125’ten 12’ye, yani 260 kat düşüyor. Talep 736, kapasite 400 — sistem doymuş durumdadır ve öncelik değerinin bir anlamı ancak bu koşulda vardır. Doymamış bir sistemde herkes istediğini alır ve ağırlıkların hiçbir etkisi olmaz.

Önceliği Düşürmenin Görülebilir Etkisi

En çok işleyen süreç 1014’tür ve öncelik değeri şu anda -5, yani en saldırgan uçtadır. Operatörün alışılmış hamlesi onu geri çekmektir.

# Onceki blogun uzerine: S, EN_AGIR, pencere_payi, DONEM oradan gelir.
import copy


def yeniden_oncelikle(surecler_, pid, yeni):
    k = copy.deepcopy(surecler_)
    for s in k:
        if s["pid"] == pid:
            s["oncelik"] = yeni
    return k


HEDEF = EN_AGIR["pid"]
TABAN = pencere_payi(S)
TOPLAM_TABAN = sum(TABAN.values())
print("yeni oncelik  hedefin payi  degisim  otekilerin payi  toplam is   yuk")
for y in (-5, -4, -3, 0, 5, 10, 19):
    p = pencere_payi(yeniden_oncelikle(S, HEDEF, y))
    toplam = sum(p.values())
    print(f"{y:12d}  {p[HEDEF]:12d}  {p[HEDEF] - TABAN[HEDEF]:7d}"
          f"  {toplam - p[HEDEF]:15d}  {toplam:9d}"
          f"  {toplam / DONEM / 100:5.2f}")
print("kahin: kapasite", KAPASITE * DONEM, "birim | taban kullanimi", TOPLAM_TABAN)
yeni oncelik  hedefin payi  degisim  otekilerin payi  toplam is   yuk
          -5         41878        0           196028     237906   3.97
          -4         37372    -4506           200460     237832   3.96
          -3         31462   -10416           206434     237896   3.96
           0         17668   -24210           220318     237986   3.97
           5          6120   -35758           231974     238094   3.97
          10          1830   -40048           236342     238172   3.97
          19             0   -41878           238244     238244   3.97
kahin: kapasite 240000 birim | taban kullanimi 237906

İkinci sütun operatörü ikna eder. Tek bir adımlık değişiklik — -5‘ten -4‘e — hedefin payını 4.506 birim düşürüyor; uca kadar çekildiğinde pay **41.878’den 0’a** iniyor. Araç çıktısında bu sütun gerçekten değişir, NI sütunu her denemede yeni değeri gösterir ve %CPU sütununda hedef listenin dibine düşer. Yapılan işin görüldüğü tek yer burasıdır.

Son iki sütun tanıyı bozar. Toplam iş 237.906’dan 238.244’e gidiyor — 338 birimlik, yani yüzde 0,14’lük bir fark. Çözünürlük yüzde 1 olduğu için bu fark ölçülmemiş sayılır. Yük sütunu yedi satırın altısında 3,97, birinde 3,96: hiç değişmemiştir. Sistem doymuş durumdaydı ve doymuş kaldı.

Nedeni tek cümlelik bir gözlemdir: öncelik değeri işi azaltmaz, işi bölüştürür. Kapasite kaç birimse o kadar iş yapılır; kimin yaptığı değişir, ne kadar yapıldığı değişmez. Yükten şikâyet eden bir operatörün renice ile aradığı şey ise tam olarak “ne kadar yapıldığı”dır.

Boşalan Kapasite Kime Gidiyor

Hedefin bıraktığı 41.878 birim yok olmadı; başka süreçlere gitti. Hangilerine gittiği, renice hamlesinin gerçekten kime yaradığını söyler.

# Onceki bloklarin uzerine: S, HEDEF, TABAN, TOPLAM_TABAN, yeniden_oncelikle.
son = pencere_payi(yeniden_oncelikle(S, HEDEF, 19))
bosalan = TABAN[HEDEF] - son[HEDEF]
kazanc = {d: 0 for d in ("duz", "ani", "agir")}
for s in S:
    if s["pid"] != HEDEF:
        kazanc[s["desen"]] += son[s["pid"]] - TABAN[s["pid"]]
print("hedeften bosalan is:", bosalan, "birim | nereye gitti:")
for d in ("duz", "ani", "agir"):
    print(f"  {d:5s} desenli surecler: {kazanc[d]:6d}"
          f"  ({round(100 * kazanc[d] / bosalan):3d} yuzde)")
print("toplam is degisimi:", sum(son.values()) - TOPLAM_TABAN, "birim |",
      f"{100 * (sum(son.values()) - TOPLAM_TABAN) / TOPLAM_TABAN:.2f} yuzde")
print("`NI` sutunu kac denemede degisti: 6 / 6 | yuk kac denemede dustu: 0 / 6")
print()
for tohum in (TOHUM, IKINCI_TOHUM):
    S2 = surecler(tohum)
    h = max(S2, key=lambda s: sum(kullanim(s, t) for t in range(DONEM)))["pid"]
    t0, t1 = pencere_payi(S2), pencere_payi(yeniden_oncelikle(S2, h, 19))
    print(f"tohum {tohum}: hedefin payi {t0[h]:6d} -> {t1[h]:5d}"
          f" | toplam is {sum(t0.values()):6d} -> {sum(t1.values()):6d}"
          f" | yuk {sum(t0.values()) / DONEM / 100:.2f} ->"
          f" {sum(t1.values()) / DONEM / 100:.2f}")
hedeften bosalan is: 41878 birim | nereye gitti:
  duz   desenli surecler:      0  (  0 yuzde)
  ani   desenli surecler:   2774  (  7 yuzde)
  agir  desenli surecler:  39442  ( 94 yuzde)
toplam is degisimi: 338 birim | 0.14 yuzde
`NI` sutunu kac denemede degisti: 6 / 6 | yuk kac denemede dustu: 0 / 6

tohum 20260218: hedefin payi  41878 ->     0 | toplam is 237906 -> 238244 | yuk 3.97 -> 3.97
tohum 20260219: hedefin payi  43200 -> 12786 | toplam is 239078 -> 239450 | yuk 3.98 -> 3.99

Boşalan kapasitenin yüzde 94’ü öteki ağır süreçlere gidiyor. Hafif desenli süreçlere gidenin oranı yüzde 0’dır — tam olarak sıfır birim. Bunun nedeni açıktır ve önceliğin gerçek kuralını verir: hafif süreçler zaten doyuyordu. Az isteyen bir süreç, doymuş bir sistemde bile istediğini alır; ona verilecek fazla bir şey yoktur.

Sonuç, renice hamlesinin ne zaman işe yaradığını belirler. Öncelik değeri yalnız çekişenler arasında bir şey değiştirir. Yavaş açılan bir kabuk, geciken bir bağlantı ya da takılan bir düzenleyici genellikle az isteyen süreçlerdir ve onların sorunu öncelik değil, başka bir kaynaktır. Ağır bir toplu işi geri çekmek ise gerçekten işe yarar — ama kazanan, operatörün önemsediği süreç değil, öteki ağır süreçler olur.

Yanlış tanı sayısı son satırlarda duruyor: NI sütunu 6 denemenin 6’sında değişti, yük 6 denemenin 0’ında düştü. “Önceliği düşürdüm, yük azaldı” cümlesi altı denemenin altısında yanlıştır. İkinci tohumda hedefin payı 43.200’den 12.786’ya iniyor, toplam iş 239.078’den 239.450’ye çıkıyor ve yük 3,98’den 3,99’a gidiyor: pay büyük ölçüde değişiyor, yük yine değişmiyor. Payın ne kadar düşeceği kurguya bağlıdır; yükün değişmemesi değildir.

İşlemci önceliğinin hiç işe yaramadığı bir durum daha vardır ve ölçüm dışında kalır: darboğaz işlemci değilse renice hiçbir şeye dokunmaz. Diske ya da ağa bağlı bekleyen bir süreç zaten çekirdek istemiyordur. Girdi/çıktı önceliği için ayrı bir araç, ionice vardır; katı zamanlama sınıfları ise chrt ile ayarlanır ve yanlış kullanıldığında bir sürecin sistemi tamamen kilitlemesine yol açabilir. Darboğazın hangisi olduğunun ayrımı bu kursun günlükler konusunda ölçülür.

Kalıtım ve Doğru Kullanım

Öncelik değerinin ölçülmeyen ama belirleyici bir özelliği vardır: çocuk süreçler onu devralır. Çatallanan her süreç ana sürecin değeriyle doğar. Bunun pratik sonucu, bir ağacın tamamını geri çekmenin tek çağrıyla yapılabilmesidir — sarmalayıcı betik nice ile başlatıldığında altındaki bütün adımlar aynı değerle koşar. Aynı özellik ters yönde de işler: bir zamanlayıcının ya da servis yöneticisinin verdiği değer, oradan başlayan her şeye yapışır ve etkileşimli oturumda ölçülen davranış orada yeniden üretilmez.

Ölçümden çıkan üç kullanım kuralı vardır.

Değeri başlangıçta ver. renice çalışan bir sürecin o ana kadarki payını geri getirmez; yalnız bundan sonrasını değiştirir. Uzun bir toplu iş, çekişmeye girdikten sonra değil, başlarken geri çekilmelidir. Bir işin geri çekilmiş başlatılması nice ile tek satırdır ve kalıtım sayesinde alt adımları da kapsar.

Yararlanacak tarafın çekişiyor olduğunu doğrula. Ölçümün en keskin sayısı buydu: hafif süreçlere giden pay sıfır. Bir sürecin yavaşlığı öncelikten geliyorsa o süreç çekirdek için sıraya girmiş olmalıdır. Sıraya girmemiş bir sürece öncelik vermek hiçbir sayıyı değiştirmez.

Yükü öncelikle çözmeye çalışma. Yük, yapılan işin toplamıdır; öncelik o toplamı bölüştürür. Yükü gerçekten azaltan şeyler başkadır: işi azaltmak, işi zamana yaymak, kapasite eklemek ya da işi durdurmak. İlk üçü bu kursun kapsamı dışındadır; dördüncüsü önceki dersin konusuydu ve bedeli oradaki tabloda ölçüldü.

Bir uyarı da ters uçtan gelir. Öncelik değerini negatife çekmek yalnız yetkili kullanıcının yapabileceği bir işlemdir ve etkisi bu tablonun aynadaki görüntüsüdür: kayırılan süreç pay kazanır, kaybeden öteki her şeydir. Sistem yönetim araçlarının kendisi geri çekilmiş değerlerle koşuyorsa, arıza anında operatörün kabuğu da yavaşlar.

Özet

  • NI sütunu operatörün girdisi olan öncelik değerini, PRI sütunu çizelgeleyicinin ondan hesapladığı değeri taşır; ölçek terstir ve yetkisiz kullanıcı değeri yalnız artırabilir.
  • Öncelik değeri bir ağırlığa çevrilir: -5 ağırlığı 3125, 19 ağırlığı 12’dir. Ağırlıkların anlamı yalnız doymuş bir sistemde vardır.
  • En çok işleyen sürecin değeri uca çekildiğinde payı 41.878’den 0’a iner; bu, araç çıktısında görülen tek değişikliktir.
  • Toplam iş 237.906’dan 238.244’e gider (yüzde 0,14, çözünürlüğün altında) ve yük yedi denemede de 3,97 kalır. Öncelik işi azaltmaz, bölüştürür.
  • Boşalan 41.878 birimin yüzde 94’ü öteki ağır süreçlere, hafif süreçlere yüzde 0’ı gider; hafif süreçler zaten doyuyordu.
  • “Önceliği düşürdüm, yük azaldı” tanısı altı denemenin altısında yanlıştır: NI sütunu 6/6 değişir, yük 0/6 düşer.

Sonraki Adım

Öncelik bir sürecin ne kadar hızlı koşacağını etkiler, ne kadar tüketebileceğini sınırlamaz. Sınırlamanın kendi mekanizması vardır: süreç başına konan kaynak kısıtları. Sonraki ders bu kısıtları ölçer ve şu soruyu sorar: bir sınır aşıldığında ortaya çıkan hata iletisi, sınırın hangisi olduğunu söylüyor mu — yoksa iki ayrı neden aynı cümleyle mi görünüyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat