Tuesday 15 January 2008

LINUX Çekirdeğini Anlamak – Süreçler Arası İletişim 2

Ali Rıza SARAL(1)


(1) Daniel P. Bovet, Marco Cesati, Understanding the Linux Kernel ‘den faydalanarak derlenmiştir.


18.1 Borular (Pipes)
Borular Unix’in bütün tat-türlerinde (flavor) bulunan bir süreçler arası iletişim mekanizmasıdır. Bir boru süreçler arasında tek yönlü veri akışıdır: bir süreç tarafından boruya yazılan verinin tamamı onu okuyan başka bir sürece çekirdek tarafından yönlendirilir.

Unix komut kabuklarında, borular operatörü ile yaratılır. Örneğin, aşağıdaki ifade kabuğa bir boru ile bağlı iki süreç yaratmağı emreder:

$ ls more

ls programını çalıştıran, birinci sürecin standart çıkışı boruya yönlendirilir; more programını çalıştıran ikinci süreç girdisini borudan okur. Aynı sonuçların iki ayrı komutu çalıştırarak elde edilebileceğini aklınızda tutunuz:

$ ls > temp
$ more < temp

İlk komut ls’in çıktısını alışılagelmiş bir dosyaya yöneltir; daha sonra ikinci komut more ‘u girişini aynı dosyadan okumağa zorlar. Tabii ki, geçici dosyalar yerine boruları kullanmak daha kullanışlı çünkü:
• Kabuk ifadesi çok daha kısa ve basit.
• Sonradan silinişi gereken geçici alışılagelmiş dosyalar yaratmağa ihtiyaç yok.

18.1.1 Using a Pipe
Borular bindirilmiş (mounted) dosya sistemlerinde ilgili hiçbir görüntüsü (image) olmayan
açık dosyalar olarak değerlendirilebilir. Yeni bir boru, bir çift dosya tanımlayıcıyı (file descriptor) geri döndüren boru() (pipe( ) ) sistem çağrısı ile yaratılabilir. Süreç birinci dosya tanımlayıcı ile oku() (read( )) sistem çağrısını kullanarak borudan okuyabilir; benzer şekilde, ikinci dosya tanımlayıcı ile boru() (pipe( ) ) sistem çağrısını kullanarak boruya yazabilir.

POSIX yalnız yarı-karşılıklı (half-duplex) boruları tanımlar, bu yüzden boru() (pipe( ) ) sistem çağrısı iki dosya tanımlayıcıyı geri döndürse bile, her süreç kullanmadan önce bunlardan birini kapatmalıdır. Eğer iki yönlü veri akışı gerekiyorsa, süreçler boru() (pipe( ) ) ‘yu iki defa başlatarak iki farklı boru kullanmalılar.

Sistem V Sürüm 4 gibi, çok sayıda Unix sistemi, tam-çift-yönlü borular gerçekleştirirler ve her iki tanımlayıcının da içine yazılışına ya da okunuşuna izin verirler.
Linux bir başka yaklaşımı benimser: herbir borunun dosya tanımlayıcıları hala tek-yönlüdür, fakat diğerini kullanmadan önce birini kapamak gerekli değildir

Geçtiğimiz örneği kaldığımız yerden ele alalım: komut kabuğu lsmore ifadesini yorumladığında, esas olarak aşağıdaki faaliyetleri yapar:
1. boru() (pipe( ) ) sistem çağrısını çalıştırır; boru() ‘nun 3 nolu dosya tanımlayıcısı(borunun okuyucu kanalı (read channel)) ve 4 noluyu (yazıcı kanalı) döndürdüğünü kabul edelim.
2. çatal() (fork( )) sistem çağrısını iki defa başlatır.
3. 3 ve 4 nolu dosya tanımlayıcıları serbest bırakmak için kapa() (close() ) sistem çağrısını iki defa başlatır.

ls programını çalıştırışı gereken birinci çocuk süreç aşağıdaki işlemleri icra eder:
1. dosya tanımlayıcı 4’ü dosya tanımlayıcıya kopyalamak için çiftle2(4,1) (dup2(4,1)) ‘yi başlatır. Bu andan itibaren dosya tanımlayıcı 1 borunun yazıcı kanalını belirtir.
2. Dosya tanımlayıcı 3 ve 4’ü serbest bırakmak için kapa() (close() ) sistem çağrısını iki kez başlatır.
3. /bin/ls programını çalıştırmak için çalıştırve() (execve( )) sistem çağrısını başlatır. Böyle bir program hiç yoktan(default) çıkışını dosya tanımlayıcı 1’e sahip olan dosyaya yazar (standart çıkış), yani, boruya yazar.

İkinci çocuk süreç more programını çalıştırmak zorunda; buyüzden, aşağıdaki işlemleri icra eder:
1. Dosya tanımlayıcı 3’ü dosya tanımlayıcı 0’a kopyalamak için çiftle2(3,0) (dup2(3,0)) ‘ü başlatır. Bu andan itibaren, dosya tanımlayıcı boru’nun okuyucu kanalını belirtir.
2. Dosya tanımlayıcı 3 ve 4’ü serbest bırakmak için kapa() (close() ) sistem çağrısını iki kez başlatır.
3. /bin/more ‘u çalıştırmak için çalıştırve() (execve( )) sistem çağrısını başlatır. Hiç yoktan, program girişini dosya tanımlayıcıya(standart giriş) sahip olan dosyadan okur; yani, borudan okur.

Bu basit örnekte, boru yalnızca iki süreç tarafından kullanıldı. Gerçekleştiriliş şeklinden dolayı, bir boru istenen sayıda süreç tarafından kullanılabilir. Açıkçası, eğer iki veya daha çok süreç aynı boruya yazar veya okursalar, erişimlerini açık ve doğrudan bir şekilde (explicitly) dosya kilitleyerek (file locking) eşzamanlı kılmalıdırlar veya süreçler arası iletişim (IPC) semaforları kullanmalıdırlar…

Çok sayıda Unix sistemi, boru() (pipe())sistem çağrısından başka, boruları kullanırken genellikle yapılan bütün kirli işleri becermek için kullanılan, paç() (popen( )) ve pkapa (pclose( )) adı verilen iki paketleyici (wrapper) fonksiyon kullanırlar. paç() (popen( )) fonksiyonunu kullanarak bir boru bir kere yaratıldı mı C kütüphanesine dahil edilmiş (fprintf( ), fscanf( ), ve diğer) üst seviye G/Ç fonksiyonları (the high-level I/O functions) ile birlikte kullanılabilirler.

Linux’ta paç() (popen( )) ve pkapa (pclose( )) C kütüphanesine dahil edilmiştir. paç() (popen( )) fonksiyonu iki başlangıç değişkeni(parameter) alır: çalıştırılabilir bir dosyanın dosyaisimi (filename) patika-ismi ve veri naklinin yönünü belirleyen bir tip (type) karakter zinciri. Bir DOSYA (FILE) veri yapısına işaret-ediciyi geri döndürür. paç() (popen( )) fonksiyonu esas olarak aşağıdaki işlemleri icra eder:
1. boru() (pipe())sistem çağrısını kullanarak yeni bir boru yaratır.
2. Kendine sıra gelince aşağıdaki işlemleri çalıştıran, yeni bir süreç çatallar(fork).
a. Eğer tip r ise, borunun yazıcı kanalı ile dosya tanımlayıcı 1 (standart çıkış) olarak ilişkilendirilen dosya tanımlayıcıyı çiftler; aksi takdirde, eğer tip w ise, borunun okuyucu kanalı ile dosya tanımlayıcı (standart giriş) olarak ilişkilendirilen dosya tanımlayıcıyı çiftler.
b. boru() (pipe()) tarafından geri döndürülen dosya tanımlayıcıları kapatır.
c. filename tarafından belirlenen programı çalıştırmak için çalıştırve() (execve( )) sistem çağrısını başlatır.
3. Eğer tip r ise, borunun yazıcı kanalı ile ilişkili dosya tanımlayıcıyı kapatır; aksi takdirde, eğer tip w ise, borunun okuyucu kanalı ile ilişkili dosya tanımlayıcıyı kapatır.
4. Boru için hangi dosya tanımlayıcı açık ise onu belirten DOSYA (FILE) dosya işaret-edicisinin adresini geri döndürür.

paç() (popen( )) başlatılışından sonra, ebeveyn ve çocuk boru içinden bilgi değiş-tokuşu yapabilirler: ebeveyn fonksiyon tarafından geri döndürülen DOSYA (FILE) işaret-edicisini kullanarak verileri ( tip r ise) okuyabilir veya (tip w ise) yazabilir. Veri çocuk süreç tarafından çalıştırılan program tarafından standart çıkışa yazılır ya da standart girişten okunur.

paç() (popen( )) tarafından geri-döndürülen dosya işaret-edicisini alan pkapa (pclose( )) fonksiyonu, yalnızca bekle4() (wait4( )) sistem çağrısını başlatır ve paç() (popen( )) tarafından yaratılmış sürecin sona erişini bekler.