Párhuzamos programozást támogató nyelvi eszközök összehasonlítása: Nyelvek, eszközök: PVM


2.3 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.

2.3.2.1 Folyamat vezérlés

A PVM rendszer a dinamikus folyamat modell megvalósításában jelenti a legnagyobb elõnyt az MPI szabvánnyal szemben. A rugalmas kezelést a következõ eszközökkel éri el:

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:

Ha sikeresen elindította az új folyamatokat, akkor azok azonosítóit (tid) adja vissza, hogy az indító program kapcsolatot teremthessen velük.

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.)

2.3.2.2 Információk

Egy futó rendszerben bármely folyamatnak lehetõsége van, hogy lekérdezze a rendszer állapotát, megismerje a többi folyamatot.

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.

2.3.2.3 Rendszer dinamikus változtatása

A pvm_addhosts és pvm_delhosts eljárásokkal a virtuális gép is megváltoztatható menet közben. Az elõbbivel egy új gépet lehet hozzáadni, az utóbbival egy ilyen gépet lehet törölni a virtuális gépbõ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é.

2.3.2.4 Signal küldés

A PVM rendszert POSIX szabványnak megfelelõ operációs rendszerekre tervezték, ezért a rajtuk lévõ megszakítás-kezelés eszközét, a signal küldés lehetõségét is beépítették. Ezzel a lépéssel a virtuális gépben lévõ folyamatok normális végrehajtási lépései megszakíthatóak, más útra terelhetõek.

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:

Ezzel akár felügyelõ folyamatokat is lehet implementálni, melyek hiba esetén a kiesett folyamatokat újraindítják, új gép beállása esetén pedig a munkát újra eloszthatják az új erõforrás kihasználása érdekében.

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.

2.3.2.5 Paraméterek beállítása

Lehetõség van a rendszer belsõ paramétereit is megváltoztatni a pvm_setopt és pvm_getopt rutinokkal. Egy átlagos programban erre nincs szükség, de van benne néhány lehetõség, amit érdemes megemlíteni:

2.3.2.6 Üzenetküldés

Folyamatok közötti kommunikáció egyetlen eszköze a PVM-ben az üzenetek küldése és vétele.

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!


2.3.2.7 Folyamatok csoportosítása

A folyamatok dinamikus csoportosítása a PVM publikus változatában a meglévô rutinokra épül rá. Az ehhez szükséges adminisztrációt egy külön program (pvmgs) végzi el. Néhány hardware gyártó által adott változatban ezek is benne vannak a PVM rendszerben, ezért ott nem kell külön programot használni erre a célra (pl.: Cray T3D, Parsytec PowerXplorer).

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 a pvm_barrirer("csoport", pvm_gsize("csoport")) hívás ekvivalens az MPI_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:

A felhasználó maga is írhat ilyen eljárásokat.

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.)

2.3.3 Példák

2.3.3.1 Mandelbrot halmaz számítása

Ez a példa csak a PVM-et használó program lényegét tartalmazza. A szakdolgozat végén megtalálható a példa teljes változata.

/* 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).

2.3.3.2 Termelõ-fogyasztó probléma

Ez a megoldás SIMD modellben íródott. A legelsõ folyamat valósítja meg az osztott, korlátozott sort és indítja a többi folyamatot, melyek megosztva termelõ, illetve fogyasztó feladatokat lát el.

/* 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);
}

2.3.4 Összefoglalás

A PVM rendszert azért fejlesztették ki, hogy egy egységes felületet biztosítsanak párhuzamos programok fejlesztésére valóban párhuzamos architektúrákon, illetve hálózatba kapcsolt számítógépeken. A fejlesztés során a korábbi változatokkal való kompatibilitás megõrzése érdekében megtartottak olyan elemeket is, amelyeket a fejezet elején ismertetett MPI-ban már jobban oldottak meg (pl. üzenet összeállítása).

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.



P�rhuzamos Programoz�si Eszközök, Frohner Ákos
Hosted by www.Geocities.ws

1