A. függelék - Absztrakt programok megvalósítása C/PVM-ben

Absztrakt programok megvalósításához a PVM szolgáltatásait használhatjuk. A PVM a Parallel Virtual Machine rövidítése, egy köztes réteg, amely az operációs rendszer és a felhasználó program között helyezkedik el. A PVM feladata, hogy programozási nyelvtől és operációs rendszertől független egységes felületet biztosítson elosztott programok komponenseinek együttműködéséhez. A PVM szolgáltatásait egy függvénykönyvtáron keresztül vehetjük igénybe. A könyvtár leglényegesebb elemei a pvm_mytid, a pvm_send, a pvm_recv, a pvm_spawn függvények, amelyek segítségével egy folyamat bejelentkezhet a PVM rendszerbe, üzenetet küldhet és fogadhat, illetve folymatot indíthat. A PVM használatához ismernünk kell azt a felületet is, amelyet az operációs rendszer nyújt a programok fordításához, összeszerkesztéséhez, futtatásához. A futtatás előtt össze kell állítanunk azon számítógépek halmazát, amelyen elosztott programunk működni fog. Az alábbiakban a Linux operációs rendszerre jellemző parancsokat mutatjuk be. Először létre kell hoznunk a pvm3/bin/LiNUX könyvtárat, ahol a PVM rendszer a futtatható állományokat keresi. A futtatható programot az aimk program segítségével állíthatjuk elő egy megfelelő Makefile alapján. Helyezzük el a forrászöveget és a make file-t egy alkönyvtárban majd adjuk ki az aimk parancsot. A futtatható állományra mutató hivatkozásokat helyezzük el a pvm3/bin/LiNUX könyvtárba. Indítsuk el a PVM konzolt a pvm paranccsalA pvm -nlocalhost paranccsal indíthatjuk el a konzolt, ha nincs hálózati összeköttetés más számítógépekkel. , majd bővítsük az igénybe vett számítógépek halmazát az add számítógépnév paranccsal. Végül futtassuk a programot a spawn -> programnév utasítással. A konzolt a halt utasítással állíthatjuk le.


 pvm3/src/hello:  hello.c, hello\_other.c, Makefile.aimk
 aimk
 aimk links
 pvm
 pvm> spawn -> hello
 pvm> add nyl35
 pvm> conf
 pvm> halt

Példaként bemutatunk egy egyszerű C nyelvű programot, amely PVM egy printf függvényhívásban bejelentkezik a PVM rendszerbe és kiírja a saját folyamatazonosítóját a képernyőre, majd elindít egy másik folyamatot (hello_other) és üzenetet fogad tőle pvm_recv. A kapott üzenetet egész számként értelmezve kicsomagolja és elhelyezi a num változóban, majd kiírja a képernyőre.


 hello.c
 
 #include <stdio.h>
 #include "pvm3.h"
 int main() {
      int tid;
      int num;
      printf("i’m t%x\n", pvm_mytid());
      pvm_spawn( "hello_other",(char**)0, 0, "", 1, &tid);
      pvm_recv(-1, -1);
      pvm_upkint(&num,1,1);
      printf("from t%x: %d\n", tid, num);
      pvm_exit();
     return 0;
 }

Az elindított gyernekfolyamat is bejelentkezik a PVM rendszerbe a pvm_mytid hívással, majd azonosítva azt őt indító folyamatot (pvm_parent) üzenetként elküldi ennek a folyamatnak az ő szonosítóját. Az üzenetküldés három lépésből áll, az üzenetküldő puffer inicializálásából (pvm_initsend), az üzenet becsaomagolásából (pvm_pkint) és magából az üzenetküldésből (pvm_send).


 hello_other.c
 
 #include "pvm3.h"
 
 int main() {
      int tid = pvm_mytid();
      int ptid = pvm_parent();
      pvm_initsend(PvmDataDefault);
      pvm_pkint(&tid,1,1);
      pvm_send(ptid, 1);
      pvm_exit();
      return 0;
 }

Második példánk az asszociatív művelet eredményét kiszámító absztarkt program egy lehetséges C/PVM megvalósítása. A példaprogramban az adatok egész számok, a művelet az összeadás. Az absztrakt program közvetlenül hivatkozhat a mátrix elemeire, osztott változókat használ. PVM-ben ez nem lehetséges, a folyamatok csak üzenetek útján cserélhetnek információt. Az alábbi megvalósítás a . ábra minden oszlopához egy-egy folyamatot rendel hozzá, amelyik rendre kiszámítja az oszlop elemeit felülről lefelé. A következő elem kiszámításához mindig szükség van az előző elemre és egy másik oszlopból (egy másik folyamattól) egy további elemre, ha azt már meghatározták. Adatvezérelt megoldást készítünk, azaz nem a szükséges adatok elkérésére kerül sor, hanem az elkészült részeredmények kérés nélkül jutnak el azokhoz a folyamatokhoz, amelyeknek szükségük van rá. Az számítást végző folyamatok elején egész számok küldését és fogadását megkönnyítő segédfüggvények találhatóak. Az . folyamat első lépésben saját és a többi folyamat azonosítóját kapja meg az őt indító szülő folyamattól, majd ciklusban küld részeredményket és fogad adatokat. Végül kiiírja az első szám összegét.

assoc.c:


 #include <stdio.h>
 #include <stdlib.h>
 #include "pvm3.h"
 
 void sendInt( int to, int mit ){
         pvm_initsend(PvmDataDefault);
         pvm_pkint(&mit,1,1);
         pvm_send(to,0);
 }
 
 int recvInt( int from ){
         int data;
         pvm_recv(from,0);
         pvm_upkint(&data,1,1);
         return data;
 }
 
 void main(){
         int tasknum;
         int *tids;
         int id;
         int data;
         int t = 1;
 
         pvm_mytid();
 
         pvm_recv(pvm_parent(),0);
         pvm_upkint(&tasknum,1,1);
         tids = (int *)malloc(1+tasknum*sizeof(int));
         pvm_upkint(&tids[1],tasknum,1);
         pvm_upkint(&id,1,1);
         pvm_upkint(&data,1,1);
 
         while (   ( id+t <= tasknum ) || ( id-t >= 1 )    ) {
                 if ( id-t >= 1 )

                         sendInt(tids[id-t],data);
                 if ( id+t <= tasknum )
                         data += recvInt(tids[id+t]);
                 t<<=1;
         }
 
         printf("partial sum[%d..%d] =\t%d\n",id,tasknum,data);
         pvm_exit();
 }
 
 

A főprogram a parancssorból olvassa be az vektor elemeit, majd a vektor méretének megfelelő számú folyamatot indít. Egy for ciklusban minden gyermekfolyamatot inicializál, elküldve annak saját és a többi folyamat azonosítóját, ill. a vektor megfelelő elemét.


 
 #include <stdio.h>
 #include <stdlib.h>
 #include "pvm3.h"
 
 void main( int argc, char *argv[] ){
         int tasknum = argc-1;
         if (tasknum>0) {
                 int i;
                 int *tids = (int *)malloc(1+tasknum*sizeof(int));
                 pvm_mytid();
                 pvm_spawn("assoc",(char **)NULL,
                           "",tasknum,&tids[1]);
                 for (i=1;i<=tasknum;i++){
                         int data = atoi(argv[i]);
                         pvm_initsend(PvmDataDefault);
                         pvm_pkint(&tasknum,1,1);
                         pvm_pkint(&tids[1],tasknum,1);
                         pvm_pkint(&i,1,1);
                         pvm_pkint(&data,1,1);
                         pvm_send(tids[i],0);
                 }
                 pvm_exit();
         } else  fprintf(stderr,
 "The numbers are given in the command line!\n");
 }