Pipe, Unix sistemlerindeki en ilkel fakat, etkili haberleşme yöntemlerindendir. Pipe yöntemi
Win32 sistemlerinde de çok benzer bir biçimde kullanılmaktadır. Pipe yöntemi isimsiz ve
isimli olmak üzere ikiye ayrılır. İsimli pipe’lara Unix dünyasında FIFO da denilmektedir.
Pipe aslında kernel içerisinde tahsis edilmiş olan bir FIFO kuyruk sistemidir. Pipe, Unix
sistemlerinde dosya sistemi ile bütünleştirilmiştir. Pipe yönteminde temel olarak bir process
pipe’a yazma yaparken bir process de okuma yapar. Okuma işlemi FIFO sistemine göre
yapılır. Pipe kullanımında senkronizasyon bloke yöntemiyle sağlanabilir. Okuyan process
pipe boşsa bloke olur. Pipe’ın bir uzunluğu vardır. Yazan taraf pipe doluysa pipe’ta yer
boşalana kadar bloke olur. Pipe’tan okuma yapıldığında yer açılır. İşlem yazan tarafın pipe’ı
kapatmasıyla sonlandırılır. Bu işlemi okuyan taraf sanki dosyanın sonuna gelinmiş de 0 byte
okunmuş gibi algılar. Pipe’a okuma va yazma yapmak için yine read ve write fonksiyonları
kullanılır.
Komut satırında pipe işlemi
Shell üzerinde a ve b iki program olmak üzere,
a | b
işlemi a programının stdout dosyasını yaratılmış olan bir pipe’a, b programının stdin
dosyasını da aynı pipe’a yönlendirir. Böylece a programının stdout dosyasına yazdığı her şey
pipe’a yazılır. b programı stdin dosyasından okuma yaptığında pipe’tan okur. Şüphesiz pipe
burada bir eşzamanlılık problemini çözmektedir. a programı stdout dosyasını kapattığında ya
da sonlandığında b programı sanki pipe’ın sonuna gelmiş gibi 0 byte okur.
/* a.c */
#include <stdio.h>
int main(void)
{
int i;
for (i = 0; i < 10; ++i)
printf("%d\n", i);
return 0;
}
/* b.c */
#include <stdio.h>
int main(void)
{
char buf[80];
int n;
for (;;) {
if (scanf("%d", &n) == EOF)
break;
printf("%d\n", n);
}
return 0;
}
Anahtar notlar: scanf fonksiyonu önce boşluk karakterlerini atar, sonra format karakteriyle
belirtilen uygun giriş varsa alır, parametrelere yerleştirdiği giriş sayısıyla geri döner. scanf
parametresi ile belirtilen adrese bir şey yerleştirememiş olduğu durumda dosya sonunu
görürse EOF değeri ile geri döner. scanf fonksiyonu eğer format karakterine uygun bir giriş
bulamazsa (örneğin “%d” ile sayı okunmak istensin fakat biz “ali” gibi bir yazı girmiş
olalım) beğenmediği karakteri tampona geri yazarak (ungetch gibi bir fonksiyonla)
okuyabildiği parça sayısıyla geri döner. gets ve fgets fonksiyonları ‘\n’ görene kadar okuma
yaparlar. Eğer hiçbir karakter okuyamazlarsa NULL göstericiyle geri dönerler.
Bir dosya üzerinde işlem yapan Unix programlarının çoğu komut satırı argümanı olarak dosya
ismi girilmediğinde stdin dosyasından okuma yaparlar. Böylelikle kullanıcı bu programlarla
shell üzerinde pipe işlemi uygulayabilir. Örneğin;
ls | wc
Burada wc komut satırı argümanı almadığına göre stdin’den okuma yapacaktır. ls programı
çıkışı stdout dosyasına yazdığına göre pipe işlemi gerçekleşecektir.
Benzer biçimde örneğin more programı da komut satırı argümanı olarak dosya ismi almazsa
stdin’den okuma yapar.
popen ve pclose Fonksiyonları
Bu fonksiyonlar yüksek seviyeli pipe işlemi yapan fonksiyonlardır. Kullanımları çok basittir.
FILE *popen(const char *cmd, const char *type);
int pclose(FILE *f);
Bu fonksiyonlar pek çok sistem tarafından desteklenmelerine karşın bir POSIX fonksiyonu
değildirler, ancak POSIX.2 içerisinde bu fonksiyonlara değinilmiştir. Fonksiyonların
prototipleri <stdio.h> içerisindedir. popen fonksiyonunun ikinci parametresi “w” ya da “r”
olabilir. Bir pipe yaratıldığında iki dosya betimleyicisi oluşur. Bu dosya betimleyicilerinden
biri dosyaya yazmakta, diğeri okumakta kullanılır.
popen fonksiyonu şöyle çalışmaktadır: Kullanılan modun “r” olduğunu düşünelim. Fonksiyon
önce pipe’ı yaratır ve iki betimleyiciyi oluşturur. Okumakta kullanılan betimleyiciyi FILE
yapısıyla ilişkilendirerek geri dönüş değeri olarak verir. Bundan sonra popen shell programını
çalıştırmak için bir kez fork yapar, fork işleminden sonra pipe’a yazma yapmakta kullanılan
betimleyiciyi dup2 fonksiyonuyla stdout yerine yerleştirir. Bundan sonra exec ile birinci
parametresiyle belirtilen programı çalıştırır. Sonuç olarak şöyle bir durum oluşur: popen
fonksiyonunun geri döndürdüğü dosyadan okuma yapıldığında aslında pipe’tan okuma
yapılmış olur. Fonksiyonunun birinci parametresinde belirtilen komutun çıktıları (yani
buradaki programın stdout’u) da pipe’a yönlendirilir. “w” modunda tam tersi bir işlem oluşur.
Yani programcı yazdığında pipe’a yazar, yazılanlar pipe aracılığıyla birinci parametresiyle
belirtilen programın stdin’ine yönlendirilir. pclose işleminde pipe kapatılır.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch;
FILE *f;
C ve Sistem Programcıları Derneği 102
if ((f = popen(“ls”, “r”)) == NULL) {
fprintf(stderr, “Cannot create pipe...\n”);
exit(1);
}
while ((ch = fgetc(f)) != EOF)
putchar(ch);
pclose(f);
return 0;
}
Örneğin program içerisinde elde edilen bir takım bilgilerin more programı yardımıyla ekrana
yazdıracak olalım. Bunun için bilgileri önce dosyaya yazıp daha sonra more programını
çalıştırmak yerine değerleri buldukça pipe yoluyla more programına aktarmak daha pratik bir
yöntemdir.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i, ch;
FILE *f;
if ((f = popen(“more”, “w”)) == NULL) {
fprintf(stderr, “Cannot create pipe...\n”);
exit(1);
}
for (i = 0; i < 100; ++i)
fprintf(f, “%d\n”, i);
pclose(f);
return 0;
}
Shell’deki pipe işlemini yapan benzer bir program ekte verilmiştir. Bu programda popen
fonksiyonuyla iki ayrı pipe yaratılmıştır. Pipe’lardan biri “w” diğeri “r” modunda açılmıştır.
“r” modunda açılan pipe okunduğunda aslında birinci programın stdout’a gönderdikleri elde
edilir. Okunanlar “w” modunda açılmış pipe’a yazılırlar. İkinci program stdin dosyasını
okuduğunda bu pipe’ı okuyacaktır.
a à stdout à pipe1 (“r” modu) à fin à buf à fout à pipe2 (“w” modu) à stdin à b