original in it: Leonardo Giordani
it to en: Leonardo Giordani
en to tr: Özcan Güngör
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.
Bu makalenin anlaşılabilmesi için ön koşullar şunlardır:
İş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.
İ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
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:
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.
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*/ };
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*/ ... };
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)
length = sizeof(struct message) - sizeof(long);
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)
Bir kuyruğu silmek için şu
fonksiyondan IPC_RMID bayrağı yardımıyla faydalanırız:
msgctl()
msgctl(qid, IPC_RMID, 0)
#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); }
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; }