Párhuzamos programozást támogató nyelvi eszközök összehasonlítása: Nyelvek, eszközök: PVM
PVM (Parallel Virtual Machine) egy software rendszer, amellyel hálózatba kapcsolt számítógépeket egyetlen nagy párhuzamos számítógépként lehet látni, és kezelni.
A rendszer fejlesztése 1989-ben kezdôdött meg az Oak Ridge National Laboratory-nál. Azóta - több amerikai intézmény támogatásával - a rendszer kiteljesedett és a párhuzamos programok írásának egyik szabványává vált. Nagy elônye, hogy a publikus változata ingyen elérhetô, emellett sok hardware gyártó biztosítja a saját gépére optimalizált, gyorsabb változatát is.
A rendszerben vannak programok, amelyek a felhasználó által írt párhuzamos program futtatását biztosítják, és C, illetve Fortran 77 könyvtárak a programok közötti kommunikáció és szinkronizáció megvalósításához.
PVM rendszerbe kapcsolhatunk több - akár különbözô típusú - számítógépet. A rendszer a rajta futó programok szempontjából ezek után egy nagy, elosztott memóriájú virtuális számítógépnek látszik: különbözô processzorokon, különbözô programokat indíthatunk el, melyek közötti szinkronizációt, és kommunikációt a PVM könyvtári függvényekkel oldhatjuk meg.
A virtuális gépben egy-egy taszk, vagy folyamat egy-egy UNIX programnak fog megfelelni (vannak más rendszerprogramokhoz írt változatok is). PVM három szinten támogat különbözô felépítésû dolgokat:
A PVM rendszer két részbôl áll. Az elsô
része egy démon program - amelyet pvmd3-nak hívnak
- minden, a rendszerbe bekapcsolt számítógépen fut,
hogy létrejöjjön a virtuális gép. A rendszer
beindításakor minden gépen a felhasználó
jogaival elindul egy-egy példánya ennek a programnak. A
második rész a rutinkönyvtár, amelyben a
felhasználó által hívható
függvények vannak a kommunikáció, taszk
indítás és kezelés támogatására.
2.3.1
Bevezetõ az eszközrõl
PVM-et bárki installálhat egy
számítógépen, ha van rá
azonosítója. Ha egy gépen többen is szeretnék
használni a rendszert, akkor persze érdemes azt egy központi
helyre rakni, hogy ne foglaljon el fölöslegesen területeket. A
továbbiakban a PVM_ARCH név az adott gép
architektúrájának PVM-beli nevét fogja takarni. A
publikus változat nagyjából 35 rendszeren fut, de
léteznek a gépek gyártói által adott
programcsomagok is.
2.3.2
Felhasználói felület
A felület függvényeirôl részletes
információ a User's Guide függelékében,
illetve a rendszerhez adott online dokumentációkban
található. Ez a fejezet csak áttekintést ad a
lehetôségekrôl. A függvények legtöbbje
Fortran 77 nyelvben is használható, de itt csak a C nyelvû
felületrôl esik szó.
A taszkoknak a virtuális gépben egyértelmû azonosítójuk van, amelyet címként lehet használni a kommunikációban és a folyamatok kezelésében. A továbbiakban ezt a tid szó jelöli.
A pvm_mytid rutin hatására a program egy PVM folyamattá válik, bekerül a virtuális gépbe. A visszatérési érték a taszk PVM-beli azonosítója lesz. A rutin többször is meghívható, de csak egyszer hajtja végre az inicializáló lépéseket.
A pvm_exit rutin közli a rendszerrel, hogy kilép belôle. A program ez után folytathatja a mûködését, csak PVM hívásokat nem hajthat már végre.
A pvm_spawn rutin folyamatok indítására szolgál. Az új folyamatot megvalósító futtatható program neve és parancssori argumentumait kell megadni a folyamat leírásához. Az új folyamat helye is meghatározható, erre a PVM sok lehetõséget biztosít:
Folyamatok leállítására a pvm_kill rutin szolgál. Ez a futó folyamatot a futtató operációs rendszer módszerével állítja le. (A rutin neve a UNIX-okon használt módszerbõl, a kill() függvénybõl ered.)
A legegyszerûbb rutin ezek közül a pvm_parent , mely annak a folyamatnak az azonosítóját adja vissza, aki pvm_spawn-nal elindította, vagy a PvmNoParent értéket, ha ilyen nem volt.
Folyamatok állapotának lekérdezésére szolgál a pvm_pstat , mellyel megtudható, hogy egy folyamat fut-e még, vagy létezik-e egyáltalán.
Hasonló feladatot old meg a pvm_mstat , amely gépekrõl szolgáltat információkat. Segítségével megtudható, hogy egy adott gép benne van-e a virtuális gépben, és ha igen, akkor pillanatnyilag elérhetõ-e. (A publikus PVM a virtuális gépben lévõ valódi gépek között nem tart fenn állandó kapcsolatot, ezért rövid ideig tartó kapcsolatszakadásokat képes elviselni.)
A virtuális gép felépítését mondja el a pvm_config rutin: gépek, architektúrák száma, valamint az egyes gépek nevei, rajtuk futó PVM démonok azonosítói, architektúrái, illetve a relatív sebességük.
A virtuális gép összes folyamatáról kérhetõ információ a pvm_tasks rutinnal. Ez minden folyamatról a következô adatokat adja meg: azonosító, szülô azonosítója, állapot, démon azonosítója és a folyamat neve.
Egy adott folyamat a pvm_tidtohost használatával megtudhatja a hozzá tartozó démon azonosítóját, így kiderítheti, hogy melyik gépen fut.
Ezek az eljárások nagy segítséget jelenthetnek hibatûrõ alkalmazások írásánál, futás közbeni - program környezetébõl származó - hibák felismerésénél.
A PVM csak egy virtuális gépet képes kezelni, tehát két futó virtuális gépet nem lehet menet közben összekapcsolni egyetlen rendszerré.
Ennek a lehetõségnek passzív részét - azaz a folyamat válaszát egy signal-ra - az adott operációs rendszer keretei között kell megvalósítani.
Egy signal küldésére a virtuális gép bármely folyamata felé a pvm_sendsig rutin használható. Ha a folyamat másik gépen is fut, akkor a PVM rendszer gondoskodik a signal továbbításáról.
A pvm_notify rutin felkéri a PVM rendszert, hogy bizonyos eseményekkor értesítse ôt üzeneteken keresztül. A figyelhetõ események:
Ezekkel az eszközökkel kivételes eseményeket is jól lehet kezelni, például nagy hiba esetén (vagy akár rendszeres idõközönként) egyszerûen rá lehet venni minden folyamatot, hogy szakítsa meg a futását és mentse el aktuális állapotát.
Az üzenet elküldése általában három részbôl áll. Elôször inicializálni kell egy küldô puffert a pvm_initsend vagy pvm_mkbuf rutinok meghívásával. Másodszor ebben a pufferben össze kell állítani az üzenetet a pvm_pk* nevû függvények használatával. Harmadszor a kész üzenetet el kell küldeni a címzett(ek)hez a pvm_send vagy pvm_mcast rutinokkal.
A pvm_psend rutinnal a három lépést egybe lehet fogni, de csak egy folyamatos tömböt lehet így elküldeni. A rutin elônye, hogy néhány speciális architektúrán sokkal gyorsabb, mint a fenti három lépéses üzenetküldés.
Üzenetek fogadására sokféle megoldás van. Lehet a programot blokkolva, bizonyos idôkorláttal várakozva, vagy nem várva venni egy üzenetet. Specifikálhatjuk, hogy kitôl, és mit szeretnénk venni. Ha a rendszer által biztosított üzenetvételi módszer nem megfelelô, akkor lecserélhetjük azt a pvm_recvf függvény használatával. Az üzenetet a pvm_upk* rutinokkal csomagolhatjuk ki.
Ez a két lépés a pvm_precv rutinnal egybefogható. A pvm_psend és pvm_precv által nyújtott egyszerû módszerek - néhány speciális architektúrától eltekintve - ekvivalensek a többlépéses megoldásokkal.
pufferek: a következô rutinok akkor szükségesek, ha a felhasználó több különbözô puffert szeretne használni az alkalmazásában. PVM-ben mindig egy aktív küldô puffer van, amibe egy elküldendô üzenetet lehet összeállítani, és egy aktív vevô puffer, amiben a legutóbb vett üzenet van benne.
A pvm_mkbuf használható új küldô puffer létrehozása. Beállítható az üzenet összeállításakor használt módszer is:
Az MPI-ban a kommunikátorból ki lehet nyerni azt az információt, hogy az üzenetet fogadó gép milyen adatábrázolást használ, s kell-e használni az XDR kódolást az üzenetek összeállításánál. Az utolsó pont az MPI szokásos üzenetküldéseiben nem értelmezhetõ, hiszen az üzenet összeállítása és elküldése egybeesik. Az utolsó pontnál említett hatást az MPI-ban az állandósított kommunikáció eszközével lehet elérni.
A pvm_initsend törli az aktuális aktív küldô puffert, és egy újat inicializál a megadott kódolással. Ha csak egy puffert használunk, akkor mindig meg kell hívni egy üzenet összeállítása elôtt.
A pvm_freebuf használható egy puffer felszabadítására. Csak akkor kell használni, ha külön készítettünk puffert, vagy átállítottuk az aktív puffert.
Az aktív küldõ puffer beállítására és lekérdezésére a pvm_setsbuf és pvm_getsbuf rutinok, a vevõ puffer beállítására és lekérdezésére a pvm_setrbuf és pvm_getrbuf eljárások használatosak.
Az alapértelmezett pufferek átállításának akkor lehet értelme, ha több PVM-et használó könyvtár van egy alkalmazásban, és ezek egymástól függetlenül szeretnének dolgozni. Ezzel a megoldással megôrizhetik a puffereik aktuális állapotát.
Üzenet összeállítása: a pakoló rutinokkal adatokat rakhatunk be egy már létrehozott küldô pufferbe. Az összeállított üzenet nagyságának csak a memória szab határt, de vigyázni kell, mert semmilyen ellenôrzés nincs a vevô oldalon, hogy azt pakoljuk-e ki amit beleraktunk egy üzenetbe. Egyszerû változók és tömbök pakolása egyszerû, struktúrákat viszont elemenként kell belerakni egy üzenetbe.
A rendszer által ismert adattípusok: byte (pvm_pkbyte), komplex számok (pvm_pkcplx), lebegõpontos számok (pvm_pkfloat) és az egész számok variációi (pvm_pkint). A pakoló függvényekkel egy egész tömböt is be lehet rakni a pufferbe.
C-beli szöveg bepakolása (0-val terminált karaktertömb) a pvm_pkstr használható. Ez nem tárolja a szöveg hosszát, csak a 0 kódú karakterrel jelzi a végét.
A legkényelmesebb megoldás a pvm_packf rutin használata, melyet a printf függvény formátuma szerint lehet üzenetek összeállítására használni. A formátum lehetséges elemeit kibõvítették, hogy tömböket is be lehessen rakni egy pufferbe használatával. (Egyedi változóknál értéket, tömböknél pedig az elsô elem címét kell átadni a további paraméterekben.)
Üzenetküldés és vétel:
Üzenet elküldésének legegyszerûbb módja a pvm_send használata. Ez egy adott azonosítójú folyamatnak, tetszõleges üzenettípussal (tárgy megjelöléssel) elküldi az aktív üzenetpufferben összeállított üzenetet.
Ha több folyamatnak egyszerre kell elküldeni ugyanazt az üzenetet, akkor érdemes a pvm_mcast eljárást felhasználni. Ennek egy tömbben több címet is ki lehet jelölni. Hatása ekvivalens pvm_send többszöri meghívásával, de egyes esetekben a rendszer így gyorsabban megoldhatja a feladatot. (Pl. egy másik gépre csak egyszer küldi el az üzenetet, amit a gépen futó daemon eloszt az ottani folyamatok között.)
Egy lépéses - MPI-hoz hasonló - üzenetküldésre alkalmas a pvm_psend rutin. Ez egyetlen hívásba sûríti a puffer inicializálásának, feltöltésének és elküldésének lépéseit. Ez csak elõre specifikált adattípusokból állá vektor elküldésére képes. A lehetséges típusok halmazát (byte, komplex szám, lebegõpontos szám és egész szám típusok) nem lehet kibõvíteni, mert a PVM nem biztosít típus konstrukciós mûveleteket.
Ez a rutin az aktív puffert nem változtatja meg. Alapvetõen egyszerû üzenetváltásokhoz tervezték.
A pvm_recv rutin alkalmas üzenet fogadása egy adott azonosítójú folyamattól. Az üzenet típusát is elõ lehet írni. E két paraméter bármelyikét -1-es értékkel helyettesítve, tetszõleges folyamattól, illetve tetszõleges típussal lehet üzenetet fogadni. A pvm_recv(-1,-1) az elsô beérkezô üzenetet fogadja.
A rutin blokkoló, azaz addig vár amíg a megfelelô üzenet meg nem érkezik a folyamathoz. Ha az üzenet megjön, akkor az aktív vételi puffert törli, és azt az új üzenetre állítja.
A pvm_nrecv a pvm_recv-hez hasonlóan üzenet vételére használható, de ha még nem érkezett meg az üzenet, akkor nem vár rá, és 0 értékkel tér vissza.
Ez a rutin tehát nem blokkolja a program futását, de nem is inicializál egy háttér folyamatot az üzenet vételére.
Hatásában hasonló a pvm_probe eljárás is, mely megnézi, hogy érkezett-e a paraméterekben specifikált üzenet a folyamathoz. Ha igen, akkor 0-tól eltérô visszatérési értékkel fejezôdik be. Ez is azonnal visszatér, de az aktív vételi puffert siker esetén sem törli, az üzenetet nem fogadja.
Egy PVM-es program valójában minden hozzá érkezõ üzenetet fogad, de azokat egy várakozási sorba helyezi. Ebbõl a sorból lehet üzeneteket kivenni a fenti rutinokkal, illetve ezt a sort nézik végig üzeneteket keresve.
A pvm_bufinfo mûvelet egy adott pufferben lévô üzenet hosszát, típusát és küldôjének azonosítóját adja meg. Akkor használható jól, ha az üzenet vételekor lett specifikálva a küldõ (-1 érték használata), mégis fontos lenne megtudni azonosítóját, mert válaszolni akar a folyamat a kapott üzenetre.
A vételi függvények családjának még két eleme van: a pvm_trecv segítségével a pvm_recv-hez hasonló módon lehet üzenetet fogadni, de türelmi idõ is elõírható, ameddig a folyamat várakozik a meg nem érkezett üzenetre.
A pvm_precv rutin a pvm_psend párja, azaz az üzenetek vételének és kipakolásának lépéseit fogja össze egyetlen eljáráshívásba.
Még egy érdekes lehetõséget biztosít a PVM rendszer a pvm_recvf mûvelettel. Ezzel az üzenetfogadó eljárások által használt szûrô függvény cserélhetõ le.
Ezt a függvényt akkor hívják meg a fenti eljárások, amikor végignézik a várakozási sor elemeit, és megvizsgálják, hogy egy adott üzenet megfelel-e az elõírt specifikációnak.Ezzel tetszõleges üzenetfogadási stratégiák is megvalósíthatóak, pl. a leghosszabb beérkezett üzenetet fogja venni a pvm_recv rutin a várakozási sorból.
Üzenetek kipakolása: az aktív vevô pufferbôl az összeállításhoz hasonló rutinokkal lehet kinyerni az adatokat. Ezek a korábban említett adattípusokat ismerik (byte (pvm_upkbyte), komplex számok (pvm_upkcplx), lebegõpontos (pvm_upkfloat) és egész típusok (pvm_upkint)).
Szöveg kipakolására a pvm_upkstr rutin, formátum szerinti kipakolásra pedig a pvm_unpackf eljárás használható.
Az összes rutinnál elegendô helyet kell biztosítani az adatok eltárolására.
A PVM rendszer nem ellenôrzi, hogy azt pakoljuk-e ki, amit beleraktunk egy üzenetbe. Erre a felhasználónak kell ügyelni!
A PVM rendszerben a csoportok használata nagyon egyszerû. Egy taszk bármikor csatlakozhat egy csoporthoz, vagy elhagyhatja azt anélkül, hogy a többieket értesítenie kéne. A legtöbb csoportokhoz tartozó függvényt bárki meghívhatja, még akkor is ha nem tagja a csoportnak.
Az adott folyamat a pvm_joingroup rutinnal csatlakozhat egy névvel megjelölt csoporthoz, illetve a pvm_lvgroup eljárással hagyhatja el azt.
Egy folyamat egyszerre több csoportnak lehet tagja, sôt egy csoportból kiléphet, majd újra beléphet futása során kedve szerint.
Minden csoportban minden folyamatnak lesz egy egyedi sorszáma. Ez a számozás 0-tól kezdõdik, de a dinamikus be- és kilépések miatt elõfordulhat, hogy nem folytonos.
A pvm_gettid rutinnal a csoportbeli sorszám alapján megtudható a folyamat azonosítója.
A pvm_getinst ennek inverze, azaz az azonosító alapján megmondja a folyamat csoportbeli indexét.
Egy csoport méretét a pvm_gsize rutinnal lehet lekérdezni. Ez a névvel azonosított csoport lekérdezés pillanatában mért méretét adja vissza. (Semmi garancia nincs arra, hogy ez az érték a rutin visszatéréséig nem változik meg.)
PVM rendszerben az MPI-hoz hasonló kollektív kommunikációval megoldható feladatokat a csoportokhoz tartozó rutinok használatával lehet elvégezni. Ezek hasonló módon viselkednek, mint az ott ismertetett mûveletek, de a PVM sajátosságai miatt (pl. csak az alap adattípusok használhatóak bennük) nem annyira rugalmasak.
Több folyamat szinkronizációjára a pvm_barrier szolgál. Ez addig vár amíg az adott nevû csoportban elõre megadott számú folyamat meg nem hívja ezt a függvényt.
A híváskor a folyamatnak benne kell lennie a csoportban. A találkozóban résztvevõk száma kisebb is lehet mint a csoport mérete.
Ha a csoport mérete már nem változik, akkor apvm_barrirer("csoport", pvm_gsize("csoport"))hívás ekvivalens azMPI_Barrier(csopor_comm)eljáráshívással.
A csoportban lévõ összes folyamatnak - kivéve saját magának - a pvm_bcast rutinnal lehet elküldeni egy üzenetet.
A pvm_reduce rutinnal asszociatív és kommutatív mûvelet lehet végrehajtani egy csoporton belül. A mûvelet eredménye egy kijelölt folyamatba kerül. Következõ algoritmusok vannak elõre definiálva:
Itt is léteznek adatmozgató mûveletek:
A pvm_gather függvénnyel a csoportban lévô folyamatoktól lehet adatokat összegyûjteni. Egy kijelölt folyamat memóriájába gyûlnek össze a többi folyamattól küldött adatelemek. Az adatelemek elhelyezése a folyamat csoportban elfoglalt helyétõl függ.
A pvm_scatter rutinnal a kijelölt folyamat memóriájában lévõ vektor elemeit lehet elosztani a pvm_gather inverz mûködésének megfelelõen.
Ezek a csoportmûveletek tehát rugalmasabbak az MPI-ban definiáltaknál, de a segítségükkel definiált kollektív mûveletek halmaza nem olyan gazdag.
A csoportokkal a PVM-ben könnyedén meg lehet oldani két egymást nem is ismerõ folyamatcsoport csatlakoztatását, hiszen csak a csoport nevét kell ismerni ehhez. (Itt a PVM rendszer tölti be a mindenkit ismerõ folyamat szerepét.)
/* mandel_pvm.c */
/*
* Mandelbrot halmaz szamitasa
*/
#include "pvm3.h"
void main(int argc, char *argv[])
{
pvm_mytid(); /* belepes a PVM rendszerbe */
if(pvm_parent() == PvmNoParent)
master(argc, argv); /* fo program */
else
slave(); /* seged programok */
pvm_exit(); /* kilepes */
}
void master(int argc, char *argv[])
{
...
/* parameterek beolvasasa */
read_args(argc, argv, &mandel, &fn);
np = /* processzorok szama (pl. pvm_conf ) */
/* Segedprogramok inditasa */
pvm_spawn("mandel_pvm", NULL, PvmTaskDefault,
"", np, tids);
...
/* munka szetosztasa a segedek kozott */
for(i=0, sy =0, step_y=mandel.y1;
i<np;
i++, sy+=dy, step_y+=delta_y)
{
pvm_initsend(PvmDataDefault);
pvm_packf("%d %d %lf %lf %lf %lf", mandel.wd, dy,
mandel.x1, step_y, mandel.x2, step_y+delta_y);
pvm_send(tids[i], MANDEL_WORK);
}
...
/* a foprogram is dolgozik */
slice = calc_mandel_tile(&work);
/* eredmenyek osszegyujtese */
for(i=0, sy=0; i<np; i++, sy+=dy)
{
bufid = pvm_recv(tids[i], MANDEL_RETURN);
pvm_upkbyte(pix + (mandel.wd * sy),
mandel.wd * dy, 1);
}
bcopy(slice, pix + (mandel.wd * sy), mandel.wd * work.ht);
/* eredmeny kiirasa egy file-ba */
write_pix(fn, pix, &mandel));
}
void slave(void)
{
...
master = pvm_parent();
/* varakozas az instrukciokra */
pvm_recv(master, MANDEL_WORK);
pvm_unpackf("%d %d %lf %lf %lf %lf", &work.wd, &work.ht,
&work.x1, &work.y1, &work.x2, &work.y2);
/* a kiadott feladat elvegzese */
slice = calc_mandel_tile(&work);
/* az eredmeny visszakuldese */
pvm_initsend(PvmDataDefault);
pvm_pkbyte(slice, work.wd * work.ht, 1);
pvm_send(master, MANDEL_RETURN);
}
A
példa egy egyszerû Task-Farm megoldással
párhuzamosítja a feladatot.
A forráskódból látható, hogy az üzenetküldések között a fordító és a futtató rendszer csak a programozó által biztosított üzenettípus alapján fedezhetné fel a kapcsolatot, tehát a párhuzamos program egészére nem lehet semmilyen automatikus helyességvizsgálatot végezni.
A PVM-ben írt megoldás erõssége a rugalmasság (futás idõben indít új folyamatokat), és a kivételes helyzetek kezelésének lehetõsége (pvm_notify).
/* prodcons_pvm.c */
#include "pvm3.h"
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define PRODCONS_QUEUEID 1
#define PRODCONS_GET 2
#define PRODCONS_GETACK 3
#define PRODCONS_PUT 4
#define PRODCONS_PUTACK 5
#define PRODCONS_END 6
#define PRODCONS_NUMBER 10
#define PRODCONS_TASKNUM 4
#define PRODCONS_TASKNAME "prodcons_pvm"
void queue_task(int);
void consumer_task(int);
void producer_task(int);
void main(int argc, char *argv[])
{
int mytid, rank;
mytid = pvm_mytid();
rank = pvm_joingroup("prodcons");
if(rank == 0) queue_task(mytid);
else if((rank % 2) == 0) consumer_task(mytid);
else if((rank % 2) == 1) producer_task(mytid);
pvm_lvgroup("prodcons");
pvm_exit();
}
void queue_task(int mytid)
{
int tasks[PRODCONS_TASKNUM];
int active_num, i;
int queue[PRODCONS_NUMBER];
int head=0, tail=0, full=0, empty=1;
/*
* starting and initializing new tasks
*/
active_num = pvm_spawn(PRODCONS_TASKNAME, NULL,
PvmTaskDefault, "", PRODCONS_TASKNUM, tasks);
pvm_initsend(PvmDataDefault);
pvm_pkint(&mytid, 1, 1);
for(i = 0; i < PRODCONS_TASKNUM; i++)
{
if(tasks[i] > 0)
pvm_send(tasks[i], PRODCONS_QUEUEID);
}
/*
* main cycle
*/
while(active_num > 0)
{
int bufid, sender;
if((bufid = pvm_nrecv(-1, PRODCONS_END)) > 0)
{
active_num--;
}
if(!full &&
((bufid = pvm_nrecv(-1, PRODCONS_PUT))>0))
{
int number;
pvm_upkint(&number, 1, 1);
queue[head++] = number;
head %= PRODCONS_NUMBER;
pvm_bufinfo(bufid, NULL, NULL, &sender);
pvm_initsend(PvmDataDefault);
pvm_send(sender, PRODCONS_PUTACK);
if(head == tail)
{
printf("t0x%x queue is full\n", mytid);
fflush(stdout);
full = 1;
}
empty = 0;
}
if(!empty &&
((bufid = pvm_nrecv(-1, PRODCONS_GET))>0))
{
int number;
number = queue[tail++];
tail %= PRODCONS_NUMBER;
pvm_bufinfo(bufid, NULL, NULL, &sender);
pvm_initsend(PvmDataDefault);
pvm_pkint(&number, 1, 1);
pvm_send(sender, PRODCONS_GETACK);
if(head == tail)
{
printf("t0x%x queue is empty\n", mytid);
fflush(stdout);
empty = 1;
}
full = 0;
}
}
}
void producer_task(int mytid)
{
int queue_tid;
int number;
int i;
pvm_recv(pvm_parent(), PRODCONS_QUEUEID);
pvm_upkint(&queue_tid, 1, 1);
srand(mytid);
number = rand() % 100;
for(i=0; i<PRODCONS_NUMBER; i++)
{
number++;
printf("t0x%x producing: %d\n", mytid, number);
fflush(stdout);
pvm_initsend(PvmDataDefault);
pvm_pkint(&number, 1, 1);
pvm_send(queue_tid, PRODCONS_PUT);
pvm_recv(queue_tid, PRODCONS_PUTACK);
}
pvm_initsend(PvmDataDefault);
pvm_send(queue_tid, PRODCONS_END);
}
void consumer_task(int mytid)
{
int queue_tid;
int number;
int i;
pvm_recv(pvm_parent(), PRODCONS_QUEUEID);
pvm_upkint(&queue_tid, 1, 1);
pvm_initsend(PvmDataDefault);
for(i=0; i<PRODCONS_NUMBER; i++)
{
pvm_send(queue_tid, PRODCONS_GET);
pvm_recv(queue_tid, PRODCONS_GETACK);
pvm_upkint(&number, 1, 1);
printf("t0x%x consuming: %d\n", mytid, number);
fflush(stdout);
}
pvm_initsend(PvmDataDefault);
pvm_send(queue_tid, PRODCONS_END);
}
PVM-ben az MPI-hoz hasonlóan szinte minden párhuzamosságot igénylõ feladat megoldható több-kevesebb ráfordítással. A rendszer dinamikus folyamat kezelése miatt kliens/szerver alkalmazások írására is alkalmas lehet.
A rendszerben találhatóak elemek a kivételes esetek kezelésére is (pvm_notify), amelyek nagy segítséget jelentenek hibatûrõ alkalmazások írásakor.
A PVM rendszer csak programok párhuzamosságát támogatja. Nincsenek benne eszközök adat párhuzamosság, illetve folyamat-szál, utasítás szintû párhuzamosság támogatására.
Dinamikus folyamat modellje viszont nagy rugalmasságot ad a rendszernek. Bármikor lehet új folyamatokat létrehozni, és ezeket bármikor meg lehet állítani, akár egy másik folyamatból is. A folyamatok indításakor rábízhatja a felhasználó a rendszerre, hogy válassza ki a szerinte megfelelõ processzort az új folyamat futtatására, de erre kikötéseket is tehet. A rendszer legújabb változataiban az erõforrás kezelõ programrészeket le lehet cserélni, ezzel az adott környezet lehetõségeit maximálisan kihasználó erõforrás kezelési stratégiákat lehet implementálni.
PVM-ben nincsenek virtuális processzorok, de speciális változók beállításával az egyes folyamatok között csatornákat is létre lehet hozni, a gyorsabb kommunikáció érdekében.
PVM-ben csak aszinkron, pufferelt üzenetküldés áll a felhasználó rendelkezésére a folyamatok közötti kommunikáció megszervezése során. Ezek az üzenetküldések szimmetrikus címzést tesznek lehetõvé, és az üzenetek sorrendjét két pont között megõrzik.
Az üzenet küldés mindig blokkoló eljárás, de ez az aszinkron tulajdonság miatt nem hátráltatja nagyon a folyamatok futását. Az üzenetek vételére vannak blokkoló, nem blokkoló és lejárati idõhöz kötött eljárások is.
A szinkronizációra csak több folyamat egy pontnál való várakoztatása (pvm_barrier) használható fel függetlenül. Szinkron üzenetküldés is szimulálható egy aszinkron üzenetküldés párral, de ennél magasabb szintû szinkronizációs módszerek megvalósítására már nincs egyszerû megoldás.
A PVM rendszer egyéb jellemzõi:
A PVM rendszer tehát a használat során fejlõdött mai állapotáig, ezért sok nagyon hasznos, és számításigényes feladatok megoldása során felmerülõ problémák megoldására alkalmas eljárást tartalmaz. Néhány - kompatibilitás miatt megõrzött - megoldástól eltekintve nagyon jól használható eszköz párhuzamos programok írására.
|
|
|
|