Part 4: Adding Parallel Programming Capabilities to C++ Through the PVM

6.3.1. Process Management & 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 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 et al 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 recipt 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, doubles, floats, int s, longs, bytes, etc. Table 6-3 shows the list of pvmpk routines by type.

Table 6-3

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

· In Place

XDR (External Data Representation) is a standard used to describe and to 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 exist 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 arbitrary length. This provides for maximum flexibility within the application. The counter parts to the pvm_send messages are the PVM receive messages. There are five important functions in the receive family of routines.





Sample chapter, summaries, captions, table of contents, code example and listings are provided for your information. Copyright 2003 Addison Wesley. All rights reserved. No part of these materials may be duplicated or reproduced, in any form or by any means, without the written permission of the publisher.