Akımlar ve Evreler
Önceki XII. Oylum - Akımlar Üzerinde Giriş/Çıkış Sonraki
Akımlar ve Evreler
Akımlar çok evreli yazılımlarda tek evreli yazılımlardaki gibi kullanılır. Ancak yazılımcı olası karışıklıkların farkında olmalıdır. Birçok akım işlevinin tasarımı ve gerçeklenmesi çok evreli yazılım geliştirmeyle ilgili ek gereksinimlerden oldukça etkileneceğinden yazılımın birinin evreleri asla kullanamayabileceğininin bilinmesi ayrıca önemlidir.
POSIX standardı öntanımlı olarak akım işlemlerinin atomik olmasını gerektirir. Örneğin iki akım işleminin iki evre halinde aynı anda bir akıma uygulanması işlemler sırayla yapılıyormuş gibi yapılmasına sebep olacaktır. Okuma ve yazma sırasında uygulanan tampon işlemleri aynı akımı kullanan diğerlerinden korunur. Bu, her akımın yapılacak bir çalışma öncesi (dolaylı olarak) elde edilen bir dahili kilitleme nesnesine sahip olması ile sağlanır.
Ancak bunun yeterli olmadığı ya da bunun istenmediği durumlar da vardır. Eğer yazılım birden fazla akım işlevi çağrısının atomik olarak yapılmasını gerektiriyorsa dolaylı kilitleme mekanizması yetersiz kalır. Bir yazılımın üretmek istediği bir satırı çeşitli işlev çağrılarıyla oluşturması buna bir örnek olarak verilebilir. Atomlara ayırma işi tüm işlev çağrıları üzerinde değil, işlevlerin kendileri tarafından kendi işlemlerini atomlarına ayırarak yapılmalıdır. Bunun olabilmesi için de akım kilitleme işleminin yazılım kodunda yapılması gerekir.
void flockfile
(FILE *akım)
işlev
flockfile işlevi akım ile ilişkili dahili kilitleme nesnesi elde etmekte kullanılır. İşlev, başka hiçbir evrenin doğrudan flockfile/ftrylockfile üzerinden veya dolaylı olarak bir akım işlevi çağrısı üzerinden akımı kilitleyememesini sağlar. Kilit elde edilinceye kadar evre engellenecektir. funlockfile işlevine yapılacak bir doğrudan çağrı kilit nesnesini serbest bırakacaktır.
int ftrylockfile
(FILE *akım)
işlev
ftrylockfile işlevi flockfile işlevi gibi akım ile ilişkili dahili kilitleme nesnesi elde etmeye çalışır. flockfile işevinin aksine bu işlev kilit kullanılabilir değilse engelleme yapmaz. Kilit başarıyla elde edilirse işlev sıfırla döner, aksi takdirde akımı başka bir evre kilitlemiştir.
void funlockfile
(FILE *akım)
işlev
funlockfile işlevi akım’ın kilitleme nesnesini serbest bırakır. Akım bir flockfile çağrısı ya da başarılı bir ftrylockfile çağrısı tarafından önceden kilitlenmiş olmalıdır. Akım işlemleri tarafından uygulanan dolaylı kilitlemeler sayılmaz. funlockfile işlevi bir hata durumu döndürmez ve o anki evre tarafından kilitlenmemiş bir akım için yapılan bir çağrının davranışı tanımsızdır.
Yukarıdaki işlevlerin nasıl kullanılacağını gösteren ve çok evreli yazılımlarda bile kullanılabilecek aşağıdaki örnekte bir çıktı satırı atomik olarak üretilmektedir (evet, aynı iş tek bir fprintf çağrısı ile yapılabilirdi ama kimi zaman bu mümkün olmaz):
FILE *fp;
{
   …
   flockfile (fp);
   fputs ("This is test number ", fp);
   fprintf (fp, "%d\n", test);
   funlockfile (fp)
}
Doğrudan kilitleme olmaksızın fputs çağrısı döndükten sonra ve fprintf işlevinin number sözcüğünden sonra numarayı basmasından önce başka bir evrenin fp akımını kullanması mümkün olabilir.
Bu açıklamalardan sonra akımlardaki kilitleme nesnelerinin basit karşılıklı red nesneleri (mutexes - mutual exclusion objects) olmadığı anlaşılmış olmalıdır. Aynı akımın aynı evre içinde iki kere kilitlenmesi mümkün olduğundan kilitleme nesneleri, ardışık karşılıklı red nesnelerine eşdeğer olmalıdır. Bu karşılıklı red nesneleri sahibinin izini takibeder ve kendi sayısınca kilit elde eder. Akımdaki kilitleme nesnelerini tamamen serbest bırakmak için aynı evreler tarafından aynı sayıda funlockfile çağrısı gereklidir. Örneğin:
void
foo (FILE *fp)
{
  ftrylockfile (fp);
  fputs ("in foo\n", fp);
  /* Bu çok yanlış!!!  */
  funlockfile (fp);
}
Burada önemli olan, funlockfile işlevinin "sadece" ftrylockfile işlevinin akımı başarıyla kilitlediği anlaşıldığı takdirde kullanılabileceğidir. Örnekte ftrylockfile işlevinin sonucu gözardı edildiğinden yapılan işlem yanlıştır. flockfile kullanılsaydı bu yanlış olmayacaktı. Yukarıdaki gibi bir kodun sonucunda ya funlockfile o anki evre tarafından kilitlenmeyen bir akımı serbest bırakmaya çalışacak ya da akımı vakitsiz olarak serbest bırakacaktır. Kod aşağıdaki gibi olmalıydı:
void
foo (FILE *fp)
{
  if (ftrylockfile (fp) == 0)
    {
      fputs ("in foo\n", fp);
      funlockfile (fp);
    }
}
Kilitlemenin niçin gerekli olduğu konusunu hallettiğimize göre artık kilitlemenin ne zaman istenmediği ve bu durumda ne yapılabileceği üzerinde duralım. Kilitleme işlemleri (doğrudan ya da dolaylı) bedavaya gelmez. Bir kilit alınmasa bile maliyeti sıfır değildir. Uygulanan işlemler çok işlemcili ortamlardaki güvenli bellek işlemlerini gerektirir. Böyle sistemlerdeki çok sayıda yerel önbellekle bu oldukça maliyetlidir. Yani, en iyisi çok gerekli değilse (bir akımın iki veya daha fazla evre tarafından kullanılamayacağı bağlamlarda) kilitlemeden tamamen kaçınmaktır. Bu çoğu zaman uygulama kodu için ugulanabilir; çok sayıda bağlam içinde kullanılabilen kütüphane kodu için bir bağlam kilitleme kullanımı ve tutuculuk anlamında öntanımlı olacaktır.
Kilitlemeden kaçınmada iki temel mekanizma vardır. İlki akım işlemlerinin _unlocked sonekli biçimlerini kullanmaktır. POSIX standardı bunların pek azını tanımlar ve GNU kütüphanesi birkaç tane daha ekler. İşlevlerin bu biçimleri isimlerine _unlocked soneki getirilen işlevlerle benzer davranışı gösterir; tek farkla, akımları kilitlemezler. Bu işlevler potansiyel olarak çok daha hızlı olduğundan daha çok tercih edilirler. Bu sadece kilitleme işlemlerinden kendilerini korumalarından dolayı değildir. Daha önemli olarak, putc ve getc işlevleri çok basittir ve geleneksel olarak (evrelere girişten önce) tampon boş değilse çok hızlı olan makrolar halinde gerçeklenmişlerdir. Kilitleme gereksinimlerinin eklenmesiyle bu işlevlerin kodu çok büyüdüğünden artık makrolar halinde gerçeklenememektedirler. Ancak bu makrolar aynı işlevsellikle yeni isimler altında (putc_unlocked ve getc_unlocked) hala kullanılabilmektedir. Hızlarındaki bu dev farktan dolayı _unlocked sonekli işlevlerin kullanılması kilitleme gerekli olduğu durumlarda bile tercih edilmesine sebep olmaktadır. Kilitleme ile birlikte kullanıma bir örnek:
void
foo (FILE *fp, char *buf)
{
  flockfile (fp);
  while (*buf != '/')
    putc_unlocked (*buf++, fp);
  funlockfile (fp);
}
Bu örnekte putc işlevi kullanılsaydı ve doğrudan kilitleme olmasaydı, putc işlevi döngü sonlanana kadar her çağrıda bir olmak üzere kilidi defalarca elde edecekti. Yukarıdaki örnek putc_unlocked makrosu akım tamponunun kilitleme olmaksızın doğrudan değiştirilmesi anlamında kullanılarak yazılmıştır.
Kilitlemeden kaçınmak için ikinci yol Solaris’te bulunan ve GNU C kütüphanesinde de kullanılabilir olan standart dışı bir işlevi kullanmaktır.
int __fsetlocking
(FILE *akım,
 int   tür)
işlev
__fsetlocking işlevi, akım işlemlerinin akım’ın kilitleme nesnesini dolaylı elde edip etmeyeceğini seçmekte kullanılır. Öntanımlı olarak kilit dolaylı elde edilir ancak bu işlev kullanılarak kilidin alınması iptal edilebilir ya da tekrar yerleştirilebilir. tür parametresi olarak kullanılabilecek üç değer vardır:
FSETLOCKING_INTERNAL
akım öntanımlı dahili kilitlemeyi hemen kullanmaya başlayacaktır. _unlocked sonekli biçim hariç her akım işlemi akımı dolaylı olarak kilitleyecektir.
FSETLOCKING_BYCALLER
__fsetlocking işlevi döndükten sonra akım kilitleme için kullanıcı sorumlu olur. Durum FSETLOCKING_INTERNAL ile öntanımlı duruma döndürülünceye kadar bunu dolaylı olarak yapacak bir akım işlemi yoktur.
FSETLOCKING_QUERY
__fsetlocking işlevi sadece akımın o anki kilitleme durumunu sorgular. Dönen değer duruma bağlı olarak ya FSETLOCKING_INTERNAL ya da FSETLOCKING_BYCALLER olacaktır.
__fsetlocking işlevinin dönüş değeri akımın çağrı öncesi durumunu belirtmek üzere ya FSETLOCKING_INTERNAL ya da FSETLOCKING_BYCALLER olacaktır.
İşlev ve tür parametresinin değerleri stdio_ext.h başlık dosyasında bildirilmiştir.
Bu işlev özellikle yazılım kodu _unlocked işlevleri hakkında yeterli bilgiye sahip olunmadan yazılmışsa (ya da yazılımcı onları çok delice kullanmışsa) yararlıdır.
Önceki Üst Ana Başlık Sonraki
Akımların Kapatılması Başlangıç Akımlar ve Uluslararasılaştırma
Bir Linux Kitaplığı Sayfası