[LinuxFocus-icon]
Ev  |  Erişimdüzeni  |  İçindekiler  |  Arama

Duyumlar | Belgelikler | Bağlantılar | LF Nedir
Bu makalenin farklı dillerde bulunduğu adresler: English  Castellano  Deutsch  Francais  Nederlands  Russian  Turkce  

convert to palmConvert to GutenPalm
or to PalmDoc

[Leonardo]
tarafından Leonardo Giordani
<leo.giordani(at)libero.it>

Yazar hakkında:

Milan Politechnico'da Telekoninikasyon Mühendisliğinde okuyor.Network yoneticisi olarak çalışıyor ve programlama (özellikle Assembly ve C/C++) ile ilgileniyor.1999'dan beri neredeyse sadece Linux/Unix ile çalışıyor.



Türkçe'ye çeviri:
Özcan Güngör <ozcangungor(at)netscape.net>

İçerik:

 

Eşzmanlı Programlama - İleti Kuyrukları (1)

[run in paralell]

Özet:

Bu makale

Bu makalenin anlaşılabilmesi için ön koşullar şunlardır:

  • Minimun kabuk bilgisi
  • Temel C dili bilgisi(yazım,döngüler,kütüphaneler)

LinuxFocus'un son iki sayısında yayınlanan makaleleri okumak yararlı olacaktır (Kasım2002 ve Ocak2003).
_________________ _________________ _________________

 

Giriş

Son makalede, eşzamanlı işlemler kavramını tanıttık ve iç işlem iletişimi probleminin çözümünü öğrendik:Semaforlar.Orada gördüğümüz gibi, semafor kullanımı bize paylaşılmış kaynaklara ulaşımın yönetilmesi imkanını vermektedir.Yani kabaca iki ya da daha çok işlemin eşzamanlı çalışmasını sağlar.

İşlemlerin eşzamanlı çalışması demek, işlemlerin zamanlaması demektir. Mutlak referans sisteminde değil (hangi işlemin hangi zamanda başlayıp hangi zamanda biteceğini vermek) göreceli olanda (hangi işlemin önce hagngisinin sonra çalışacağını planlamak) yapılır.

Semafor kullanımı kendisini iki yolla gösterir:karmaşık veya sınırlı. Karmaşıktır çünkü her işlem, diğer işlemlerin onunla eşzamanlı çalışabilmesi için bir semafor yönetmesi gerekir. Sınırlıdır çünkü işlemler arasında parametre değişimine izin vermez. Bir işlemin oluşma örneğine bakalım:Bu olay çalışan bütün işlemler tarafında bilinmelidir ama semafor bu tür bilgilerin gönderilmesine izin vermez.

Paylaşılmış kaynaklara semafolarla ulaşımın eşzamanlı kontrolü, bizi, bir işlemin sürekli tıkamasına götürür: Bir işlem işin içine girdiğinde kaynağı bırak ve diğerleri kullanmadan onu kilitle.Gördüğümüz gibi eşzamanlı programlama dünyasında hangi işlemin ne zaman çalıştırılacağını çok iyi bilemiyoruz.

Bu kısa açıklamadan da anlaşılacağı gibi semaforlar karmaşık eşzamanlama sorunları için yetersizdir.Daha şık bir çözüm ileti kuyruklarıdır.Bu makalede işlemlerarası iletişim imkanı teorisini işleyeceğiz ve SysV fonksiyonlarını kullanarak küçük bir program yazacağız.

 

İleti Kuyruğu Teorisi

Her işlem bir veya daha fazla kuyruk adı verilen yapı oluşturabilir:Her yapı bir ya da daha fazla değişik tiplerde ileti tutabilir .Bu iletiler değişik kaynaklara sahiptirler ve her durumun bilgileri içerirler; herkes iletileri kendi belirtecini sağladığı kuyruklara gönderebilir. İleti, kuyruğa sırayla bağlanır, bilgileri kronolojik olarak okur (en eskiden en yeniye) fakat seçicidir,yani sadece belirli bir tipte olan iletileri okur:Bu son özellik okuduğumuz iletinin önceliğinin bir nevi kontrolünü verir.

İletilerin kullanımı,işlemler arasındaki posta sisteminin basit bir uygulamasıdır.İleti kendi kutusuna gelmiş olan postayı önerilen sırada alır ve postada söylenileni yapar.

İki işlemin eşzamanlaması basit olarak iki işlem arasında bir iletinin kullanılmasıyla yapılır:Kaynaklar hala kendi durumlarını işlemlere bildiren bir semafora sahip olacaklar ancak zamanlama doğrudan yapılacak.Artık başlangıçta çok karmaşık olan bir sorunun ileti kuyruğu kullanarak basitleştiğini anlıyoruz.

C dili ile ileti kuyruğu programlamaya başlamadan önce eşzamanlamayla ilgili bir sorun hakkında konuşalım:İletişim protokolü gereksinimi

 

Bir Protokol Oluşturma

Protokol, bir kümedeki elemanların etkileşimlerini kontrol eden bir kuraldır; son makalede, en basit protokolü gördük:Bir semafor oluşturma ve iki işlemin diğerinin durumuna bakarak çalışması.İleti kuyruğunun kullanımı çok karmaşık protokollerin uygulanmasına izin verir: Bütün ağ protokollerinin (TCP/IP, DNS, SMTP,...) ve hatta iki bilgisayar arasındaki iletşiminin, ileti değişim mimarisi üzerine kurulu olduğunu düşünmek yeterlidir. Bir bilgisayar içinde olan veya daha fazlasının arasında olan işlemlerarası iletişim arasında bir fark yoktur.İleride de göreceğimiz gibi, şu an konuştuğumuz kavramı yaymak çok kolay bir iş olacaktır.

Bu basit protokol örneği ileti değişimi temeline dayanır:A ve B işlemi eşzamanlı çalışır ve farklı verileri kullanır;çalışmayı bitirdiklerinde sonuçlarını birleştirirler.Birbirleriyle etkileşimlerini düzenleyen protokol aşağıdaki gibi olabilir:

İŞLEM B:


İŞLEM A

Bu durumda kimin verileri birleştireceği tamamen keyfidir; genel olarak işin işine karışan işlemin doğasına dayanır (sunucu/istemci) ama bu daha özel bir makalenin konusudur.

Bu protokol kolayca n adet işleme genişletilebilir: A dışındaki her işlem kendi verisiyle çalışır ve A'ya ileti gönderir.A yanıt verdiğinde, her işlem kendi sonucunu gönderir: A dışındaki lşlemlerin yapısı henüz değişmedi.

 

Sistem V İleti Kuyrukları

Artık bu kavramları Linux altında uygulama zamanı geldi. Daha önce de söylediğim gibi, semaforların yönetimine benzer fonksiyonlar ileti kuyrukları için de vardır:Okuyucunun işlem oluşturma, sistem çağrılarının kullanımı ve IPC anahtarları gibi temel konuları bildilerini varsayıyorum.

Bir iletiyi tanımlayan sistemin temel yapısına msgbuf denir.Bu linux/msg.h içinde tanımlanmıştır.

/* msgsnd ve msgrcv çağrıları için ileti tamponu*/
struct msgbuf {
        long mtype;         /* ileti tipi*/
        char mtext[1];      /* ileti metni*/
};

mtype alanı, iletinin tipini temsil eder ve zorunlu olarak pozitif değerdir:Sayılar ile ileti tipleri arasındaki ilişki daha önceden tanımlanmalıdır ve protokol tanımlamasının bir parçasıdır.İkinci kısım, iletinin içeriğini tutar ama tanımlama sırasında belirlenmek zorunda değildir. msgbuf yapısı karmaşık verileri tutabilecek şekilde yeniden tanımlanabilir.Öneğin şunu yazmak mümkündür:
struct message {
        long mtype;         /* ileti tipi*/
        long sender;        /* gönderenin id'si*/
        long receiver;      /* alıcının id'si*/
        struct info data;   /* iletinin içeriği*/
        ...
};

Tam olarak eşzamanlama teorisinin argümalarıyla yüzleşmeden önce, maksimum boyuta (4056 byte) sahip bir iletinin nasıl oluşturulacağını düşünmeliyiz. Açıkça görüldüğü gibi kerneli bu boyutu artırıp tekrar derleyebiliriz.Ancak bu uygulamamızı biraz büyütür (dahası, bu değer oldukça iyi performans göstercek bir değere eşitlenmiştir ve bu değeri büyütmek iyi değildir).

Yeni bir kuyruk oluşturmak için şu fonksiyonu çağırmek gerekir: msgget()

int msgget(key_t key, int msgflg)
Bu fonksiyon bir IPC anahtarı ve bazı bayrakları argüman olarak alır. Bu bayrakları şimdilik şu değere eşitleyebiliriz:
IPC_CREAT | 0660
(eğer bu kuyruk yoksa oluştur; kullanıcı ve gruba kullanma hakkı ver). Fonsiyon kuyruk belirtecini geri döndürür.

Daha önceki makalede olduğu gibi hiç hata olmayacağını varsayıyoruz.Böylece kodu basitleştirebiliriz ve hatta ileriki makalelerde güvenli IPC kodlarında bahsedebiliriz.

Belirtecini bildiğimiz bir kuyruğa bir ileti göndermek için şu fonksiyonu kullanırız: msgsnd()

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg)

Burada msqid kuyruğun belirtecidir, msgp göndereceğimiz iletinin göstericisidir (burada tipi struct msgbuf olarak verilmiştir), msgsz iletinin boyutudur ( mtype 'nın uzunluğu hariçtir.Bir long tipini uzunluğudur ve genelde 4 bytedır) ve msgflg bekleme politikasıyla alakalı bir bayraktır.İletinin uzunluğu şu şekilde bunabilir:
length = sizeof(struct message) - sizeof(long);

Eğer msgflg IPC_NOWAIT değerine eşitlenmişse, gönderici işlem bazı boşlukların durumları uygun olana kadar beklemeyecek ve bir hata verip çıkacaktır; Bu konuyu hata yönetimi başlğı altında inceleyeceğiz.

Bir kuyruktaki iletiyi okyabilmek için şu fonksiyonu kullanırız: msgrcv()

int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg)


Burada msgp okunacak iletinin nereye kopyalanacağı belirtir. mtype ilgilendiğimiz ileti altkümesini gösterir.

Bir kuyruğu silmek için şu fonksiyondan IPC_RMID bayrağı yardımıyla faydalanırız: msgctl()

msgctl(qid, IPC_RMID, 0)

Şimdiye kadar söylediklerimizi bir program ile test edelim.Bu program bir ileti kuyruğu oluşturur, bir ileti gönderir ve onu okur.. Doğru çalışan bir sistemi kontrol edeceğiz.
#include <stdio.h>
#include <stdlib.h>
#include <linux/ipc.h>
#include <linux/msg.h>

/* msgbuf yapısını tekrar tanımlar*/
typedef struct mymsgbuf
{
  long mtype;
  int int_num;
  float float_num;
  char ch;
} mess_t;

int main()
{
  int qid;
  key_t msgkey;

  mess_t sent;
  mess_t received;

  int length;

  /* pseuo-rastgele sayı üreteci
	çekirdeğini başlatır*/
  srand (time (0));

  /* iletinin uzunluğu */
  length = sizeof(mess_t) - sizeof(long);

  msgkey = ftok(".",'m');

  /* Kuyruğu oluşturur*/
  qid = msgget(msgkey, IPC_CREAT | 0660);

  printf("QID = %d\n", qid);

  /* Bir ileti oluşturur*/
  sent.mtype = 1;
  sent.int_num = rand();
  sent.float_num = (float)(rand())/3;
  sent.carattere = 'f';

  /* bir ileti gönderir*/
  msgsnd(qid, &sent, length, 0);
  printf("İLETİ GÖNDERİLDİ...\n");

  /* Bir ileti alır*/
  msgrcv(qid, &received, length, sent.mtype, 0);
  printf("ALINAN İLETİ...\n");

  /* Alınan ve gönderilen iletilerin
	aynı olup olmadığını kontrol eder*/
  printf("Tamsayı = %d (sent %d) -- ", received.int_num,
         sent.int_num);
  if(received.int_num == sent.int_num) printf(" Tamam\n");
  else printf("HATA\n");

  printf("Ondalık sayı = %f (sent %f) -- ", received.float_num,
         sent.float_num);
  if(received.float_num == sent.float_num) printf(" Tamam\n");
  else printf("HATA\n");

  printf("Char = %c (sent %c) -- ", received.ch, sent.ch);
  if(received.ch == sent.ch) printf(" TAMAM\n");
  else printf("HATA\n");

  /* Kuyruğu yok et*/
  msgctl(qid, IPC_RMID, 0);
}

Şimdi iki işlem oluşturabilir ve onları ileti kuyruğu ile iletişim içine koyabiliriz.Birazcık işlem bölme kavramını düşünelim:Baba işlem tarafından tutulan bütün değişkenlerin değerleri oğul işlemlere taşınır (bellek kopyalama). Baba ve oğul işlemlerin belirtecini bildikleri ve ulaşabildikleri bir ileti kuyruğunu işlemi bölmeden önce oluşturmalıyız.

Yazdığım kod oğul ileşim verisini baba işleme gönderdiği bir ileti kuyruğu kullanır.Oğul işlem rastgele sayılar üretir ve bu babaya gönderir.Ardından her ikisi de bu sayıları ekrana yazarlar.

#include <stdio.h>
#include <stdlib.h>
#include <linux/ipc.h>
#include <linux/msg.h>
#include <sys/types.h>

/* ileti yapısını tekrar tanımlar*/
typedef struct mymsgbuf
{
  long mtype;
  int num;
} mess_t;

int main()
{
  int qid;
  key_t msgkey;
  pid_t pid;

  mess_t buf;

  int length;
  int cont;

  length = sizeof(mess_t) - sizeof(long);

  msgkey = ftok(".",'m');

  qid = msgget(msgkey, IPC_CREAT | 0660);

  if(!(pid = fork())){
    printf("BABA - QID = %d\n", qid);

    srand (time (0));

    for(cont = 0; cont < 10; cont++){
      sleep (rand()%4);
      buf.mtype = 1;
      buf.num = rand()%100;
      msgsnd(qid, &buf, length, 0);
      printf("OĞUL - İLETİ NUMARASI %d: %d\n", cont+1, buf.num);
    }

    return 0;
  }

  printf("BABA - QID = %d\n", qid);

  for(cont = 0; cont < 10; cont++){
    sleep (rand()%4);
    msgrcv(qid, &buf, length, 1, 0);
    printf("BABA - İLETİ MUMARASI %d: %d\n", cont+1, buf.num);
  }

  msgctl(qid, IPC_RMID, 0);

  return 0;
}

Burada iki işlem oluşturduk.Bu işlemler ileti değişim sistemi aracılığı ile birlikte çalışabiliyorlar.Burada bir protokole gereksinim duymadık çünkü işlemler çok basit.Sonraki makalede yine ileti kuyruklarından bahsedeceğiz ve değişik ileti tiplerini yöneteceğiz.Kendi IPC projemizi (telefon anahtarlama simülatörü) oluturabilmek için iletişim protokolleri üzerinde daha çok çalışacağız.  

Önerilen Kaynaklar

 

Bu yazı için görüş bildiriminde bulunabilirsiniz

Her yazı kendi görüş bildirim sayfasına sahiptir. Bu sayfaya yorumlarınızı yazabilir ve diğer okuyucuların yorumlarına bakabilirsiniz.
 talkback page 

Görselyöre sayfalarının bakımı, LinuxFocus Editörleri tarafından yapılmaktadır
© Leonardo Giordani, FDL
LinuxFocus.org
Çeviri bilgisi:
it --> -- : Leonardo Giordani <leo.giordani(at)libero.it>
it --> en: Leonardo Giordani <leo.giordani(at)libero.it>
en --> tr: Özcan Güngör <ozcangungor(at)netscape.net>

2003-03-11, generated by lfparser version 2.35