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], °er, 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.
dupile 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.