[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 yonetici si 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şzamanlı Programlama - İleti Kuyrukları (2)

[run in paralell]

Özet:

Bu makale dizisi, okuyuculara çokluişlem kavramının ve onun Linux altındaki uygulamalarının tanıtımını hedefler.Çokluişlemin temelindeki teorik kavramların tanıtımından başlayıp, basit ama etkili bir iletişim protokolü içeren bir işlemler arası iletişim uygulaması vereceğiz.Bu makalenin anlaşılabilmesi için gerekli bilgiler şunlardır:

  • Minimum kabuk bilgisi
  • Temel C dili bilgisi (yazım, döngüler, kütüphaneler)
Komutların kullanım klavuzları(manual pages), komutun hemen yanındaki parantezler içinde verilmiştir. Bütün glibc fonksiyonları, "info Libc" komutu ile belgelendirilmiştir.

Daha önce bu konu hakkında yayınlamış makaleleri okumak iyi olabilir:

 

Giriş

Bu dizideki son makalede, iki (ya da daha çok) işlemi nasıl eşzamanlı ve ileti kuyruklarını kullanarak nasıl birlikte çalıştırabileceğimizi öğrendik.Bu makalede, daha ileri gidip, kendi ileti değiş-tokuşumuz için bir protokol oluşturmaya başlayacağız.

Daha önce de söylediğimiz gibi, protokol, iki kişinin ya da makinanın konuşabilmesi için (farklı olsalar bile) gerekli olan kurallar kümesidir.Örneğin, İngilizce'nin kullanımı bir protokoldür. Çünkü bu dil, benim Hintli okuyucularımla( yazdıklarımla her zaman çok ilgileniyorlar) anlaşmamı sağlıyor. Daha Linux ile ilgili bir örnek vermek gerekirse, eğer kernelinizi yeniden derlerseniz (korkmayın, o kadar zor değil), Network bölümü altında, kernelinizin anlayabileceği bir çok protokol göreceksiniz.Örneğin, TCP/IP.

Bir protokol oluşturabilmemiz için önce nasıl bir uygulama geliştireceğimize karar vermeliyiz.Bu defa bir telefon anahtarı(switch) simülatörü yazacağız.Ana işlem, telefon anahtarı olacak ve oğul işlemler, kullanıcı olarak hareket edecekler: Her kullanıcıya, anahtar aracılığıyla ileti göndermesine izin vereceğiz.

Protokol üç durumun üstesinden gelecek: kullanıcının doğumu(örneğin, kullanıcı var ve bağlı), kullanıcının sıradan işleri ve kullanıcın ölümü(kullanıcı artık bağlı değil).Şimdi bu durumlar hakkında konuşalım:

Bir kullanıcı, kendi ileti kuyruğunu oluşturduğu sisteme bağlandığında (unutmayın, işlemler hakkında konuşuyoruz), anahtarın, kullanıcıya nasıl ulaşacağını bilebilmesi için, kullanıcı, kimliğini anahtara göndermelidir. Burada, gereksinim duyduğunda, bazı yapıları ve verileri başlatması için gerekli zamanı vardır.Anahtardan, anahtar aracılığı ile diğer kullanıcılara gönderilecek olan iletiyi yazacağı kuyruğun kimliğini alır.

Kullanıcı iletileri gönderebilir ve alabilir. Bir kullanıcını diğer kullanıcıya bir ileti göndermesi halinde şu iki durum oluşur: alıcı bağlıdır veya değildir. Her iki durumda da gönderene bir bilgi gönderilmesi gerektiğine karar veririz. Böylece iletisine ne olduğunu bilir. Bunu, alıcı değil, anahtar yapar.

Bir kullanıcı anahtarla bağlantısını kestiğinde, anahtarı haberdar etmelidir ve başka birşeye gerek yoktur. Bu tür bir çalışmayı tanımlayan kod aşağıdadır:

/* Birth - Doğum */
create_queue  		/* kuyruk_oluştur */
init  			/* başlat */
send_alive  		/* bağlı_gönder */
send_queue_id   	/* kuyruk_kimlik_gönder */
get_switch_queue_id	/* anahtar_kuyruk_kimlik_al */

/* Work - Çalışma */
while(!leaving){  	/* ayrılmıyor ise */
 receive_all      	/* hepsini_al */
 if(<send_condition>){    /* durumu_gönder ise */
  send_message			/* ileti_gönder */
 }
 if(<leave condition>){	/* durumu_bırak */
  leaving = 1			/* ayrılıyor=1  */
 }
}

/* Death - Ölüm */
send_dead		/* ölü_gönder */

, telefon anahtarının davranışını tanımlamalıyız: Bir kullanıcı bağlandığında, bize kendi ileti kuyruğunu kimliğini içeren bir ileti gönderir. Biz bunu, bu kullanıcıya gönderilmiş iletileti ulaştırmak için kaydetmeliyiz ve bu kullanıcının diğer ulaştırmamız üzere gönderdiği iletilerin depolanacağı ileti kuyruğunun kimliğini göndererek cevap vermeliyiz. Sonra bu kullanıcıdan gelen bütün iletileri analiz edip gönderilern kullanıcıların sisteme bağlı olup olmadığını kontrol etmeliyiz.Eğer kullanıcı bağlı ise iletiyi göndeririz, değilse iletiyi sileriz.Her iki durumda da kullanıcıyı bilgilendiririz.Bir kullanıcı öldüğünde, sadece onun ileti kuyruğunun kimliğini sileriz ve kullanucu ulaşılamaz olur.

, kod uygulaması şöyle olur:

while(1){
 /* Yeni kullanıcı */
 if (<birth of a user>){   	/* bir kullanıcının doğumu */
  get_queue_id				/* kuyruk_kimlik_al */
  send switch_queue_id  		/* gönder_anahtar_kuyruk_kimlik */
 }

 /* Kullanıcı ölür */
 if (<death of a user>){		/* kullanıcının ölümü */
  remove_user				/* kullanıcı_sil */
 }

 /* İleti ulaştırması */
 check_message				/* ileti_kotrolü */
 if (<user alive>){		/* kullanıcı_bağlı */
  send_message				/* ileti gönder */
  ack_sender_ok				/* gönderen_tamam_gönder */
 }
 else{
  ack_sender_error			/* gönderen_hata_gönder */
 }
}
 

Hatalarla Başa Çıkma

Hata durumlarıyla başa çıkma, bu projedeki en öncemli ve en zor bölümdür.Dahası, Tam bir hata kontrol altistemi, yazılan kodun neredeyse %50'sidir. Bu makalede nasıl iyi bir hata kontrol rutini yazılacağını açıklamayacağım çünkü bu konu çok karmaşıktır ama bu andan itibaren hata durumlarını kontrol edeceğim ve yöneteceğim. Hata kontrol için iyi bir tanıtım, glibc(www.gnu.org) kullanım klavuzları ile gelmektedir ama -ilgilenirseniz- bunu ile ilgi bir makale yazaceğım.  

Protokol Uygulaması - Katman 1

küçük protokolümüz, iki katmandan oluşmaktadır: İlki(düşük olanı), kuyukları yöneten, iletileri hazırlayan ve gönderen fonksiyonlardan oluşurken yüksek katman, anahtarın ve kullanıcların davranışlarını tanımlayan kodlardaki(yukarıda verilen) fonksiyonlar gibi, protokolü uygular.

Yapacağımız ilk iş, msgbuf'ın kernel prototipini kullanarak iletiler için kendi yapımızı oluşturmalıyız

typedef struct
{
  int service;
  int sender;
  int receiver;
  int data;
} messg_t;

typedef struct
{
  long mtype; /* İleti tipi */
  messg_t messaggio;
} mymsgbuf_t;

Bu iletide daha da genişleteceğimiz genel birşeydir: Gönderen ve alan kullanıcı alanları, bir kullanıcı kimğini içerir ve veri alanı, genel veriler içerir. Servis alanı ise anahtara bir servis isteği için kullanılır. Örneğin, iki servisimizin olduğunu düşünebiliriz: biri hemen, diğeri gecikmeli ulaştırma.Bu durumda veri alanı, gecikme süresini iletir. Bu sadece bir örnektir ama servis alanın bize ne gibi olasılıklar sunduğunu anlamamızı sağlar.

Şimdi, yapımızı yonetecek olan bazı fonksiyonları uygulayabiliriz, özellikle ileti alanını alan ve dolduran fonksiyonları.Bu fonksiyonlar az çok aynıdır. Bu yüzden sadece iki tanesini vereceğim ve diğerlerini .h dosyalarında bulabilirsiniz.

void set_sender(mymsgbuf_t * buf, int sender)
{
  buf->message.sender = sender;
}

int get_sender(mymsgbuf_t * buf)
{
  return(buf->message.sender);
}

Bu fonksiyonların amacı, kodu kısaltmak değil(sadece bir satırdan oluşuyor): Basitce anlamlarını hatırlamak ve protokolün insan diline daha yakı olmasımı sağlamak, böylece kullanımını kolaylaştırmak.

Şimdi, IPC anahtarlarını üretecek, ileti kuyruklarını oluşturacek ve silecek, ileti gönderecek ve alacak fonksiyonları yazmalıyız: Bir IPC anaktarı oluşturmak kolaydır:

key_t build_key(char c)
{
  key_t key;
  key = ftok(".", c);
  return(key);
}

Bir kuyruk oluşturacak fonksiyon:

int create_queue(key_t key)
{
  int qid;

  if((qid = msgget(key, IPC_CREAT | 0660)) == -1){
    perror("msgget");
    exit(1);
  }

  return(qid);
}

gördüğünüz gibi, hata ile başa çıkma, bu durumda, kolyadır.Aşağıdaki ,fonksiyon bir kuyruk siler:

int remove_queue(int qid)
{
  if(msgctl(qid, IPC_RMID, 0) == -1)
  {
    perror("msgctl");
    exit(1);
  }
  return(0);
}

Son fonksiyon ileti alan ve göndern fonksiyondur: bir ileti öndermek, onu belirli bir kuyurğa yazmaktır, örneğin, bize ahantar tarafından veril olna kuyruk.

int send_message(int qid, mymsgbuf_t *qbuf)
{
  int result, lenght;
  lenght = sizeof(mymsgbuf_t) - sizeof(long);
  if ((result = msgsnd(qid, qbuf, lenght, 0)) == -1){
    perror("msgsnd");
    exit(1);
  }

  return(result);
}

int receive_message(int qid, long type, mymsgbuf_t *qbuf)
{
  int result, length;
  length = sizeof(mymsgbuf_t) - sizeof(long);

  if((result = msgrcv(qid, (struct msgbuf *)qbuf, length, type, IPC_NOWAIT)) == -1){
    if(errno == ENOMSG){
      return(0);
    }
    else{
      perror("msgrcv");
      exit(1);
    }
  }

  return(result);
}

Hepsi bu. Fonksiyonları layer1.h dosyasında bulabilirsiniz: Bunları kullanarak bir program yazmaya çalışın(örneğin, geçen makaledeki örnek). Bir sonraki makalede, protokolün Katman 2'si hakkında konuşacağız ve uygulamasını yapacağı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:
en --> -- : Leonardo Giordani <leo.giordani(at)libero.it>
en --> tr: Özcan Güngör <ozcangungor(at)netscape.net>

2003-05-08, generated by lfparser version 2.35