Saturday 26 January 2008

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

Ali Rıza SARAL(1)

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


18.1.2 Boru Veri Yapıları (Pipe Data Structures)

Şimdi tekrar sistem çağrısı seviyesinde düşünmeğe başlamalıyız. Bir kere bir boru yaratıldı mı, bir süreç oku() (read( ) ve yaz() (write( )) Sanal Dosya sistemi (VFS) çağrılarını kullanarak ona erişir. Bu yüzden her boru için, çekirdek biri okumak diğeri yazmak için iki tane dosya nesnesi ve bir idüğüm (inode) nesnesi yaratır. Bir süreç bir boruya yazmağa veya onu okumağa başladığında uygun dosya tanımlayıcıyı kullanmalıdır.

Bir idüğüm (inode) nesnesi bir boruya değindiğinde, kendisinin u alanı bir boru_idüğüm_bilgi (pipe_inode_info yapısı)’ndan oluşur, Table 18-1’de görüldüğü gibi.


Tablo 18-1. boru_idüğüm_bilgi yapısı (The pipe_inode_info Structure)
Tip Alan Tanım
char * base Address of kernel buffer
unsigned int start Read position in kernel buffer
unsigned int lock Locking flag for exclusive access
struct wait_queue * wait Pipe/FIFO wait queue
unsigned int readers Flag for (or number of) reading processes
unsigned int writers Flag for (or number of) writing processes
unsigned int rd_openers Used while opening a FIFO for reading
unsigned int wr_openers Used while opening a FIFO for writing


Bir idüğüm ve iki dosya nesnesi dışında, her boru kendi boru arabölgesine (pipe buffer) ‘una sahiptir, yani boruya yazılmış ve hala okunuşu gereken tek bir sayfa çerçevesi (page frame). Bu sayfa çerçevesinin adresi boru_idüğüm_bilgi (pipe_inode_info) yapısının zemin (base) alanına depolanır. idüğüm (inode) nesnesinin i_büyüklük (i_size) alanı henüz okunuşu gerçekleşmemiş boru arabölgesine (buffer) yazılmış byte’ların sayısını depolar; aşağıda o sayıya şu andaki boru büyüklüğü (pipe size) diyoruz.

Boru arabölgesine hem yazan hem de okuyan süreçler tarafından erişilir, bu yüzden çekirdek arabölge içinde iki şuandaki konumun izini takip etmek zorundadır:
• boru_idüğüm_bilgisi (pipe_inode_info) yapısının başla (start) alanı içine depolanan okunacak ilk byte’ın göreli konumu
• başla (start) ve boru Büyüklüğü’nden çıkartılan yazılacak ilk byteın göreli konumu

Boru’nun veri yapıları üzerinde yarışım koşullarından sakınmak için, çekirdek bore arabölgesine eşzamanlı (concurrent) erişimleri yasaklar. Bunu başarmak için, boru_idüğüm_bilgisi (pipe_inode_info veri yapısındaki kilit (lock) alanından faydalanır. Maalesef, kilit (lock) alanı yeterli değildir. Göreceğimiz gibi, POSIX bazı boru işlemlerinin atomik lmasını zorunlu kılar. Dahası, POSIX okuyucuların arabölgeyi boşaltabilmeleri için, boru dolu olduğu zaman yazıcı süreçin askıya alınışına müsaade eder. Bu şartlar, idüğüm nesnesi içinde bulunan ek bir i_atomik_yaz (i_atomic_write) semaforunu kullanarak sağlanır: bu semafor bir başka yazıcı arabölge dolu olduğundan askıya alınmışken bir sürecin yeni bir yazıcı işlem başlatışını engeller.

18.1.3 Bir Boru Yaratmak ve Yok Etmek (Creating and Destroying a Pipe)
Bir boru disk görüntüsü olmayan bir küme SanalDosyaSistemi (VFS) nesnesi olarak gerçekleştirilir. Aşağıdaki tartışmada göreceğimiz gibi, bir boru, en azından bir süreç ona değinen bir dosya tanımlayıcıya sahip olduğu sürece, sistem içinde kalır.

boru() (pipe( )) sistem çağrısına, boru_yap() (do_pipe( )) fonksiyonunu çağıran sis_boru() (sys_pipe( )) fonksiyonu tarafından hizmet edilir. Yeni bir boru yaratmak için, boru_yap() aşağıdaki işlemleri yapar:
1. Borunun okuyucu kanalı için bir dosya nesnesi ve dosya tanımlayıcı ayırır, dosya nesnesinin bayrak (flag) alanını O_YALNIZOKU (O_RDONLY) ’ya ayarlar, ve d_işle (f_op) alanını the roku_boru_dişlem (read_ pipe_fops) tablosunun adresi ile başlangıç-koşullar.
2. Borunun yazıcı kanalı için bir dosya nesnesi ve dosya tanımlayıcı ayırır, dosya nesnesinin bayrak (flag) alanını O_YALNIZYAZ (O_WRONLY) ’ya ayarlar, ve d_işle (f_op) alanını the roku_boru_dişlem (write_ pipe_fops) tablosunun adresi ile başlangıç-koşullar.
3. Boru için bir idüğüm (inode) nesnesini başlangıç koşullayan boru_idüğümünü_edin (get_ pipe_inode( )) fonksiyonunu başlatır. Bu fonksiyon boru arabölgesi için aynı zamanda bir sayfa çerçevesi ayırır ve onun adresini pipe_inode_info yapısının base alanına depolar.
4. dentry nesnesini ayırır ve onu iki dosya nesnesini ve idüğüm (inode) nesnesini birbirine ilişkilendirmek için kullanır.
5. Kullanıcı Üslubundaki sürece iki dosya tanımlayıcıyı geri döndürür.

Yeni bir boruya yazmak veya okumak için erişebilen tek süreç bir boru() (pipe( )) sistem
çağrısı çıkartan süreçtir. Borunun bir okuyucu ve bir yazıcıya gerçekten sahip olduğunu temsil etmek için boru_idüğüm_bilgisi (pipe_inode_info) veri yapısının okuyucular (readers) ve yazıcılar (writers) alanları 1 başlangıç değerlerini alır. Genel olarak, eğer karşı düşen borunun dosya nesnesi hala açık ise bu her iki alana 1 değeri verilir; Eğer karşı düşen dosya nesnesi serbest bırakılmışsa bu alana 0 değeri verilir, çünkü artık başka hiçbir süreçtarafından erişilmez. Yeni bir süreci çatallamak (forking) okuyucular (readers) ve yazıcılar (writers) alanlarının değerlerini arttırmaz, dolayısıyla değerleri hiçbir zaman 1 ‘in üstüne çıkmaz; yine de hala ebeveyn süreç tarafından kullanılan bütün dosya nesnelerinin kullanım sayaçlarının değerlerini arttırır. Böylece, ebeveyn ölse bile nesneler serbest bırakılmaz, ve boru çocukların kullanışı için açık kalacaktır. FIFO’larla ilişkilendirildiklerinde okuyucular (readers) ve yazıcılar (writers) alanları bayrak yerine sayaç olarak kullanılırlar.

Ne zaman bir süreç, bir boru ile alakalı bir dosya tanımlayıcı üzerinde bir kapa() (close( )) sistem çağrısı başlatırsa, çekirdek kullanım sayacını azaltan dkoy() fput( ) fonksiyonunu karşı düşen nesne üzerinde çalıştırır. Eğer sayaç 0 olursa, fonksiyon dosya işlemlerinin serbestbırak (release) metodunu çalıştırır.

Boru_okuyuş_sal() (pipe_read_release( )) ve boru_yazış_sal() (pipe_write_release( )) fonksiyonlarının her ikisi de borunun dosya nesnelerinin serbestbırak (release) metodlarını gerçekleştirilişi için kullanılırlar. boru_idüğüm_bilgisi (pipe_inode_info) veri yapısının okuyucular (readers) ve yazıcılar (writers) alanlarına değer verirler. Daha sonra her fonksiyon boru_serbestbirak() (pipe_release( )) fonksiyonunu başlatır. Bu fonksiyon borunun durumundaki değişiklikten farkına varışları için borunun bekleyiş kuyruğundaki herbir süreci uyandırır. Dahası, fonksiyon okuyucular (readers) ve yazıcılar (writers) alanlarının 0 olup olmadıklarını sınar; eğer böyle ise, boru arabölgesini içeren sayfa çerçevesini serbest bırakır.