6.3 The Basic Mechanics of the PVM

The PVM environment consists of two components : the PVM daemon ( pvmd ) and the pvmd library. One pvmd daemon runs on each host computer in the virtual machine. The pvmd serves as a message router and controller. A pvmd is used to start additional pvmd s. Each pvmd manages the lists of PVM tasks on its host machine. The pvmd performs process control, some minimal authentication, and fault tolerance. Usually the first pvmd is started manually. This pvmd then starts the other pvmd s. Only the original pvmd may start additional pvmd s. Only the original pvmd may unconditionally stop another pvmd .

The pvmd library contains the routines that allow one PVM task to interact with other PVM tasks. The library also contains routines that allow the PVM task to communicate with its pvmd . Figure 6-6 shows the basic architecture of the PVM environment.

Figure 6-6. Basic architecture of the PVM environment.

graphics/06fig06.gif

The PVM environment will consist of two or more PVM tasks. Each task will contain one or more send buffers. However, only one send buffer may be active at a time. This is called the active send buffer. Each task has an active receive buffer. Notice in Figure 6-6 that communication between PVM tasks is actually accomplished using TCP sockets. The pvm_send() routines make socket access transparent. The programmer does not access the TCP socket calls directly. Figure 6-6 also shows PVM tasks communicating to their pvmd s using TCP sockets and pvmd s communicating between themselves using UDP sockets. Again, the socket calls are performed by the PVM routines. The programmer does not have to do low-level socket programming. The PVM routines we use in this book fit into four categories:

  • Process Management and Control

  • Messaging Packing and Sending

  • Message Unpacking and Receiving

  • Message Buffer Management

While there are other categories of PVM routines, such as the Information and Utility Functions and the Group Operations, we focus on the message processing and process management routines. We will discuss any other routines in the context of the programs in which they are used.

6.3.1 Process Management and Control Routines

There are six commonly used process management and control routines.

Synopsis

 #include "pvm3.h" int pvm_spawn(char *task, char **argv, int flag, char *location,               int ntask,int *taskids); int pvm_kill(int taskid); int pvm_exit(void); int pvm_addhosts(char **hosts,int nhosts,int *status); int pvm_delhosts(char **hosts,int nhosts,int *status); int pvm_halt(void); 

The pvm_spawn() routine is used to create new PVM tasks. The routine can specify how many tasks to create, where to create the tasks, and arguments to be passed to each task. For example:

 pvm_spawn("agent_filters",argv++,1,"host 3",30,&Task3); 

The task parameter should contain the name of the program that the pvm_spawn() is to execute. Since the program that is executed by the pvm_spawn() routine is a standalone program, command-line arguments may be required. The argv parameter is used to pass any command-line arguments to the program. The location parameter specifies which host the task is to be executed on. The taskids parameter will contain either the task identifiers for the spawned tasks or status codes representing any error conditions that might have been created during the spawn process. The ntasks parameter specifies how many instances of the task to create. The pvm_kill() routine is used to kill tasks other than the calling task. The taskid passed to pvm_kill() can reference any other user -defined task in the PVM. This routine works by sending the SIGTERM signal to the PVM task to be killed . The pvm_exit() routine is used to cause the calling task to be removed from the PVM. While the task can be removed from the PVM, the process that the task belonged to may continue to execute. Keep in mind that a task executing PVM calls may have other work to perform that is not related to the PVM. The pvm_exit() routine should be called by any task that no longer has work relevant to the PVM processing. The pvm_addhosts() allows the caller to dynamically add more computers to an existing PVM. Typically, the pvm_addhosts() is called with a list of one or more hostnames:

 int Status[3]; char *Hosts[] = {"porthos", "dartagnan","athos"}; pvm_addhosts("porthose",1,&Status); //... or ... pvm_addhosts(Hosts,3,Status); 

The Hosts parameter will usually contain the names of one or more hosts listed in the .rhosts file or the .xpvm_hosts file. The nhost parameter will contain the number of hosts to be added to the PVM, and the status parameter will contain a value = to nhosts if the call was successful. If the call was not able to add any hosts, the return value will be less than 1 . If the call was only partially successful, the return value will represent the number of hosts added. Likewise, the pvm_delhosts() allows the caller to dynamically remove one or more computers from an existing PVM. The hosts parameter will contain a list of one or more hosts. The nhosts parameter will contain the number of hosts to be removed. For instance:

 pvm_delhosts("dartagnan",1); 

causes the computer with hostname dartagnan to be removed from the PVM environment. The pvm_addhosts() and pvm_delhosts() may be called during runtime. This allows the programmer to have a dynamically sizeable PVM. Any PVM task running on a host computer that is deleted from the PVM will be killed. If there are any pvmd s running on the computers that are deleted from the PVM, the pvmd s will be stopped also. If a host fails for some reason, the PVM environment will automatically delete the host. The return values for pvm_delhosts are the same as they are pvm_addhosts() . The pvm_halt() routine shuts down the entire PVM system. All tasks and pvmd s are stopped.

6.3.2 Message Packing and Sending

Geist, Beguelin, and colleagues state the message model of the PVM environment accordingly :

PVM daemons and tasks can compose and send messages of arbitrary lengths containing typed data. The data can be converted using XDR 1 when passing between hosts with incompatible data formats. Messages are tagged at send time with a user-defined integer code and can be selected for receipt by source address or tag. The sender of a message does not wait for an acknowledgement from the receiver, but continues as soon as the message has been handed to the network and the message buffer can be safely deleted or reused. Messages are buffered at the receiving end until received. PVM reliably delivers messages, provided the destination exists. Message order from each sender to each receiver in the system is preserved; if one entity sends several messages to another, they will be received in the same order.

The PVM library consists of a family of routines used to pack the various data types into a send buffer. There are pack routines for character arrays, double s, float s, int s, long s, byte s, and so on. Table 6-3 shows the list of pvmpk routines by type.

Table 6-3. pvmpk Routines

Message Packing Functions

bytes

 int pvm_pkbyte(char *cp, int count, int std); 

complex/double complex

 int pvm_pkcplx(float *xp, int count, int std); int pvm_pkdcplx(double *zp, int count, int std); 

double

 int pvm_pkdouble(double *dp, int count, int std); 

float

 int pvm_pkfloat(float *fp, int count, int std); 

int

 int pvm_pkint(int *np, int count, int std); 

long

 int pvm_pklong(long *np, int count, int std); 

short

 int pvm_pkshort(short *np, int count, int std); 

string

 int pvm_pkstr(char *cp); 

Each of the pack routines in Table 6-3 are used to store an array of data in the send buffer. Notice in Figure 6-6 that each PVM task will have at least one send and receive buffer. Each of the pack routines takes a pointer to an array of the appropriate data type. Every pack routine except for pvm_pkstr() takes the total number of items to be stored in the array (not the number of bytes!). The pvm_pkstr() routine assumes the character array it is working with will be NULL terminated . Each pack routine except the pvm_pkstr() has as the last parameter a value that represents how to traverse the source array as items are selected to be packed into the send buffer. The parameter is often referred to as the stride. For instance, if the stride is four, then every fourth element will be selected from the source to be stored in the send buffer. It is important to note that the pvm_initsend() routine should be used prior to sending each message. The pvm_initsend() routine clears the buffer and prepares it to send the next message. The pvm_initsend() routine prepares the buffer to send the message in one of three formats: XDR, Raw, or In Place.

XDR (External Data Representation) is a standard used to describe and encode data. Keep in mind that the hosts involved in a PVM can be different machine types. For instance, a PVM might consist of Sun, Macintosh, Crays, and AMD machines. These machines might have different word sizes and may store data types differently. In some instance the bit ordering might be different from one machine to another. The XDR standard is used to allow the machines to exchange data in an architecture-independent way. The Raw format is used to send the data in the native format of the sending machine. No special encoding is used. The in place format really does not pack the data in the send buffer. Instead, only pointers to the data and size of the data is sent. The receiving task copies the data directly. These three types of encoding are represented by three constants in the PVM library:

PvmDataDefault

XDR

PvmDataRaw

No special encoding

PvmDataInPlace

Only pointers and sizes copied to send buffer

For example:

 int BufferId; BufferId = pvm_initsend(PvmDataRaw); //... 

specifies that data be packed into the send buffer as is, that is, with no special encoding. If the pvm_initsend() call is successful, it will return the number of the send buffer in BufferId . It is important to remember that although only one send buffer can be active at a time, a PVM task can have multiple send buffers. Each buffer will have a number associated with it. The PVM library defines several send routines.

Synopsis

 #include "pvm3.h" int pvm_send(int taskid, int messageid); int pvm_psend(int taskid, int messageid,char *buffer,int len,               int datatype); int pvm_mcast(int *taskid,int ntask,int messageid); 

In each of these routines, taskid is the task identifier of the PVM task that receives the message. In the case of pvm_mcast() , the taskid refers to a collection of tasks represented by the task identifiers passed in the array *taskid . The messageid parameter specifies which message to send. The message identifiers are integers and are user-defined. They are used by the sender and receiver to identify which message will be waited on by the receiver and which message will be sent by the sender. For example:

 pvm_bufinfo(N,&NumBytes,&MessageId,&Ptid); //... switch(MessageId) {    case 1 : // do something             break;    case 2 : // do something else             break             //... } 

In this case, the pvm_bufinfo() routine is used to get information about the last message received by receive buffer N . We can get the number of bytes, the messageid , and who sent the message. Once we get the messageid we can execute the appropriate logic. The pvm_send() routine performs a pseudo-blocking send to the specified task, that is, the task only blocks as long as it takes to make sure that the message has been properly sent. The task does not wait for the receiver to actually receive the task. The pvm_psend() routine sends the message directly to the specified task. Notice that the pvm_psend() routine has a buffer parameter used to contain the message to be sent. The pvm_mcast() is used to send a message to multiple tasks simultaneously . The arguments for the pvm_mcast() will include an array of taskids that receives the message, the number of tasks involved in the multicast, and the messageid to identify the message sent. Figure 6-6 shows that each PVM task has its own send buffer. The buffer exists just long enough for the message to be guaranteed to be on its way.

With the exception of control messages, the meaning of the messages between any two PVM tasks is application defined, that is, the sending and the receiving task must have a predefined use for each message. The messages are asynchronous, of arbitrary data types, and of arbitrary length. This provides for maximum flexibility within the application. The counterparts to the pvm_send messages are the PVM receive messages. There are five important functions in the receive family of routines.

Synopsis

 #include "pvm3.h" int pvm_recv(int taskid, int messageid); int pvm_nrecv(int taskid, int messageid); int pvm_precv(int taskid, int messageid, char *buffer,               int size, int type, int sender, int messagetag,               int messagelength); int pvm_trecv(int taskid,int messageid,               struct timeval *timeout); int pvm_probe(int taskid, int messageid); 

The pvm_recv() routine is used to receive messages from other PVM tasks. This routine creates a new active buffer that will contain the message received. The taskid parameter specifies the task identifier of the sending task. The messageid parameter identifies the message that is being sent from the sender. Keep in mind that a task may send multiple messages, each with different or the same messageid . If the taskid = -1 , then the pvm_recv() routine will accept a message from any task. If the messageid parameter = -1 , then the routine will accept any message. The pvm_recv() routine return value will be the buffer id of the new active buffer if the call is successful and will be a value < 0 if an error has occurred. When a task calls the pvm_recv() routine will block and wait until the message has been received. After the message is received, it is retrieved from the active message buffer using one of the unpack routines. For instance:

 //... float Value[10]; pvm_recv(400002,2); pvm_unpkfloat(400002, Value,1); cout << Value.. 

The pvm_recv() routine causes this code to wait on a message from a task identified as 400002 . The messageid received from 400002 must be 2 before the routine unblocks. The unpack routine is then used to retrieve the array of float s. Whereas the pvm_recv() routine causes the task to wait until it receives a message, the pvm_nrecv() routine is a nonblocking receive. If the appropriate message has not arrived, the pvm_nrecv() routine will immediately return. If the message has arrived, the pvm_nrecv() will return immediately and the active buffer will contain the message. If an error condition occurs, then pvm_nrecv() will return a value < 0 . If the message has not arrived, the routine returns . If the message has arrived, the number for the new active buffer will be returned. The taskid parameter will contain the task identifier for the sending task. The messageid parameter will contain a user-defined message id. If the taskid = -1 , then the pvm_nrecv() routine will accept a message from any task. If the messageid = -1 , or then the routine will receive any message. When messages are received in the active buffer by either pvm_recv() or pvm_nrecv() , a new active buffer is created and the current receive buffer is cleared.

Whereas the pvm_recv() , pvm_nrecv() , and the pvm_trecv() receive their messages into a new active buffer, the pvm_precv() routine receives its message directly into a user-defined buffer. The taskid parameter contains the task identifier for the sending task. The messageid parameter identifies which message is being received. The buffer parameter will contain the actual message. So instead of getting the message from the active buffer using one of the unpack routines, the message is retrieved directly from the buffer parameter. The size parameter contains the length in bytes of the message. The type parameter specifies the data type of the message. The values for data type are:

PVM_STR

PVM_BYTE

PVM_SHORT

PVM_INT

PVM_FLOAT

PVM_DOUBLE

PVM_LONG

PVM_USHORT

PVM_CPLX

PVM_DCPLX

PVM_UINT

PVM_ULONG

The pvm_trecv() is an important routine that allows the programmer to use a timed receive. The pvm_trecv() routine causes the calling task to block and wait for the message, but only for the amount of time specified for the timeout parameter. The specified parameter is a structure of type timeval defined time.h . For example:

 #include "pvm3.h"  //...  struct timeval TimeOut;  TimeOut.tv_sec = 1000;  int TaskId;  int MessageId;  TaskId = pvm_parent();  MessageId = 2;  pvm_trecv(TaskId,MessageId,&TimeOut);  //... 

the TimeOut variable has the tv_sec member set to 1000 seconds. The timeval struct can be used to set the timeout values in seconds and microseconds. The timeval is a struct has the structure:

 struct timeval{    long tv_sec; // seconds    long tv_usec; // microseconds }; 

This means the pvm_trecv() routine will block the calling task for at the most 1000 seconds. If this message gets there before the 1000 seconds have expired , the routine will return. This routine can be used to help prevent indefinite postponement and deadlock. If pvm_trecv() is successful, it will return the number of the new active buffer. If an error occurs, then a value < 0 will be returned. If taskid = -1 , the routine will accept a message from any sender. If the messageid parameter = -1 , it will accept any message.

The pvm_probe() routine determines whether a particular message has arrived from the specified sender. The taskid parameter identifies the sender. The messageid parameter identifies the particular message. If the pvm_probe() routine sees the specified message, then the routine returns the buffer number for the new active buffer. If the specified message has not arrived, the routine returns a . If an error condition has occurred, the routine will return a value < 0 .

Synopsis

 #include "pvm3.h" int pvm_getsbuf(void); int pvm_getrbuf(void); int pvm_setsbuf(int bufferid); int pvm_setrbuf(int bufferid); int pvm_mkbuf(int Code); int pvm_freebuf(int bufferid); 

There are six useful buffer management routines that can be used for setting, identifying, and dynamically creating the send and receive buffers. The pvm_getsbuf() routine is used to get the number for the active send buffer. If there is no current buffer, this routine will return . The pvm_getrbuf() routine is used to get the id number for the active receive buffer. Keep in mind that every time a message is received, a new active receive buffer is created and the current receive buffer is cleared. If there is no current receive buffer, pvm_getrbuf() will return . The pvm_setsbuf() routine sets the active send buffer to bufferid . Typically, a PVM task has only one send buffer. However, sometimes multiple send buffers are required. Although only one send buffer can be active at a time, a PVM task may create additional send buffers using the pvm_mkbuf() routine. The pvm_setsbuf() can be used to set the active buffer to send buffers that have been created at runtime. This routine returns the buffer identifier for the previous active send buffer. The pvm_setrbuf() sets the active receive buffer to bufferid . Remember that the PVM unpack routines work with the active receive buffer. If there is more than one buffer, then the pvm_setrbuf() can be used to set the current buffer to be used by the unpack routines. If the call to pvm_setrbuf() is successful, it will return the buffer id of the previous buffer. If the buffer identifier passed to pmv_setrbuf() is not valid or does not exist, then the routine can return one of the following error messages: PvmBadParam or PvmNoSuchbuf . The pvm_mkbuf() routine is used to create a new message buffer. The Code parameter specifies whether the buffer will be set up to contain data encoded as XDR format, native machine format, or pointers and sizes. The Code parameter can be one of three values:

PvmDataDefault

XDR

PvmDataRaw

Machine dependent (no encoding)

PvmDataInPlace

Pointers to the data and sizes of data only used

If the pvm_mkbuf() routine is successful, it will return the buffer id of the new active buffer. If an error occurs, the function will return a value < 0 . For every call to pvm_mkbuf() there should be a call made to pvm_freebuf() when the send buffer is no longer needed. The memory allocated by the pvm_mkbuf() routine is released by pvm_freebuf(). pvm_freebuf() should only be used on a buffer that is no longer needed, for example, after the message has been sent.



Parallel and Distributed Programming Using C++
Parallel and Distributed Programming Using C++
ISBN: 0131013769
EAN: 2147483647
Year: 2002
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net