İçeriğe geç
academia.sh

Ders 17 / 22

Dosya Sistemleri

Aynı dosyaları taşıyan dizin üç iç düzende 449.985.000, 125.312 ve 59.800 karşılaştırmaya mal oluyor; sağlama taşımayan tasarımda bozuk bloğa dokunan 54 okumanın 54'ü hatasız dönüyor.

İçindekiler

Önceki ders bölümü aygıtın gövdesine çizilmiş bir aralık olarak bıraktı: başlangıcı, bitişi ve bir tür etiketi olan, içi hakkında hiçbir şey söylenmeyen bir parça. O aralığın içine bir düzen kurulduğu anda birkaç karar alınır ve bu kararların çoğu sonradan değiştirilemez.

Bu dersin sorusu şudur: aynı bölümde, aynı dosyalarla, farklı tasarımlar hangi noktalarda ayrışır. Ölçülecek iki eksen var — bir dizinde ad aramanın maliyeti ve bozulmuş bir bloğun okuma sırasında fark edilip edilmediği. İkisi de sayılabilir ve ikisi de araç çıktısında görünmez.

Bir Dosya Sistemi Ne Tutar

Bir dosya sistemi (file system) dört ayrı defteri aynı bölümün içinde tutar. Birincisi ad defteridir: dizin girdileri, adı bir kimliğe bağlar. İkincisi meta veri defteridir: her dosyanın boyutu, izinleri, sahibi, zaman damgaları ve içeriğinin hangi bloklarda durduğu. Üçüncüsü alan defteridir: hangi blokların dolu, hangilerinin boş olduğu. Dördüncüsü tutarlılık defteridir: bir güncelleme yarıda kalırsa diskteki yapıların birbiriyle uyumlu kalmasını sağlayan kayıt.

Bu dört defterin nasıl tutulacağı tasarımdan tasarıma değişir ve fark buradan doğar. Karşılaşılan aileler arasında XFS, Btrfs, ZFS ve genişletilmiş dosya sistemi ailesi sayılabilir; bu ders adları değil, aralarındaki eksenleri ölçer. Eksenler dörttür: meta veri defterinin boyutu biçimlendirme anında mı sabitlenir yoksa gerektikçe mi büyür; ad defteri sıralı bir liste midir yoksa aranabilir bir yapı mıdır; alan defteri blok blok mu yoksa bitişik aralıklar hâlinde mi tutulur; okunan bloğun bozulmadığı sınanır mı.

Hangi ailenin öntanımlı olarak kurulacağı dağıtıma göre değişir ve bu ders bir aileyi diğerine üstün saymaz. Ölçülen şey seçimin bedelidir, sıralaması değil. Bir eksende ucuz olan tasarım başka bir eksende pahalıdır ve hangi eksenin ağır bastığı iş yükünün kendisine bağlıdır; iş yükü değiştiğinde karar da eskir, ama dosya sistemi yerinde durmaya devam eder.

Kurulmuş bir bölümün hangi tasarımı taşıdığı okunarak öğrenilir. lsblk -f her bölümün tür etiketini ve kimliğini yazar, blkid aynı bilgiyi tek tek verir. Aşağıdaki döküm biçimi gösteren bir örnek dökümdür; çalıştırılmamıştır ve bu derste hiçbir sayı iddiası buradan gelmez.

NAME   FSTYPE      FSVER  LABEL   UUID                       MOUNTPOINT
sdb
├─sdb1 ext4        1.0    kok     11111111-1111-1111-1111-1  /
├─sdb2 xfs                veri    22222222-2222-2222-2222-2  /veri
├─sdb3 ext4        1.0    gunluk  33333333-3333-3333-3333-3  /gunluk
└─sdb4 swap        1      takas   44444444-4444-4444-4444-4  [SWAP]

Dökümdeki kimlik alanı sonraki derslerde işe yarayacak: bir bölümün adı değişebilir, kimliği değişmez.

Biçimlendirme Anında Sabitlenen Sayılar

Bir bölüme dosya sistemi kurmak, o bölümün başına bir üstbilgi yazıp defterlerin yerini ayırmaktır. Bu işlem bir dizi sayıyı sabitler ve sabitlenenlerin çoğu sonradan değiştirilemez ya da ancak dosya sistemi boşaltılarak değiştirilebilir.

Sabitlenen ilk sayı blok boyudur: dosya sisteminin ayırdığı en küçük birim. İkincisi meta veri kaydı sayısıdır; sabit tablo tutan tasarımlarda bu sayı biçimlendirme anında belirlenir ve dosya sistemi büyütülmedikçe artmaz. Üçüncüsü dizinin iç düzenidir ve aşağıda ölçülüyor. Dördüncüsü sağlamanın hangi defterleri kapsadığıdır. Beşincisi tutarlılık kaydının kipidir: yalnız meta veri mi günlüğe alınacak yoksa veri de mi. İşletim Sistemi Kavramları kursundaki dosya sistemi soyutlaması dersi günlüklü düzenin yazma çarpanını ölçmüştü; o ölçüm burada tekrarlanmaz.

Bir de alan defterinin biçimi vardır. Blok listesi tutan bir tasarımda her blok için bir kayıt gerekir; bitişik aralık tutan bir tasarımda peş peşe uzanan yüz blok tek kayıttır. İkincisi büyük ve parçalanmamış dosyalarda çok daha az meta veri üretir, parçalanma arttıkça kazancı erir. Hangi biçimin seçileceği dosya boyu dağılımına bağlıdır ve bu dağılım biçimlendirme anında bilinmez; karar tahminle verilir.

Yıkıcı komut sınırı burada başlıyor: bir bölümü biçimlendiren komut, o bölümdeki her şeyi geri alınamaz biçimde siler ve yanlış bölüm adı yazılırsa uyarı vermeden çalışabilir. Bu ders biçimlendirme komutunu çalıştırılabilir tam biçimde vermez. Güvenli sınama yolu, bir dosyayı geri döngü aygıtı olarak bağlayıp denemeyi orada yapmaktır.

Aynı Dizin, Üç İç Düzen

Kurgu sunucunun /veri bölümünde ölçüm toplayıcı birimin ürettiği dosyalar tek bir dizinde birikiyor. Dizin bir tablodur ve o tablonun iç düzeni bir tasarım kararıdır. İşletim Sistemi Kavramları kursundaki dosya sistemi soyutlaması dersi bir dizini okumayı tek adım saymış ve girdi sayısının adım sayısını değiştirmediğini varsaymıştı. Bu ders o varsayımın bedelini ödüyor.

  • DP8 — Ölçülen iş yeni bir dosya oluşturmaktır. Her oluşturma önce bir aramadır: aynı ad dizinde var mı. Sayılan şey bu aramadaki karşılaştırmalardır.
  • DP9 — Sıralı liste düzeninde arama baştan sona gider. Karma tabanlı düzende ad bir kovaya düşer ve yalnız o kovadaki adlarla karşılaştırılır; kova sayısı 4096’dır ve biçimlendirme anında sabitlenmiştir. Ağaç düzeninde bir dal 200 ad taşır ve arama, ağacın derinliği kadar adımda biter.
  • DP10 — Karşılaştırma sayısı ölçülür; gerçek zaman ölçülmez.
"""Dizin arama: ayni dosyalari tasiyan dizin , uc farkli ic duzen."""
KOVA = 4096          # karma tabanli dizindeki kova sayisi
DAL = 200            # B-agaci dugumunun tasidigi ad sayisi


def kurma_maliyeti(n, duzen):
    """Her yeni ad once ARANIR (var mi diye). Toplam karsilastirma."""
    if duzen == "dogrusal":
        return n * (n - 1) // 2
    if duzen == "karma":
        return sum(i // KOVA + 1 for i in range(n))
    derinlik, toplam = 1, 0
    for i in range(1, n + 1):
        if i > DAL ** derinlik:
            derinlik += 1
        toplam += derinlik
    return toplam


DUZEN = ("dogrusal", "karma", "agac")
OLCEK = (1000, 5000, 10000, 30000, 100000)
TABAN = {d: kurma_maliyeti(1000, d) for d in DUZEN}

print("dosya sayisi  ", "  ".join(f"{d:>12s}" for d in DUZEN))
for n in OLCEK:
    print(f"{n:12d}  ", "  ".join(f"{kurma_maliyeti(n, d):12d}" for d in DUZEN))
print()
print("1000 dosyadan dogrusal olceklenen tahmin ile gercek")
print("dosya sayisi  duzen         tahmin       gercek   sapma kat  yanlis tani")
yanlis = 0
for n in OLCEK:
    for d in DUZEN:
        tahmin = TABAN[d] * n // 1000
        gercek = kurma_maliyeti(n, d)
        kat = gercek / tahmin
        y = kat > 2
        yanlis += y
        print(f"{n:12d}  {d:10s} {tahmin:12d} {gercek:12d}  {kat:10.2f}  {str(y):>11s}")
print("yanlis tani:", yanlis, "/", len(OLCEK) * len(DUZEN))
dosya sayisi       dogrusal         karma          agac
        1000         499500          1000          1800
        5000       12497500          5904          9800
       10000       49995000         17712         19800
       30000      449985000        125312         59800
      100000     4999950000       1271200        259800

1000 dosyadan dogrusal olceklenen tahmin ile gercek
dosya sayisi  duzen         tahmin       gercek   sapma kat  yanlis tani
        1000  dogrusal         499500       499500        1.00        False
        1000  karma              1000         1000        1.00        False
        1000  agac               1800         1800        1.00        False
        5000  dogrusal        2497500     12497500        5.00         True
        5000  karma              5000         5904        1.18        False
        5000  agac               9000         9800        1.09        False
       10000  dogrusal        4995000     49995000       10.01         True
       10000  karma             10000        17712        1.77        False
       10000  agac              18000        19800        1.10        False
       30000  dogrusal       14985000    449985000       30.03         True
       30000  karma             30000       125312        4.18         True
       30000  agac              54000        59800        1.11        False
      100000  dogrusal       49950000   4999950000      100.10         True
      100000  karma            100000      1271200       12.71         True
      100000  agac             180000       259800        1.44        False
yanlis tani: 6 / 15

Üç sayı yan yana. Kâhin: 30.000 dosyayı tek dizinde oluşturmak sıralı listede 449.985.000, karma tabanlı düzende 125.312, ağaç düzeninde 59.800 karşılaştırmadır. Araç çıktısı: üç dizin de ls ile aynı 30.000 adı verir ve dizin girdisi sayısı her üçünde eşittir; iç düzen çıktıda yoktur. Yanlış tanı: küçük bir dizinde ölçüp doğrusal ölçekleyen tahmin, on beş durumun altısında iki katından fazla sapıyor.

Tabloda üstünlük yok, eğri farkı var. Bin dosyada ağaç düzeni karma düzenden pahalıdır (1800’e karşı 1000); yüz bin dosyada beş kat ucuzdur. Karma tabanlı düzenin de bir sınırı görünüyor: kova sayısı biçimlendirme anında sabitlendiği için zincirler uzuyor ve 100.000 dosyada maliyet doğrusal tahminin 12,71 katına çıkıyor. Sabitlenen her sayı, ölçek büyüdükçe ödenen bir borçtur.

Sağlama Toplamı Olan ve Olmayan Okuma

İkinci eksen okumanın doğruluğudur. Bir blok diskte kendiliğinden bozulabilir; yazıldığı gibi kalmadığı hâlde aygıt onu hatasız döndürür. Tasarım bunu sınamıyorsa bozulma sessizdir: uygulama yanlış baytı doğru sanır.

  • DP11 — Ortak tanımın /veri dosya sistemi 30.000 dosya taşır ve her dosya bir blok tutar. Dosya düğümü tablosu 2048 bloktur; bir bloğa 16 dosya düğümü sığar.
  • DP12 — Bir dosyayı okumak iki bloğa dokunur: kendi veri bloğu ve dosya düğümünün bulunduğu tablo bloğu.
  • DP1340 blok bozulur; hangileri olduğu üreteçle seçilir. Sağlama taşıyan bir tasarım bozulmayı okuma anında yakalar, taşımayan yakalamaz.
"""Saglama toplami: bozulan blok okuma sirasinda yakalaniyor mu."""
TOHUM = 20260218
BLOK = 4096
DOSYA = 30000                 # ortak tanimin /veri dosya sistemi
DUGUM_BLOKTA = 16             # 4096 baytlik bloga 16 dosya dugumu (256 bayt)
META = 2048                   # 32768 dugumluk tablo , blok cinsinden
BOZUK = 40


def uretec(tohum):
    d = tohum

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


def bozulanlar(tohum=TOHUM, adet=BOZUK, veri=DOSYA, meta=META):
    """Kahin: hangi bloklar gercekten bozuldu. Ilk `veri` tanesi veri blogu."""
    r = uretec(tohum)
    return {r(veri + meta) for _ in range(adet)}


def okuma(tasarim, bozuk, veri=DOSYA):
    """Her dosya kendi veri blogunu ve dugum tablosundaki blogunu okur."""
    veri_saglama, meta_saglama = tasarim
    yakalanan = sessiz = 0
    for i in range(veri):
        vb = i in bozuk
        mb = (veri + i // DUGUM_BLOKTA) in bozuk
        if (vb and veri_saglama) or (mb and meta_saglama):
            yakalanan += 1
        elif vb or mb:
            sessiz += 1
    return {"yakalanan": yakalanan, "sessiz": sessiz}


TASARIM = {"saglama yok": (False, False), "meta saglamali": (False, True),
           "veri+meta saglamali": (True, True)}
B = bozulanlar()
print("bozulan blok:", len(B), "| veri blogu:", sum(1 for x in B if x < DOSYA),
      "| meta veri blogu:", sum(1 for x in B if x >= DOSYA))
print("kahin: bozuk bloga dokunan okuma:", okuma((True, True), B)["yakalanan"],
      "/", DOSYA)
print()
print("tasarim               hatayla donen  sessizce bozuk donen  yanlis tani")
for ad, t in TASARIM.items():
    o = okuma(t, B)
    print(f"  {ad:20s} {o['yakalanan']:13d}  {o['sessiz']:20d}  {o['sessiz']:11d}")
ek = -(-(DOSYA + META) * 4 // BLOK)
print("saglama toplami deposu:", ek, "blok |", round(ek / (DOSYA + META) * 100, 3),
      "yuzde")
print()
for tohum in (TOHUM, 20260219):
    b2 = bozulanlar(tohum)
    print(f"tohum {tohum}: saglamasiz sessiz {okuma((False, False), b2)['sessiz']:4d}"
          f"  meta saglamali {okuma((False, True), b2)['sessiz']:4d}"
          f"  tam saglamali {okuma((True, True), b2)['sessiz']:4d}")
bozulan blok: 40 | veri blogu: 38 | meta veri blogu: 2
kahin: bozuk bloga dokunan okuma: 54 / 30000

tasarim               hatayla donen  sessizce bozuk donen  yanlis tani
  saglama yok                      0                    54           54
  meta saglamali                  16                    38           38
  veri+meta saglamali             54                     0            0
saglama toplami deposu: 32 blok | 0.1 yuzde

tohum 20260218: saglamasiz sessiz   54  meta saglamali   38  tam saglamali    0
tohum 20260219: saglamasiz sessiz   70  meta saglamali   38  tam saglamali    0

Üç sayı yan yana. Kâhin: 40 blok bozuldu ve bunlara dokunan okuma sayısı 54’tür — 38 dosya kendi veri bloğundan, 16 dosya iki bozuk tablo bloğundan etkileniyor. Araç çıktısı: sağlama taşımayan tasarımda okuma çağrılarının hepsi hatasız dönüyor; hiçbir araç bir şey bildirmiyor. Yanlış tanı: o tasarımda 54 okuma bozuk baytı doğru sanıyor; meta veri sağlaması eklendiğinde 38’e iniyor, veri de sağlandığında sıfır oluyor.

Bedeli tabloda: 32.048 bloğun sağlama 32 blok yer tutuyor, yani yüzde 0,1. Yer maliyeti küçük, işlem maliyeti ise her okumaya eklenen bir hesaptır ve bu ders onu saymaz. İkinci tohumda bozuk blok dağılımı değişiyor, sessiz okuma 54 yerine 70 çıkıyor; sıralama değişmiyor. Sayının kendisi kurguya bağlı, sıfırla arasındaki fark değil.

Buradaki asıl sonuç şudur: sağlama olmayan bir tasarımda “hata yok” çıktısı, “veri doğru” demek değildir. Bozulma ancak bir tutarlılık denetimi çalıştırıldığında ya da veri kullanıldığında ortaya çıkar; ikisi de okumadan sonradır. Kursun üçüncü iddiası burada da işliyor — geç bakan az görür.

Tutarlılık denetimi de bedava değildir ve ne yaptığına dikkat etmek gerekir. fsck sınıfı araçlar defterleri baştan sona gezip birbirleriyle uyumlu olup olmadıklarına bakar; buldukları uyumsuzluğu onararak çıkarlar ve onarım bazen bir dosyayı sahipsiz kayıtların toplandığı bir dizine taşımak demektir. Bu iş takılı bir dosya sisteminde yapılamaz: araç çalışırken dosya sistemi değişmemelidir, aksi hâlde araç kendi okuduğu defteri geçersiz kılar. Bu yüzden takılı bir dosya sisteminde onarım komutu çalıştırılabilir tam biçimde verilmez; sınama, dosya sistemi ayrıldıktan sonra ya da yalnız rapor üreten seçenekle yapılır.

Denetimin bulabildiği şeyin sınırı da vardır. Defterler arası uyumsuzluk bulunur; bir veri bloğunun içeriğinin değişmiş olduğu bulunamaz, çünkü doğru içeriğin ne olduğunu söyleyen bir kayıt yoktur. Tabloda sağlamanın satın aldığı şey tam olarak budur: doğru içeriğin ne olduğunu bilmeye değil, yanlış olduğunu anlamaya yarayan 32 bloklu bir kayıt.

Özet

  • Bir dosya sistemi ad defteri, meta veri defteri, alan defteri ve tutarlılık defterini aynı bölümde tutar; aileler arasındaki fark bu dört defterin nasıl tutulduğudur, bir üstünlük sıralaması değildir.
  • 30.000 dosyayı tek dizinde oluşturmak sıralı listede 449.985.000, karma tabanlı düzende 125.312, ağaç düzeninde 59.800 karşılaştırmadır; üç dizin de ls ile aynı listeyi verir.
  • Küçük bir dizinde ölçüp doğrusal ölçekleyen tahmin on beş durumun altısında iki katından fazla sapıyor; karma düzende kova sayısının biçimlendirme anında sabitlenmesi 100.000 dosyada tahmini 12,71 kat aşırıyor.
  • Sağlama taşımayan tasarımda bozuk bloğa dokunan 54 okumanın 54’ü hatasız dönüyor; meta veri sağlamasıyla 38, veri sağlamasıyla 0 oluyor. Depolama bedeli 32 blok, yüzde 0,1.
  • Biçimlendirme anında sabitlenen her sayı bir tavandır; bir sonraki ders o tavanlardan birine çarpmanın ne göründüğünü ölçer.

Sonraki Adım

Bu ders meta veri defterini bir tasarım ekseni olarak saydı ama içine bakmadı: bir dosyanın adı dışındaki her şeyini taşıyan kaydın kendisi bir kaynaktır ve tükenebilir. Sonraki ders o kaydı ölçer ve kursun en çarpıcı sayısını çıkarır — aynı dosya sisteminde blok doluluk 0,0125 iken yazma başarısız oluyor. Disk yüzde bir dolu ve sistem dolu; doluluk yüzdesine bakan tanı burada kesinlikle yanılı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