|
![]() |
|
![]() tarafýndan Leo Giordani <leo.giordani(at)libero.it> Yazar hakkýnda: Milan,Politecnico'da Telekominikasyon Fakültesi'nde öðrenci.Netwok yöneticiliði yapýyor ve programlama ( özellikle assembly ve C++) ile ilgileniyor.1999'dan beri özellikle Linux/Unix üzerinde çalýþýyor. Türkçe'ye çeviri: Özcan Güngör <ozcangungor(at)netscape.net> Ýçerik: |
Title: Eþzamanlý Programlama - Ýþlemler arasý iletiþim![]() Özet:
Bu makale dizisinin amacý, okuyucuya çokluiþlemler ve onun Linux'ta uygulanýþý
hakkýnda bilgi vermektir.Teorik bilgilerden baþlayýp iþlemler arasý iletiþim
örneði gösteren tamamlanmýþ bir örnek vereceðiz.Basit ama etkili bir iletiþim
protokü kullanacaðýz.
|
A { d->d+1 } & B { d->output }
Burada "&" eþzamanalý iþlemi ifade eder.Birinci mümkün çalýþma:
(-) d = 5 (A) d = 6 (B) output = 6
ama eðer önce B çalýþýrsa þunu elde ederiz :
(-) d = 5 (B) output = 5 (A) d = 6
Bu örnekten eþzamanalý çalýþmanýn doðru yönetilmesinin önemini anlýyorsunuz.Verilerin
ayný olmamasý riski büyüktür ve kabul edilemez.Bu veri kümelerinin banka
hesaplarýnýz olduðunu düþünün.Bu sorunu asla küçümsemezsiniz.
Bir önceki makalede waitpid(2) fonksiyonu kullanarak eþzamanlamanýn
ilk biçimini görmüþtürk.Bu fonksiyon, bir iþlemin devam etmeden önce baþka
bir iþlemin bitmesi için beklemeye yarar.Aslýnda bu, okuma-yazma
anýnda oluþabilecek karmaþýklýðý büyük ölçüde engeller.Bir veri kümesi
üzerinde P1 çalýþýyorsa, P2 ayný veri kümesinde veya bir alt kümesinde
çalýþacaksa önce P1'in bitmesini beklemek zorundadýr.
Açýkca bu yöntem bir çözümdür.fakan en iyisi deðildir.P2, P1'in iþleminin
bitmesi için -çok kýsa sürecekse bile- uzun zaman bekleyecektir.Biz kontrolümüzün
alanýný daraltmalýyýz.Örneðin:tek bir veriye ya da veri kümesine
eriþme gibi.Bu sorunun çözümü kütüphanelerin temeli olan,SysV IPC(System
V InterProcess Communication-SistemV Ýþlemlerarasý Ýletiþim) adý verilen
bir takým kütüphane ile verilir.
key_t ftok(const char *pathname, int proj_id);
Bu fonksiyon, var olan bir dosya adýný (pathname) ve bir tam sayý kullanýr.Anahtarýn
tek olacaðýnýn garantisi yoktur çünkü dosyadan alýnan parametreler (i-node
sayýsý ve sürücü numarasý), benzer kombinasyonlarý oluþturabilir.Var
olan anahtarlarý kontrol eden ve ayný anahtarýn kullanýmasýný engelleyecek
küçük bir kütüphane oluþturmak iyi bir çözüm olacaktýr.
int semget(key_t key, int nsems, int semflg);
Burada key,IPC anahtarýdýr.nsems, oluþturmak istediðimiz semafor sayýsýdýr.semflg,
12 bitlik eriþim kontorlüdür.Ýlk 3'ü oluþturma politikasýna göre deðiþir,
diðer 9'ü ise kullanýcý, group ve diðerlerinin (Unix dosya sistemindeki
gibi) okuma ve yazma haklarýdýr.Tam açýklama için ipc(5)'nin kullanma klavuzunu
okuyun.Farkettiðiniz
gibi SysV bir semafor kümesini kontrol eder ve bu sebeple kod daha küçüktür.
Ýlk semaforumuzu oluþturalým:
#include <stdio.h>
#include <stdlib.h>
#include <linux/types.h>
#include <linux/ipc.h>
#include <linux/sem.h>
int main(void)
{
key_t key;
int semid;
key = ftok("/etc/fstab", getpid());
/* Bir semafor içren bir semafor kümesi oluþturalým */
semid = semget(key, 1, 0666 | IPC_CREAT);
return 0;
}
Ýleride semaforlarýn nasýl yönetildiðini ve silindiðini öðreneceðiz.Semaforu yönetme fonksiyonu semctl(2)'dir:
int semctl(int semid, int semnum, int cmd, ...)
Burada parametreler semafor kümesinin veya (istenirse) sadece semnum ile belirtilen semaforun iþlevine baðlý olarak cmd ile verilir.Biz gereksinim duydukça bazý seçenekleri anlatacaðýz.Ama tüm liste kullanma klavuzunda verilmiþtir.cmd iþlevine baðlý olarak fonksiyon için baþka parametreler de tamýnlamak gerekebilir.Þu þekildedir:
union semun {
int val;
/* SETVAL deðeri */
struct semid_ds *buf; /* IPC_STAT, IPC_SET
için tampon*/
unsigned short *array; /* GETALL, SETALL için
dizi*/
/* Linux'a özgü kýsým */
struct seminfo *__buf; /* IPC_INFO için tampon*/
};
Semafora deðer verebilmek için SETVAL yönergesi kullanýmlaýdýr ve bu deðer semun birliðinde (union) belirtilmelidir:Bir önceki programdaki semforun deðerini 1 yapalým:
[...]
/* Bir semaforlu bir semafor kümesi oluþtur */
semid = semget(key, 1, 0666 | IPC_CREAT);
/* 0 numaralý semaforun deðerini 1 yap */
arg.val = 1;
semctl(semid, 0, SETVAL, arg);
[...]
Þimdi semafor için ayrýlmýþ yeri boþaltmamýz gerekiyor.Bu iþlem, semctl fonksiyonunda IPC_RMID yönergesi ile yapýlýr.Bu iþlem semaforu siler ve bu kaynaða ulaþmak için bekleyen butün iþlemlere bir ileti gönderir.Yukarýdaki programýn devamý olarak:
[...]
/* Semafore 0'in deðerini 1 yap */
arg.val = 1;
semctl(semid, 0, SETVAL, arg);
/* Semaforu sil */
semctl(semid, 0, IPC_RMID);
[...]
Daha önce de gördüðümüz gibi eþzamanlý iþlerleri oluturmak ve yönetmek
zor deðildir.Hata yönetimi iþin içine girdiðinde, kodun karmaþýklýlýðý
açýsýndan iþler biraz zorlaþacak.
Artýk semafor semop(2) fonksiyonu ile kullanýlabilir:
int semop(int semid, struct sembuf *sops, unsigned nsops);
Burada semid, kümenin belirteci; sops, yapýlacak iþlemler dizisi ve nsops bu iþlemlerin sayýsýdýr.Bütün iþlemler sembuf yapýsýyla temsil edilir:
unsigned short sem_num; short sem_op; short sem_flg;
Örneðin kümedeki semaforun numarasý (sem_num), iþlem (sem_op) ve bekleme
politikasýný ayarlayan bayrak (þimdilik sem_flg 0 olsun).Belirleyeceðimiz
iþlemler tam sayýlardýr ve aþaðýdaki kurallara uyar:
Doluluk, O semaforunun deðerini artamamak ile; boþluk, U semaforunu deðerini azaltamamak ile anlaþýlýr.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include <linux/ipc.h>
#include <linux/sem.h>
int main(int argc, char *argv[])
{
/* IPC */
pid_t pid;
key_t key;
int semid;
union semun arg;
struct sembuf lock_res = {0, -1, 0};
struct sembuf rel_res = {0, 1, 0};
struct sembuf push[2] = {1, -1, IPC_NOWAIT, 2, 1, IPC_NOWAIT};
struct sembuf pop[2] = {1, 1, IPC_NOWAIT, 2, -1, IPC_NOWAIT};
/* Diðerleri */
int i;
if(argc < 2){
printf("Kullaným: bufdemo [býoyut]\n");
exit(0);
}
/* Semaforlar */
key = ftok("/etc/fstab", getpid());
/* 3 semafor içeren semafor kümesi oluþtur */
semid = semget(key, 3, 0666 | IPC_CREAT);
/* Semaphore #0'ýn deðerini 1 yap - Kaynak kontrolü */
arg.val = 1;
semctl(semid, 0, SETVAL, arg);
/* Semafor #1'in deðerini buf_length yap- Doluluk kontrolü */
/* Sem'in deðeri 'tampondaki boþ yer'dir */
arg.val = atol(argv[1]);
semctl(semid, 1, SETVAL, arg);
/* Semafor #2'nin deðeri buf_length'dir - Boþluk kontrolü */
/* Sem'in deðeri 'tampondaki eleman sayýs'dýr */
arg.val = 0;
semctl(semid, 2, SETVAL, arg);
/* Ayýrma */
for (i = 0; i < 5; i++){
pid = fork();
if (!pid){
for (i = 0; i < 20; i++){
sleep(rand()%6);
/* Kaynaðý kilitlemeyi dene - sem #0 */
if (semop(semid, &lock_res, 1) == -1){
perror("semop:lock_res");
}
/* Bir serbest boþluðu kilitle - sem #1 / Bir eleman koy - sem
#2*/
if (semop(semid, &push, 2) != -1){
printf("---> Ýþlem:%d\n", getpid());
}
else{
printf("---> Ýþlem:%d TAMPON DOLU\n", getpid());
}
/* Kaynaðý býrak */
semop(semid, &rel_res, 1);
}
exit(0);
}
}
for (i = 0;i < 100; i++){
sleep(rand()%3);
/* Kaynaðý kilitlemeyi dene - sem #0 */
if (semop(semid, &lock_res, 1) == -1){
perror("semop:lock_res");
}
/* Serbest boþluðun kilidini kaldýr - sem #1 / Bir
eleman al - sem #2 */
if (semop(semid, &pop, 2) != -1){
printf("<--- Ýþlem:%d\n", getpid());
}
else printf("<--- Ýþlem:%d TAMPON BOÞ\n",
getpid());
/* Kaynaðý býrak */
semop(semid, &rel_res, 1);
}
/* Seamforlarý sil */
semctl(semid, 0, IPC_RMID);
return 0;
}
Koddaki þu ilginç satýlara bakalým:
struct sembuf lock_res = {0, -1, 0};
struct sembuf rel_res = {0, 1, 0};
struct sembuf push[2] = {1, -1, IPC_NOWAIT, 2, 1, IPC_NOWAIT};
struct sembuf pop[2] = {1, 1, IPC_NOWAIT, 2, -1, IPC_NOWAIT};
Bu dört satýr, semaforlar üzerinde yapabileceðimiz iþlemlerdir: Ýlk
ikisi tek iþlem iken diðer iki iþlem çift iþlemdir.Ýlk iþlem, lock_res,
kaynaðý kilitmeyi dener:Bu iþlem ilk semaforun (0 numaralý) deðerini -eðer
semaforun deðeri sýfýr deðilse- bir azaltýr.Kaynaðýn kilitli olmasý durunda
politika hiçbiri deðildir (örneðin ilem bekleme durumunda).rel_res, lock_res'e
benzer ama kaynak býrakýlýr( deðeri pozitiftir) .
Ekleme ve çýkarma iþlemleri biraz özeldir.Bunlar iki iþlem dizisidir.Birincisi
1 numaralý semafor üzerinde ve ikincisi 2 numaralý semafor üzerindedir.Birincisi
artýrma iken ikincisi azaltmadýr veya tam tersi.Ama politika artýk bekleme
durumu deðildir:IPC_NOWAIT, iþlemi, kaynak kilitli ise çalýþmasýna devam
etmesi için zorlar.
/* Semafo #0'in deðerini 1 yap - Kaynak kontrolü */
arg.val = 1;
semctl(semid, 0, SETVAL, arg);
/* Semafor #1'i buf_length yap - Doluluk kontrolü */
/* Sem'in deðeri 'tampondaki boþ yer'dir */
arg.val = atol(argv[1]);
semctl(semid, 1, SETVAL, arg);
/* Semafor #2'yi buf_length yap - Boþluk kontrolü */
/* Sem'in deðeri 'tampondaki eleman sayýsý'dir */
arg.val = 0;
semctl(semid, 2, SETVAL, arg);
Burada semaforlara ilk deðerlerini veririz..Birincisini 1 yaparýz çünkü belirili bir kaynaða ulaþmayý kontrol eder.Ýkincisini, tamponun uzunluðuna eþitleriz.Üçüncüsünü ise boþluk ve doluluðu gösterir.
/* Kaynaðý kilitlemeye çalýþ - sem #0 */
if (semop(semid, &lock_res, 1) == -1){
perror("semop:lock_res");
}
/* Bir boþ yer kilitle - sem #1 / Bir eleman koy - sem #2*/
if (semop(semid, &push, 2) != -1){
printf("---> Ýþlem:%d\n", getpid());
}
else{
printf("---> Ýþlem:%d TAMPON DOLU\n", getpid());
}
/* Kaynaktaki kilidi kaldýr */
semop(semid, &rel_res, 1);
Y iþlemi, kaynaðý lock_res fonksiyonunu kullanarak kilitlemeya çalýþýr.Bunu gerçekleþtirdikten sonra kaynaða bir eleman koyar ve bunu ekrana yazar.Eðer bu iþlemi yapamazsa ekrana "TAMPON DOLU" iletisini yazar.Daha sonra kaynaðý serbest býrakýr.
/* Kaynaðý kilitlemeyi dene - sem #0 */
if (semop(semid, &lock_res, 1) == -1){
perror("semop:lock_res");
}
/* Serbest boþluðun kilidini kaldýr - sem #1 / Bir
eleman al - sem #2 */
if (semop(semid, &pop, 2) != -1){
printf("<--- Ýþlem:%d\n", getpid());
}
else printf("<--- Ýþlem:%d TAMPON BOÞ\n",
getpid());
/* Kaynaðý býrak */
semop(semid, &rel_res, 1);
O iþlemi, az çok Y kadar çalýþtýrýlýr:Kaynaðý kilitler, bir eleman alýr ve kaynaðý serbest býrakýr.
Bir sonraki makalede, ileti kuyruklarýndan, Ýþlemlerarasý Ýletiþimin
ve eþzamanlamanýn farklý bir yapýsýndan bahsedeceðiz.Bu makaleyi kullanarak
yazdýðýnýz programlarý -basit olsalar bile- bana da gönderin ( isim ve
e-posta adreslerinizle birlikte ).Bunlarý okumaktan memnun olacaðým.Ýyi
çalýþmalar.
|
Görselyöre sayfalarýnýn bakýmý, LinuxFocus Editörleri tarafýndan yapýlmaktadýr
© Leo Giordani, FDL LinuxFocus.org |
Çeviri bilgisi:
|
2003-02-03, generated by lfparser version 2.35