İçeriğe geç
academia.sh

Ders 05 / 22

Kaynak Sınırları

Sekiz ayrı arıza nedeni üç hata iletisine ve bir sessizliğe çöküyor: 60 olayın 13'ü hiçbir ileti üretmiyor, yalnız iletiye bakan tanı 21 kez, yalnız sınır okuyan tanı 29 kez, ikisini birleştiren tanı 13 kez yanılıyor.

İçindekiler

Öncelik bir sürecin ne kadar hızlı koşacağını etkiler, ne kadar tüketebileceğini sınırlamaz. Önceki dersin tablosunda geri çekilen süreç yine de dilediği kadar dosya açabilir, dilediği kadar çocuk üretebilir, dilediği kadar bellek isteyebilirdi. Tüketimi sınırlamanın kendi mekanizması vardır.

Bu ders o mekanizmayı ve sınırın aşıldığında ortaya çıkan arızayı ölçer. Soru şudur: hata iletisi, sınırın hangisi olduğunu söylüyor mu — yoksa ayrı nedenler aynı cümleyle mi görünüyor.

Süreç Başına Kısıt

Her sürecin bir kaynak sınırı kümesi vardır ve her sınırın iki değeri bulunur. Yumuşak sınır yürürlükte olan değerdir; süreç onu katı sınıra kadar kendisi yükseltebilir. Katı sınır tavandır ve yalnız yetkili bir kullanıcı tarafından yükseltilebilir. Sınırlar kabuğun ulimit yerleşiğiyle okunur ve yazılır:

$ ulimit -a                # kurgudaki sunucunun sinirlari , ornek dokum
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
open files                      (-n) 1024
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 4096
virtual memory          (kbytes, -v) unlimited
$ ulimit -Hn               # kati sinir
4096
$ ulimit -Sn 2048          # yumusak siniri kati sinira kadar yukselt
$ cat /proc/1014/limits
Limit                     Soft Limit  Hard Limit  Units
Max open files            2048        4096        files
Max processes             4096        8192        processes

Bu döküm örnektir ve çalıştırılmamıştır. İki okuma yolu görünüyor: ulimit çağıran kabuğun sınırlarını verir, /proc altındaki dosya ise belirli bir sürecin sınırlarını. İkisi aynı şey değildir ve fark, bu dersteki tanı hatalarının bir bölümünün kaynağıdır.

Kritik kural kalıtımdır: sınırlar çatallanma anında ana süreçten devralınır ve sonradan dışarıdan değiştirilemez. Bunun üç sonucu vardır. Birincisi, bir kabukta ulimit ile yükseltilen sınır yalnız o kabuğun bundan sonraki çocuklarını kapsar. İkincisi, servis yöneticisinin başlattığı bir süreç operatörün kabuğundan hiçbir şey devralmaz; onun sınırları birim tanımında yazılır. Üçüncüsü, çalışan bir sürecin sınırını sonradan yükseltmek yerleşik bir kabuk komutuyla yapılamaz — süreç yeniden başlatılmalıdır. “Sınırı yükselttim ama servis hâlâ aynı hatayı veriyor” cümlesinin ölçüsü budur.

Süreç başına kısıt tek kısıt türü değildir. Bir süreç öbeğinin toplamına konan kısıtlar ayrı bir mekanizmadır; ad alanı ve denetim grubu bu kursta ele alınmaz ve Çekirdek Arayüzleri ve İzolasyon kursuna bırakılır. Buradaki her sayı tek bir sürecin sınırıdır.

  • SY30. Gözlem penceresinde 60 arıza olayı görülür.
  • SY31. Arızaların sekiz ayrı nedeni vardır ve nedenler eşit sıklıkta değildir; göreli sıklıkları modelde sabittir.
  • SY32. Sekiz neden üç hata iletisi ve bir sessizlik üretir. Her ileti iki ayrı nedene karşılık gelir.
  • SY33. Dosya boyutu ve işlemci zamanı sınırları hiçbir ileti üretmez: süreç sinyalle öldürülür ve geriye yalnız bir çıkış durumu kalır.
  • SY34. Nedenlerin altısı süreç başına sınırdır; ikisi (sistem geneli tanıtıcı ve boş bellek) süreç başına değildir ve sürecin kendi sınır okumasında görünmez.
  • SY35. Birinci tanı yolu yalnız hata iletisidir. İletiden çıkarılabilecek en iyi tanı, o iletiyi üreten nedenlerin en sık görülenidir.
  • SY36. İkinci tanı yolu sınır okumasıdır: sayacın hâlâ sınırında oturup oturmadığına bakılır.
  • SY37. Olayların bir bölümü geçicidir: bakıldığında sayaç geri düşmüştür ve okuma hiçbir şey göstermez. Kanıt kendiliğinden yok olmuştur.
  • SY38. İkinci tohum 20260219’dur.
# --- ortak tanim, birinci dersteki bicimiyle
TOHUM, IKINCI_TOHUM = 20260218, 20260219
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


# --- bu dersin katmani: sekiz neden , uc ileti , bir sessizlik
# (ad, ileti, surec basina sinir mi)
NEDEN = (
    ("surec sayisi siniri",      "kaynak gecici olarak kullanilamiyor", True),
    ("is parcacigi siniri",      "kaynak gecici olarak kullanilamiyor", True),
    ("surec basina tanitici siniri", "cok fazla acik dosya",            True),
    ("sistem geneli tanitici siniri", "cok fazla acik dosya",           False),
    ("adres uzayi siniri",       "bellek ayrilamadi",                   True),
    ("sistemde bos bellek yok",  "bellek ayrilamadi",                   False),
    ("dosya boyutu siniri",      "",                                    True),
    ("islemci zamani siniri",    "",                                    True),
)
SIKLIK = (5, 2, 8, 1, 4, 1, 2, 3)      # nedenlerin goreli sikligi
HAVUZ = [i for i, w in enumerate(SIKLIK) for _ in range(w)]


def arizalar(surecler_, tohum=TOHUM, sayi=60):
    """Pencere boyunca gorulen ariza olaylari. Kahin: her olayin GERCEK nedeni."""
    r = uretec(tohum)
    olay = []
    for _ in range(sayi):
        s = surecler_[r(len(surecler_))]
        ad, ileti, surec_basina = NEDEN[HAVUZ[r(len(HAVUZ))]]
        olay.append({"pid": s["pid"], "neden": ad, "ileti": ileti,
                     "surec_basina": surec_basina,
                     "gecici": r(10) < 4})     # bakildiginda sayac geri dusmus
    return olay


S = surecler()
OLAY = arizalar(S)
print("ariza olayi:", len(OLAY), "| farkli neden:", len({o["neden"] for o in OLAY}))
sayim = {}
for o in OLAY:
    sayim.setdefault(o["ileti"] or "(ileti yok , sinyalle olduruldu)", []).append(o["neden"])
print("ileti                                  olay  kac farkli neden")
for i, n in sorted(sayim.items()):
    print(f"  {i:38s} {len(n):4d}  {len(set(n)):16d}")
ariza olayi: 60 | farkli neden: 8
ileti                                  olay  kac farkli neden
  (ileti yok , sinyalle olduruldu)         13                 2
  bellek ayrilamadi                        12                 2
  cok fazla acik dosya                     19                 2
  kaynak gecici olarak kullanilamiyor      16                 2

Tablo dersin yapısını tek bakışta veriyor. Sekiz neden dört satıra çökmüş; her satır iki ayrı nedeni taşıyor. En üstteki satırda ileti yoktur: 13 olay hiçbir metin üretmemiştir. Dosya boyutu sınırını aşan süreç SIGXFSZ, işlemci zamanı sınırını aşan süreç SIGXCPU alır; işleyici tanımlanmamışsa süreç sessizce ölür ve geriye yalnız 128 artı sinyal numarası biçiminde bir çıkış kodu kalır. Arızanın metni yoktur, yalnız bir sayı vardır.

İletinin Gizlediği Neden

İleti bir nedeni gösteriyor gibi durur, ama gösterdiği şey nedenin ait olduğu çifttir. “Kaynak geçici olarak kullanılamıyor” iletisi hem süreç sayısı sınırından hem de iş parçacığı sınırından gelebilir; ikisi de çatallanmanın başarısız olması demektir ve çekirdek ikisini ayırt eden bir metin üretmez.

# Onceki blogun uzerine: OLAY, NEDEN oradan gelir.
# Iletiden cikarilabilecek EN IYI tani: her iletinin en sik nedeni.
ILETI_TANISI = {"kaynak gecici olarak kullanilamiyor": "surec sayisi siniri",
                "cok fazla acik dosya": "surec basina tanitici siniri",
                "bellek ayrilamadi": "adres uzayi siniri",
                "": "bilinmiyor"}


def iletiyle_tani(olaylar):
    return sum(1 for o in olaylar if ILETI_TANISI[o["ileti"]] != o["neden"])


def sinirla_tani(olaylar):
    """Ileti + `ulimit` okumasi: hangi sayac sinirinda oturuyor. Sayac hala
    sinirindaysa neden okunur; olay geciciyse sayac geri dusmustur."""
    return sum(1 for o in olaylar if o["gecici"])


def birlesik_tani(olaylar):
    """Once sinir okunur; sayac geri dusmusse iletiye donulur."""
    return sum(1 for o in olaylar
               if o["gecici"] and ILETI_TANISI[o["ileti"]] != o["neden"])


print("tani yolu                             yanlis tani  /", len(OLAY))
print(f"  yalniz hata iletisi                    {iletiyle_tani(OLAY):9d}")
print(f"  yalniz sinir okumasi                   {sinirla_tani(OLAY):9d}")
print(f"  sinir okumasi , yoksa ileti            {birlesik_tani(OLAY):9d}")
print("  bunlarin icinde gecici olay          ",
      f"{sum(1 for o in OLAY if o['gecici']):9d}")
print("kahin: her olayin gercek nedeni biliniyor , kurguyu biz urettik")
print()
print("neden                            olay  iletiden dogru cikar mi")
for ad, ileti, _ in NEDEN:
    n = sum(1 for o in OLAY if o["neden"] == ad)
    print(f"  {ad:32s} {n:3d}  {'evet' if ILETI_TANISI[ileti] == ad else 'hayir'}")
tani yolu                             yanlis tani  / 60
  yalniz hata iletisi                           21
  yalniz sinir okumasi                          29
  sinir okumasi , yoksa ileti                   13
  bunlarin icinde gecici olay                  29
kahin: her olayin gercek nedeni biliniyor , kurguyu biz urettik

neden                            olay  iletiden dogru cikar mi
  surec sayisi siniri               13  evet
  is parcacigi siniri                3  hayir
  surec basina tanitici siniri      16  evet
  sistem geneli tanitici siniri      3  hayir
  adres uzayi siniri                10  evet
  sistemde bos bellek yok            2  hayir
  dosya boyutu siniri                3  hayir
  islemci zamani siniri             10  hayir

Alt tablo hangi nedenlerin iletiden okunabildiğini söylüyor: sekiz nedenin üçü doğru çıkarılabiliyor, beşi çıkarılamıyor. Çıkarılamayanlar iki sınıftır — bir çiftin daha seyrek üyesi olanlar ve hiç ileti üretmeyenler. İşlemci zamanı sınırı tek başına 60 olayın 10’unu oluşturuyor ve hakkında yazılmış tek bir kelime yok.

Yalnız iletiye bakan tanı 21 kez yanılıyor. Bu, gerçekte olabilecek en iyi ileti tabanlı tanıdır: her ileti için en sık nedeni seçtik. Operatör dağılımı bilmiyorsa daha da kötüsünü seçer.

Kanıt Kendiliğinden Yok Oluyor

Doğru tepki iletiye güvenmemek ve sınırı okumaktır: sürecin sayaçlarına bakılır, hangisinin tavana dayandığı görülür. Bu yolun kendi zaafı vardır ve tabloda duruyor.

Yalnız sınır okuyan tanı 29 kez yanılıyor — ileti tabanlı tanıdan daha kötü. Nedeni tek kelimeyle söylenebilir: geçicilik. Başarısız bir ayırma denemesi sayacı yükseltmez; denemenin ardından sayaç geri düşer. Operatör olaydan sonra baktığında bütün sayaçlar sınırın altındadır ve okuma “her şey yolunda” der. Altmış olayın 29’u bu durumdadır.

Bu, kursun ikinci iddiasının bu dersteki karşılığıdır: daha ayrıntılı bir tanı yolu, tek başına daha iyi tanı vermez. Sınır okuması iletiden çok daha zengin bir kanıttır — sayı verir, tavanı verir, oranı verir — ama kanıt olay anında yaşar ve sonrasında kaybolur.

İki yolu birleştiren tanı 13 kez yanılıyor: sınır okuması işe yaradığında ondan, geri düşmüş sayaçta iletiden yararlanıyor. Kalan 13 olay, hem kanıtı kaybolmuş hem de iletisi yanıltıcı olan olaylardır ve bu sayı indirilemez — çünkü geriye bakan hiçbir okuma o anki sayacı geri getiremez. Tek çözüm, olayı olduğu anda yakalamaktır: sınıra yaklaşmayı bir eşikten izlemek, arızayı bekleyip sonra bakmak değil.

# Onceki bloklarin uzerine: arizalar, surecler ve uc tani yolu.
for tohum in (TOHUM, IKINCI_TOHUM):
    O2 = arizalar(surecler(tohum), tohum)
    gecici = sum(1 for o in O2 if o["gecici"])
    print(f"tohum {tohum}: olay {len(O2)} | ileti {iletiyle_tani(O2):2d}"
          f" | sinir {sinirla_tani(O2):2d} | birlesik {birlesik_tani(O2):2d}"
          f" | gecici {gecici:2d}"
          f" | sessiz {sum(1 for o in O2 if not o['ileti']):2d}")
tohum 20260218: olay 60 | ileti 21 | sinir 29 | birlesik 13 | gecici 29 | sessiz 13
tohum 20260219: olay 60 | ileti 15 | sinir 25 | birlesik  5 | gecici 25 | sessiz  7

İkinci tohumda bütün sayılar düşüyor: ileti 21 yerine 15, sınır 29 yerine 25, birleşik 13 yerine 5. Sayıların kendisi kurguya bağlıdır. Ayakta kalan şey sıralamadır: her iki tohumda da birleşik yol en iyi, yalnız sınır okuması en kötüdür ve ileti tabanlı tanı ikisinin arasında kalır. Bir tanı yolunun zenginliği, tek başına sıralamadaki yerini belirlemiyor.

Sınırların pratik yönetimi bu ölçümden çıkar. Sınırlar arıza çıksın diye değil, bir sürecin makineyi tüketmesini engellesin diye konur; iyi bir sınır aşıldığında arızayı sınırlar ama nedenini gizler. O yüzden sınır koymak ile sınıra yaklaşmayı izlemek birlikte yapılır. Süreç başına sınır ayrıca kaba bir araçtır: aynı kullanıcının bütün süreçlerini kapsayan bir süreç sayısı sınırı, tek bir hatalı betiği durdururken oturum açmayı da engelleyebilir.

Sınırın Dışındaki Ölüm

Ölçülen sekiz nedenin hepsi bir sınırın aşılmasıydı ve hepsinde ölen ya da başarısız olan süreç, sınırı aşan sürecin kendisiydi. Sınırların dışında kalan bir ölüm biçimi daha vardır ve tanıyı bambaşka bir yönden bozar.

Sistemin belleği gerçekten tükendiğinde çekirdek bir kurban seçer ve onu zorlayan sinyalle öldürür. Seçim, belleği tüketen sürece göre değil, bir puanlamaya göre yapılır; puanlamada sürecin o anki bellek tutumu ağır basar. Sonuç, çoğu zaman en çok bellek tutan sürecin ölmesidir — ki bu, belleği kaçıran süreç olmak zorunda değildir. Sızıntıyı yapan küçük bir süreç, sızıntının bastırdığı sistemde büyük ve masum bir sürecin ölümüne yol açar. Ölen sürecin kendi çıktısında hiçbir iz kalmaz; olayın tek kaydı sistem günlüğündedir.

İkinci bir sessizleştirici, dökümün kendisidir. Yukarıdaki örnek dökümde çekirdek dosyası boyutu sınırı sıfırdır ve bu yaygın bir varsayılan yapılandırmadır. Sıfır olduğunda, sinyalle ölen bir süreç arkasında incelenebilecek hiçbir şey bırakmaz. Bu dersin ilk tablosundaki 13 sessiz olay için elde kalan tek şey çıkış kodudur; döküm sınırı sıfır olduğu sürece o kod da tek başına kalır.

İki gözlem aynı yere çıkar. Bir arızanın nedenini olaydan sonra bulmak, kanıtın olaydan sonra da var olmasına bağlıdır: sayaç sınırında kalmalı, döküm yazılmalı, ölüm bir yere kaydedilmelidir. Üçünün de varsayılan davranışı kanıtı saklamamaktır. Sınır koymanın yanına konması gereken şey bu yüzden bir sayı değil, bir kayıt yoludur — arıza anındaki durumu yazan bir kanca ya da eşik izleme. Ölümün nereye kaydedildiği ve o kaydın ne kadar yaşadığı kursun günlükler konusunda ölçülür.

Özet

  • Her sınırın yumuşak ve katı iki değeri vardır; sınırlar çatallanma anında devralınır ve çalışan bir sürecin sınırı kabuktan sonradan değiştirilemez.
  • Sekiz arıza nedeni üç hata iletisine ve bir sessizliğe çöküyor; her ileti iki ayrı nedene karşılık geliyor ve 13 olay hiçbir metin üretmiyor.
  • İletiden yalnız üç neden doğru çıkarılabiliyor; işlemci zamanı sınırı tek başına 60 olayın 10’unu oluşturuyor ve hakkında hiçbir metin yok.
  • Üç tanı yolunun yanlış tanı sayısı: yalnız ileti 21, yalnız sınır okuması 29, ikisinin birleşimi 13. Daha zengin kanıt tek başına daha iyi tanı vermiyor.
  • Sınır okumasının zaafı geçiciliktir: 60 olayın 29’unda sayaç bakıldığında geri düşmüştür. Kalan 13 yanlış tanı geriye bakan hiçbir okumayla indirilemez.
  • İkinci tohumda sayılar 15, 25 ve 5’e iniyor; ayakta kalan sonuç sıralamadır — birleşik yol en iyi, yalnız sınır okuması en kötüdür.

Sonraki Adım

Bu konu boyunca süreçlere operatörün penceresinden bakıldı: listelenen, aranan, arka plana alınan, sinyal gönderilen, önceliği değiştirilen ve sınırlanan tek tek süreçler. Hepsinin ortak bir zaafı vardı — operatör oradayken çalışıyorlardı. Bir sunucunun asıl işi ise kimse bakmıyorken sürer. Kursun sonraki konusu bu işi kalıcı bir nesneye bağlayan katmanı ele alır: servis yöneticisi, birimler ve aralarındaki bağımlılıklar. Orada ölçülecek ilk şey, bir birimin “çalışıyor” görünmesinin ne anlama geldiğidir.

İ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