İçeriğe geç
academia.sh

Ders 01 / 15

Dosya Tanıtıcıları

Bir işaretçinin aynı bellek konumunu gösterip göstermediği eşitlikle ölçülür; bir dosya tanıtıcısının aynı açık dosya kaydını paylaşıp paylaşmadığı ise konum davranışıyla ölçülür — tanıtıcının kendisi bir tamsayıdır ve hiçbir şey söylemez.

Yayın Güncelleme

Bu derste

  • dört sınıfı (kopya, ortak, aktarım, gözlenmeyen) çekirdeğin tek bir koşumundaki on beş gözlemle ayırt eder
  • dup ile çoğaltılan bir tanıtıcının aynı açık dosya kaydını paylaştığını konum davranışıyla gösterir
  • ayrı bir open çağrısının neden ayrı bir konum tuttuğunu, iki tanıtıcının sayısal değerine bakmadan açıklar
İçindekiler

Önceki kurs bir bloğu kimin bırakacağını sordu: bir işaretçi bir kaydı gösterdiğinde, o kaydı serbest bırakma yükümlülüğü hangi tarafa aitti. Bu kurs farklı bir soru sorar. Bir taraf bir şeyi değiştirdiğinde öteki taraf bunu görür mü? fork belleği çoğaltır, dup bir açık dosyayı paylaştırır, bir iş parçacığı adres uzayını paylaşır, bir boru baytları bir kez taşır. Her düzenek bu soruya farklı yanıt verir, ve kurs boyunca bu yanıtın dört sabit adı var: kopya, ortak, aktarım, gözlenmeyen. Bu ders dördünü tek bir koşumda gösteriyor, sonra ilkine — dosya tanıtıcılarına — biraz daha yakından bakıyor.

Kim Görüyor Sorusu: Çekirdeğin Tam Koşumu

Ölçüm iki parçadan kuruluyor. gozlem.h ve gozlem.c, bir değişikliğin adını, sınıfını ve öteki tarafın onu görüp görmediğini kaydeden küçük bir birim. Sayılar yalnız gozlem.c içinde tutuluyor; hiçbir ders bu sayılara dışarıdan dokunmuyor.

// gozlem.h — bir degisikligin kime ulastigini siniflayan kayit
#ifndef GOZLEM_H
#define GOZLEM_H

enum Sinif { KOPYA, ORTAK, AKTARIM, GOZLENMEYEN };

/* Bir degisikligi kaydeder: gordu, degisikligi yapmayan tarafin onu
   okuyup okuyamadigidir (1 ya da 0). */
void gozlem(const char *degisiklik, enum Sinif sinif, int gordu);

/* Sinif basina sayilari ve ulasan / ulasmayan toplamini basar. */
void gozlem_ozeti(void);

#endif
// gozlem.c — sayilar yalniz bu birimde tutulur
#include <stdio.h>
#include "gozlem.h"

static const char *const adlar[] = { "kopya", "ortak", "aktarim", "gozlenmeyen" };
static int sayilar[4];
static int ulasan = 0;
static int ulasmayan = 0;

void gozlem(const char *degisiklik, enum Sinif sinif, int gordu) {
    /* ortak ve aktarim ulasir, kopya ve gozlenmeyen ulasmaz */
    int beklenen = (sinif == ORTAK || sinif == AKTARIM);
    sayilar[sinif]++;
    if (gordu) {
        ulasan++;
    } else {
        ulasmayan++;
    }
    printf("%-34s %-8s %s%s\n", degisiklik, gordu ? "gordu" : "gormedi",
           adlar[sinif], gordu == beklenen ? "" : "  TUTARSIZ");
}

void gozlem_ozeti(void) {
    printf("siniflar: kopya %d, ortak %d, aktarim %d, gozlenmeyen %d\n",
           sayilar[KOPYA], sayilar[ORTAK], sayilar[AKTARIM], sayilar[GOZLENMEYEN]);
    printf("ulasan %d, ulasmayan %d\n", ulasan, ulasmayan);
}

Üçüncü birim, gorur.c, on beş küçük değişikliğin her birini bu iki işlevle ölçüyor. Bu ders çekirdeğin tamamını koşturuyor; kalan on dört ders yalnız kendi kesitini alacak, bu dosyayı yeniden yazmayacak.

// gorur.c — on bes degisiklik, her biri icin tek soru: kim goruyor
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "gozlem.h"

/* 1. Dosya tanitici: dup ile cogaltilan tanitici konumu paylasir */
static void tanitici(void) {
    int a = open("gorur.txt", O_CREAT | O_RDWR | O_TRUNC, 0600);
    if (write(a, "abcdef", 6) != 6) {
        return;
    }
    int b = dup(a);
    lseek(a, 2, SEEK_SET);
    gozlem("dup ile cogaltilan konum", ORTAK, lseek(b, 0, SEEK_CUR) == 2);
    close(b);
    close(a);
    unlink("gorur.txt");
}

/* 2. Surec: cocugun yazdigi degisken ebeveynde degismez */
static void surec(void) {
    int deger = 0;
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        deger = 1;
        _exit(deger);
    }
    int durum = 0;
    waitpid(p, &durum, 0);
    gozlem("fork sonrasi cocugun degiskeni", KOPYA, deger == 1);
}

/* 3. Sinyal: isleyicinin kaldirdigi bayrak ana akisa bir kez ulasir */
static volatile sig_atomic_t bayrak = 0;
static void isleyici(int s) {
    (void)s;
    bayrak = 1;
}
static void sinyal(void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof sa);
    sa.sa_handler = isleyici;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGUSR1, &sa, NULL);
    raise(SIGUSR1);
    gozlem("isleyicinin kaldirdigi bayrak", AKTARIM, bayrak == 1);
}

/* 4. Boru: cocugun yazdigi deger ebeveyne bir kez tasinir */
static void boru(void) {
    int uc[2];
    if (pipe(uc) != 0) {
        return;
    }
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        int deger = 42;
        close(uc[0]);
        ssize_t n = write(uc[1], &deger, sizeof deger);
        _exit(n == (ssize_t)sizeof deger ? 0 : 1);
    }
    close(uc[1]);
    int okunan = 0;
    ssize_t n = read(uc[0], &okunan, sizeof okunan);
    waitpid(p, NULL, 0);
    close(uc[0]);
    gozlem("boruya yazilan deger", AKTARIM, n == (ssize_t)sizeof okunan && okunan == 42);
}

/* 5. Is parcacigi: kilitle artirilan sayac ana is parcacigina ulasir */
static int sayac = 0;
static pthread_mutex_t kilit = PTHREAD_MUTEX_INITIALIZER;
static void *artir(void *arg) {
    (void)arg;
    pthread_mutex_lock(&kilit);
    sayac = 7;
    pthread_mutex_unlock(&kilit);
    return NULL;
}
static void is_parcacigi(void) {
    pthread_t t;
    pthread_create(&t, NULL, artir, NULL);
    pthread_join(t, NULL);
    pthread_mutex_lock(&kilit);
    int gordu = (sayac == 7);
    pthread_mutex_unlock(&kilit);
    gozlem("is parcaciginin yazdigi sayac", ORTAK, gordu);
}

/* 6. Bagli yapi: ikinci isaretciden yapilan degisiklik listede gorunur */
struct Dugum {
    int deger;
    struct Dugum *sonraki;
};
static void bagli(void) {
    struct Dugum c = { 3, NULL };
    struct Dugum b = { 2, &c };
    struct Dugum a = { 1, &b };
    struct Dugum *takma = &b;
    takma->deger = 20;
    int toplam = 0;
    for (struct Dugum *d = &a; d != NULL; d = d->sonraki) {
        toplam += d->deger;
    }
    gozlem("takma adla degisen dugum", ORTAK, toplam == 24);
}

/* 7. Halka arabellek: konan oge alan tarafa bir kez ulasir */
enum { KAPASITE = 4 };
struct Halka {
    int ogeler[KAPASITE];
    int bas;
    int adet;
};
static int halka_koy(struct Halka *h, int oge) {
    if (h->adet == KAPASITE) {
        return 0;
    }
    h->ogeler[(h->bas + h->adet) % KAPASITE] = oge;
    h->adet++;
    return 1;
}
static int halka_al(struct Halka *h, int *oge) {
    if (h->adet == 0) {
        return 0;
    }
    *oge = h->ogeler[h->bas];
    h->bas = (h->bas + 1) % KAPASITE;
    h->adet--;
    return 1;
}
static void halka(void) {
    struct Halka h = { { 0 }, 0, 0 };
    int alinan = 0;
    halka_koy(&h, 5);
    int ilk = halka_al(&h, &alinan) && alinan == 5;
    int ikinci = halka_al(&h, &alinan);
    gozlem("halkaya konan oge", AKTARIM, ilk && !ikinci);
}

/* 8. Karma tablosu: tablo anahtarin kendi kopyasini tutar */
struct Girdi {
    char anahtar[16];
    int deger;
};
static void karma(void) {
    char tampon[16] = "elma";
    struct Girdi g;
    strcpy(g.anahtar, tampon);
    g.deger = 1;
    strcpy(tampon, "armut");
    gozlem("eklendikten sonra degisen anahtar", KOPYA, strcmp(g.anahtar, "armut") == 0);
}

/* 9. Nesne benzeri tasarim: ayni nesneyi tutan iki kullanici */
struct Hesap {
    int bakiye;
};
static void hesap_yatir(struct Hesap *h, int tutar) {
    h->bakiye += tutar;
}
static int hesap_bakiye(const struct Hesap *h) {
    return h->bakiye;
}
static void nesne(void) {
    struct Hesap hesap = { 0 };
    struct Hesap *birinci = &hesap;
    const struct Hesap *ikinci = &hesap;
    hesap_yatir(birinci, 50);
    gozlem("bir kullanicinin yaptigi yatirma", ORTAK, hesap_bakiye(ikinci) == 50);
}

/* 10. Temizleme: bosaltilmayan tampon _exit ile yok olur */
static void temizleme(void) {
    int uc[2];
    if (pipe(uc) != 0) {
        return;
    }
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        close(uc[0]);
        FILE *akis = fdopen(uc[1], "w");
        if (akis == NULL) {
            _exit(1);
        }
        setvbuf(akis, NULL, _IOFBF, 256);
        fputs("tamponda kalan satir\n", akis);
        _exit(0);
    }
    close(uc[1]);
    char gelen[64];
    ssize_t n = read(uc[0], gelen, sizeof gelen);
    waitpid(p, NULL, 0);
    close(uc[0]);
    gozlem("bosaltilmadan cikilan tampon", GOZLENMEYEN, n > 0);
}

/* 11. Derleme sistemi (model): bagimliligi yazilmamis birim yeniden derlenmez */
struct Birim {
    int baslik_bagimli_mi;
    int derlendigi_surum;
};
static void derle(struct Birim *b, int baslik_surumu, int baslik_zamani, int nesne_zamani) {
    if (b->baslik_bagimli_mi && baslik_zamani > nesne_zamani) {
        b->derlendigi_surum = baslik_surumu;
    }
}
static void derleme(void) {
    struct Birim birim = { 0, 1 };
    derle(&birim, 2, 20, 10);
    gozlem("bagimliligi yazilmamis baslik", KOPYA, birim.derlendigi_surum == 2);
}

/* 12. Kitaplik (model): statik baglama kodun bir kopyasini alir */
struct Kitaplik {
    int surum;
};
struct Program {
    int gomulu_surum;
    const struct Kitaplik *bagli;
};
static void kitaplik(void) {
    struct Kitaplik k = { 1 };
    struct Program statik = { k.surum, NULL };
    k.surum = 2;
    gozlem("statik bagli kitaplik guncellemesi", KOPYA, statik.gomulu_surum == 2);
}

/* 13. Test: ayri surecte kosan testin degisikligi sonrakine gecmez */
static int ortak_durum = 0;
static void test(void) {
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        ortak_durum = 9;
        _exit(0);
    }
    waitpid(p, NULL, 0);
    gozlem("yalitilmis testin degisikligi", KOPYA, ortak_durum == 9);
}

/* 14. Hata ayiklayici (model): iki durak arasindaki ara deger */
static void hata_ayiklayici(void) {
    int degerler[3] = { 1, 2, 3 };
    int gorulen[2];
    gorulen[0] = degerler[0]; /* birinci durak */
    gorulen[1] = degerler[2]; /* ikinci durak */
    gozlem("iki durak arasindaki ara deger", GOZLENMEYEN,
           gorulen[0] == 2 || gorulen[1] == 2);
}

/* 15. Eniyileme (model): hicbir okumanin gormedigi yazma */
struct Adim {
    char islem;
    int deger;
};
static void eniyileme(void) {
    struct Adim akis[] = { { 'y', 1 }, { 'y', 2 }, { 'o', 0 } };
    int okuyan = 0;
    int son_yazma = -1;
    for (int i = 0; i < 3; i++) {
        if (akis[i].islem == 'y') {
            son_yazma = i;
        } else if (son_yazma == 0) {
            okuyan++;
        }
    }
    gozlem("okunmadan ezilen yazma", GOZLENMEYEN, okuyan > 0);
}

int main(void) {
    tanitici();
    surec();
    sinyal();
    boru();
    is_parcacigi();
    bagli();
    halka();
    karma();
    nesne();
    temizleme();
    derleme();
    kitaplik();
    test();
    hata_ayiklayici();
    eniyileme();
    gozlem_ozeti();
    return 0;
}
dup ile cogaltilan konum           gordu    ortak
fork sonrasi cocugun degiskeni     gormedi  kopya
isleyicinin kaldirdigi bayrak      gordu    aktarim
boruya yazilan deger               gordu    aktarim
is parcaciginin yazdigi sayac      gordu    ortak
takma adla degisen dugum           gordu    ortak
halkaya konan oge                  gordu    aktarim
eklendikten sonra degisen anahtar  gormedi  kopya
bir kullanicinin yaptigi yatirma   gordu    ortak
bosaltilmadan cikilan tampon       gormedi  gozlenmeyen
bagimliligi yazilmamis baslik      gormedi  kopya
statik bagli kitaplik guncellemesi gormedi  kopya
yalitilmis testin degisikligi      gormedi  kopya
iki durak arasindaki ara deger     gormedi  gozlenmeyen
okunmadan ezilen yazma             gormedi  gozlenmeyen
siniflar: kopya 5, ortak 4, aktarim 3, gozlenmeyen 3
ulasan 7, ulasmayan 8

SA1. On beş satırın hiçbiri TUTARSIZ işareti taşımıyor: her gözlem beklenen sınıfla uyuşuyor. Beş değişiklik kopya, dört ortak, üç aktarım, üç gözlenmeyen — ve yedisi öteki tarafa ulaşırken sekizi ulaşmıyor. Bu on beş satırlık liste kursun geri kalanının haritası: her ders burada geçen bir satırı alıp derinleştiriyor. Bu dersin payı ilk satır: dup ile cogaltilan konum.

dup ile Çoğaltılan Konum

SA2. tanitici işlevi bir dosyayı a tanıtıcısıyla açıyor, altı bayt yazıyor, sonra dup(a) ile ikinci bir tanıtıcı — b — türetiyor. a üzerinden konum ikiye alınıyor; ölçülen soru, b üzerinden okunan konumun da ikiye gelip gelmediği. Çıktı gordu diyor, yani geldi. Bunun nedeni tanıtıcı sayılarında değil, işletim sisteminin tuttuğu üç katmanlı yapıda: her süreç kendi tanıtıcı tablosunu tutar, her tanıtıcı bir açık dosya kaydına işaret eder, her açık dosya kaydı da bir konum taşır. open her çağrıldığında yeni bir açık dosya kaydı — ve onunla yeni bir konum — oluşturur. dup ise yeni bir kayıt açmaz; var olan kaydı gösteren ikinci bir tanıtıcı verir. a ve b farklı tamsayılar, ama aynı kaydı gösteriyorlar; konumu biri değiştirdiğinde öteki de aynı konumu okuyor.

Aynı dosyayı open ile iki kez açmış olsaydık — a’yı bir kez, sonra aynı yolu ikinci bir çağrıyla c olarak — durum farklı olurdu. İki ayrı açık dosya kaydı oluşurdu, ikisi de kendi konumunu tutardı; a üzerinden konum ikiye alındığında c sıfırda kalırdı. Kayıt paylaşımı open ile değil, aynı kaydı gösteren bir tanıtıcı türetmekle kurulur: dup, dup2, ya da fork sırasında miras alınan tanıtıcılar. gorur.c bu ayrımı dup ile gösteriyor; sonraki ders aynı mirası forkla sürdürecek, ama orada paylaşılan konum değil, çoğaltılan bellek konusu.

Sınırlayıcı Ölçüm

Bir tanıtıcı yalnız küçük bir tamsayıdır; kendi başına hiçbir şey söylemez. İki tanıtıcı aynı kayda mı, yoksa iki ayrı kayda mı bakıyor — bu, sayısal değerlerine bakarak asla anlaşılmaz. Tek güvenilir ölçüm, kaydın taşıdığı konumun davranışıdır: biri üzerinden değiştirilen konum ötekinden de okunuyorsa kayıt paylaşılıyordur; okunmuyorsa iki ayrı kayıt vardır. gorur.c bu yüzden konum değerini lseek(b, 0, SEEK_CUR) ile okuyor, tanıtıcı sayısını hiçbir yerde basmıyor — tanıtıcı numarası hedefe ve koşuma bağlıdır, konum davranışı bağlı değildir.

Özet

  • Kurs boyunca sabit kalan dört sınıf — kopya, ortak, aktarım, gözlenmeyen — tek bir soruya yanıt verir: değişikliği yapmayan taraf onu görüyor mu.
  • Çekirdeğin on beş gözlemi beş kopya, dört ortak, üç aktarım, üç gözlenmeyen olarak dağılıyor; yedisi öteki tarafa ulaşıyor, sekizi ulaşmıyor.
  • dup ile türetilen bir tanıtıcı yeni bir açık dosya kaydı açmaz; var olan kaydı gösteren ikinci bir yol açar, bu yüzden konumu paylaşır.
  • Ayrı bir open çağrısı her zaman ayrı bir açık dosya kaydı, dolayısıyla ayrı bir konum yaratır — aynı dosyaya baksa bile.
  • Bu dersin sınıfı ortak: iki tanıtıcı aynı kaydı tuttuğu için biri değiştirdiğinde öteki de görüyor.

Sonraki Adım

Bir sonraki ders aynı soruyu forka soruyor. dup bir kaydı paylaştırdı; fork ise belleği paylaştırmaz, çoğaltır — çocuğun değişkeni kendi kopyasında kalır, ebeveyn hiçbir zaman görmez.

İ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