Microway Application Note 19

Introduction

The ipcs command 

An Example Script in csh

Interprocess Communications

 

Introduction

   An issue tangentially related to memory performance is the releasing of shared memory segments on multiprocessor computers. These resources are needed for jobs that execute in parallel across multiple component processors. They are not always cleared properly when a job teminates abnormally and must be removed by hand in this case ( if enough uncleared shared memory segments accumulate, eventually parallel jobs will begin to fail. Constant rebooting of the system for every user that uses these resources and has parallel codes crash may not be an attractive solution for large clusters. The technique makes use of the ipcs and ipcrm commands. Other issues like running out of swap space, or removing paging area will be considered later. Refer to Interprocess Communications for more information on the various of types of interprocess communications (IPC).

 The ipcs command

   The ipcs command (a user command) will list the current system status for message queues, shared memory segments and semaphores:

         $ ipcs

         ------ Shared Memory Segments --------

         key    shmid      owner      perms      bytes      nattch      status

         ------ Semaphore Arrays --------

         key    semid      owner      perms      nsems     status

         ------ Message Queues --------

         key    msqid      owner      perms      used-bytes      messages

If you issue this command and there are no conficts this is what you will see.

An Example Script in csh

   Here is a script in csh (can also be written in bash - change to csh to run this script (type csh and press return)) that will clear shared memory segments and semaphores. When run by a normal user, it clears his own resources; when run by root, it clears all resources. Note that there is no danger in removing resources currently in use as they will not be deleted until they are released by the corresponding process. The ipcrm command is used to remove the resources.

          #!/bin/csh

          # clearipc - clear shared memory segments and semaphores

          setenv PATH /bin:/usr/bin:/usr/local/bin:/home:/home/test

          if ("`whoami`" == "root") then

             set us="0x"      # "0x" is the hex key value in every line

          else

             set us="$user"

          endif

  

          # The two awk commands are needed because there may or may not be a space

          # separating the items in the first two columns if the ipcs output.

          foreach thing (`ipcs -m | grep $us | awk -Fm '{print $2}' | awk '{print $1}'`)

               ipcrm -m $thing

          end

          foreach thing (`ipcs -s | grep $us | awk -Fs '{print $2}' | awk '{print $1}'`)

               ipcrm -s $thing

          end         

   The script could be easily extended to clear message queues as well, or to make the kind of item(s) to be cleared as a command line argument. Of course you could directly use the ipcs and ipcrm commands.

Interprocess Communications

   Large programming efforts often use separate processes to manage complexity and risks. Sometimes separate processes provide  enhanced performance on multiprocessor systems. Client/server processes are separate by their very nature. However, once applications become separate processes, there exists a gulf between them when they need to share data. Thus depending on the processes several types of IPC's may occur.  These include:

   Regular files when used with the appropiate lock techniques, can be used to communicate between processes. FIFO's and anonymous pipes can also be used to from pipelines between separate processes. Sockets allow communications with local and remote processes. Finally processes can notify each other using signals.

   The other three forms of IPC are:

   These three forms of IPC establish a new group of facilities because you create and control them in a diifferent manner that the preceeding forms. Except for signals, all preceeding forms used file descriptors to access and to control them. However the above three use different handles.

   The Message Queue:

   The UNIX message queue implements a priority-based queue of messages. The message is simply a short block of memory holding an application-defined message. When a message is queued, it is stored within the kernel memory so that it can be later retrieved by another process. Figure 1 below shows how messages are queued, stored and retreived. The figure shows three processes queuing messages and one process receiving messages. Message queues in general, however, can be queued by many processes and received by many processes. Every queued message has a message priority called a message type. This is set by using the msgsnd() function and is again used by the msgrcv() function that receives the message. These are UNIX library calls. An example program using this is given in program listing 1 for a client/server. The client issues a request and the server receives the message and responds. This message type determines the priority of the message when it is queued. Figure 2 shows a series of messages from A to J being queued. The number preceeding each message letter indicates the priority of the message. For example, 3C indicates message C was queued at priority 3.

Figure 1: The Message Queue Store within the kernel.

Figure 2: Priority Messages placed in a Message Queue.

   The UNIX kernel queues each message into a sub-queue that corresponds to the message priority. If no process is removing messages from the queue, Figure 3 shows how the nine messages would be sorted on the basis of their messge priority. The lowest numbers indicate the highest message priority in messge queues. When the receiving process retrieves a message, it has several choices. These are

   While Figure 3 shows that all messages are queued by priority the UNIX kernel also maintains another linked list that allows it to fetch messages on a FIFO basis. In this manner, a process may choose to ignore the priority of messages and simply fetch the earliest message that was queued. Since messages can be retrieved for a specific message priority, it is possible to use the message priority (message type) to address a message to one of several receiving processes. The message priority is a 31-bit value (the sign-bit cannot be used) on a 32-bit platform and bigger for a 64-bit one. Thus 64-bit platforms can accomodate higher number of processes. Consequently some applications have used the message type for the process ID. Each receiving process simply fetches messages that correspond to its process ID. Figure 3 shows an illustration of this. Each process selects its own messages in Figure 3 by using its process ID as the message priority.

Figure 3: Processes reading messages by process ID.

 

Program Listing 1: Example of message queues using a Client/Server Program.

msq.h - The Msq Class Definition File

// msq.h

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

struct Msg {

       long msgtyp; // Message type/priority

};

class Msq {

       enum state { ready, notReady };

       key_t key; // IPC Key

       int msqid; // IPC ID

       int error; // Last errno

protected:

     void _verify(state s);

public:

      Msq();

      Msq &create(key_t key, int flags);

      Msq &access(key_t key);

      Msq &dispose();

      Msq &destroy();

      msqid_ds &stat(msqid_ds &stbuf);

      Msq &change(msqid_ds &stbuf);

      int send(Msg &msg,size_t size,int flags=0);

      int recv(Msg &msg,size_t &size,size_t maxsz,long msgtyp,int flags=0);

      inline int getError() { return error; }

      inline key_t getKey() { return key;}

};

// End msq.h

 

msqveri.cc - The Implementation of Msq::_verify(),Msq::dispose(), and the Constructor Msq::Msq()

// msqveri.cc

#include <stdlib.h>

#include <errno.h>

#include "msq.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////

// (private) Msq::_verify

// Checks to see that the object is in a ready or

// not ready state.   If the state is not correct

// the error EINVAL is thrown.

///////////////////////////////////////////////////////////////////////////////////////////////////////////

void

    Msq::_verify(state s) {

          if ( s == ready && msqid < 0 )

              throw error = EINVAL;         //     Object is not open

          if ( s != ready && msqid >= 0 )

              throw error = EINVAL;         // Object is open!

     }

///////////////////////////////////////////////////////////////////////////////////////////////////////////

// Msq::dispose

// Disposes of the current message queue reference, if

// any.   The object is re-initialized to the not-ready

// state.

///////////////////////////////////////////////////////////////////////////////////////////////////////////

Msq &

Msq::dispose() {

    key = IPC_PRIVATE;

    msqid = -1;

    return *this;

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////

// Msq::Msq

// Constructor.  This constructor calls upon the

// method Msq::dispose() to initialize the object.

///////////////////////////////////////////////////////////////////////////////////////////////////////////

Msq::Msq() {

Msq::dispose();        //  Initialize this object

}

// End msqveri.cc

 

msqcr.cc - The Implementation of the Msq::create() Method

// msqcr.cc

#include <stdlib.h>

#include <errno.h>

#include "msq.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////

// Msq::create

// ARGUMENTS:

// key      IPC Key of the message queue or IPC-PRIVATE

// flags    The permission bits, and possibly IPC-EXCL

// This method creates a message queue.  Object must be

// in a not-ready state.

///////////////////////////////////////////////////////////////////////////////////////////////////////////

Msq &

Msq::create(key_t key,int flags) {

                _verify(notReady);                             // Object must not be open

                flags |= IPC_CREAT;                // Force a create symantic 

//    Attempt to create the message queue

msqid = msgget(this->key = key,flags);

                if ( msqid == -1 )

                     throw error = errno;

                return *this;

}

//End msqcr.cc

 

msqac.cc - The implementation of the Msq::access() Method

// msqac.cc

#include <stdlib.h>

#include <errno.h>

#include  "msq.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////

//Msq::access

// ARGUMENTS:

// key IPC Key of the message queue or IPC-PRIVATE 12:

// This method accesses a message queue.  Object must be

// in a not-ready state.

///////////////////////////////////////////////////////////////////////////////////////////////////////////

Msq &

Msq::access(key_t key) {

     _verify(notReady);  // Object must not be open 21:

//Attempt to create the message queue 24:

      msqid = msgget(this->key = key,0);

      if ( msqid == -1 )

        throw error = errno;

return *this;

}

// End msqac.cc

 

msqdest.cc-The Implementation of the Msq: :destroy() Method

// msqdest.cc

#include <stdlib.h>

#include <errno.h>

#include "msq.h"

/////////////////////////////////////////////////////////////

// Msq::destroy

// Destroys the message queue.  The object must be in a

// ready state.  The object is placed into a not-ready

// state upon successful completion.

/////////////////////////////////////////////////////////////

Msq &

Msq::destroy() {

  _verify(ready);  // Object must be open

  if ( msgctl(msqid,IPC_RMID,0) == -1

        throw error = errno;

  Msq::dispose();     // Re-initialize this object

  return *this;          // Return in not-ready state

}

// End msqdest.cc

 

msqstat. cc-The Implementation of the Msq::stat()   Method

// msqstat.cc

#include <stdlib.h>

#include <errno.h>

#include "msq.h"

/////////////////////////////////////////////////////////////

// Msq::stat

// ARGUMENTS

// stbuff  The struct msqid-ds structure to populate

//              with message queue information.

//              This method fills the supplied buffer with status

//              information about the current queue.  The object must

//               be in the ready state.

/////////////////////////////////////////////////////////////

msqid_ds &

Msq::stat(msqid_ds &stbuf) {

     _verify(ready);   // Object must be open

     if ( msgctl(msqid,IPC_STAT,&stbuf) == -1)

                  throw error = errno;

     return stbuf;

// End msqstat.cc

 

msqchg. c-The Implementation of the Msq: :change() Method

// msqchg.cc

#include <stdlib.h>

#include <errno.h>

#include "msq.h"

/////////////////////////////////////////////////////////////

// Msq::change

// ARGUMENTS

// stbuff  The struct msqid_ds structure containing

//             the changes to be made.

// Only the values msg_perm.uid, msg_perm.gid, msg_perm.mode

// and msg_qbytes values can be changed.  The value

// msg_qybytes can only be increased by the superuser.

// Object must be in the ready state.

/////////////////////////////////////////////////////////////

Msq &

Msq::change(msqid_ds &stbuf) {

       _verify(ready);   // Object must be open 24:

       if ( msgctl(msqid,IPC_SET,&stbuff) == -1 )

         throw error = errno;

       return *this;

}

// End msqchg.cc

 

msgsend. cc-The Implementation of the Msq: :send() Method

// msqsend.cc

#include <stdlib.h>

#include <errno.h>

#include "msq.h"

/////////////////////////////////////////////////////////////

// Msq::send

// ARGUMENTS

// msg     The message to be sent

// size      The total size of the message

// flags    zero or IPC_NOWAIT (optional)

// RETURNS:

// 0          No message sent (with IPC_NOWAIT)

// 1                Message was sent

// Sends a message of size bytes to the message queue.

// The size must include the total size of the message,

// including the message type.  The object must be in a

// ready state.

/////////////////////////////////////////////////////////////

int

Msq::send(Msg &msg,size_t size,int flags)

      int z;

      size_t msgsz = size - sizeof msg.msgtyp;

      _verify(ready);

      do {

           z = msgsnd(msqid,&msg,msgsz,flags);

      } while ( z == -1 && errno == EINTR);

      if ( z ) {

           if  (  flags & IPC_NOWAIT && errno ==  EAGAIN )

               return 0;  // Not sent

           // Other fatal error:

                throw error = errno;

      }

      return 1;  //  Succeeded

}

// End msqsend.cc

 

msq recv. cc-The Implementation of the Msq::recv()  Method

// msqrecv.c

#include <stdlib.h>

#include <errno.h>

#include "msq.h"

/////////////////////////////////////////////////////////////

// Msq::recv

// ARGUMENTS

// msg     The receiving buffer for the message

// size      The returned size of the message

// maxsz  The maximum size of the returned message

// msgtyp                The message type to use (priority)

// flags    Flags IPC_NOWAIT, IPC_EXCEPT and

//              IPC_NOERROR (optional)

//RETURNS

// 0          No message returned (with IPC_NOWAIT)

// 1                Message was returned

// This method receives a message from the message queue.

// Object must be in a ready state.

/////////////////////////////////////////////////////////////

int

Msq::recv(Msg &msg,size t &size,size t maxsz,long msgtyp,int flags)

        int z;

        size_t msgsz = maxsz - sizeof msg.msgtyp;

        _verify(ready);

        do {

             z = msgrcv(msqid,&msg,msgsz,msgtyp,flags);

        } while ( z==-1 && errno == EINTR );

        if ( z==-1 ) {

            if ( flags & IPC_NOWAIT && errno==EAGAIN )

               return 0;   // No message read

            throw error = errno;   // Error occurred

        }

        size = z + sizeof msg.msgtyp;    // Return size

        return 1;    // Successful

}

// End msqrecv.cc

 

statmsg. h-The Declaration of the StatMsg Message Structure

// statmsg.h

struct StatMsg : Msg {

     enum {

           stat,      //  stat a pathname

           lstat,     //  lstat a pathname

           stop      //  stop the server

      }               request;          //  Request type

      int error;                         //   zero if successful

      pid_t PID;                      //  Requesting Process ID

      union {

         char     path[256];        //  Pathname to stat

         struct   stat stbuf;         //  stat(2) or lstat(2) info

       } u;                                 //  union

}

// End statmsg.h

 

statsrv. cc - The statsrv Server Listing

// statsrv.cc

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include "msq.h"

#include "statmsg.h"

int

main(int argc,char **argv) {

      int quit = 0;   // True when stop received

      Msq q;           //  Message queue object

      StatMsg m;    //  Message buffer

      size_t msz;     //  Message size

      char pathname[256+1];     //  Local copy of pathname

      msqid_ds mstat;                //  Message queue info

 

      (void) argc;

      (void) argv;

// Create the server message queue

       try {

            q.reate(OxFEEDFOOD,0600);

       } catch ( int e ) {

            errno=e;

            perror("Creating a queue");

       }

// Obtain queue information

        try {

               q.stat(mstat);

        } catch ( int e ) {

              errno = e;

              perror("q.stat()");

        }

        printf("Queue permissions were: %@4o\n",mstat.msg_perm.mode);

// For demonstration purposes,

// make the queue read & writable to all

mstat.msg_perm.mode = 0666;

         try {

              q.change(mstat);

         } catch ( int e ) {

               errno = e;

               perror("q.change()");

         }

         printf("Queue permissions now : %04o\n",mstat.msg_perm.mode);

// Server message loop

do  {

//                Receive a message of type                1

       try {

             q.recv(m,msz,sizeof m,1,0);

       } catch ( int e ) {

              errno = e;

              perror("Receiving from queue");

              return 1;

        }

//   Process message

        switch ( (int) m.request ) {

        case StatMsg::stat :                  // stat(2) request

             strncpy(pathname,m.u.path,sizeof pathname);

             pathname[sizeof pathname-1] = 0;

             m.error = stat(pathname,&m.u.stbuf) ? errno : 0;

             break;

        case StatMsg::lstat :                 // lstat(2) request

             strncpy(pathname,m.u.path,sizeof pathname);

             pathname[sizeof pathname-1] = 0;

             m.error = lstat(pathname,&m.u.stbuf) ? errno : 0;

             break;

        case StatMsg::stop :                //  stop server

             quit = 1;                             //  Stop the server

             m.error = 0;                       // Ack request

             break;

        default :                                   // Unknown request

             m.error = EINVAL;

        }

//  Reply to client

m.msgtyp = m.PID;                           //  Reply to this process

        try {

               q.send(m,sizeof m);

        } catch ( int e ) {

               errno = e;

               perror("q.send()");

               return 1;

        }

} while ( !quit );

//  Destroy the message queue

q.destroy();

return 0;

//  End statsrv.cc

 

statcln.cc-The Source Listing for the statcln Client Program

//  statcln.cc

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include "msq.h"

#include "statmsg.h"

int

main(int argc,char **argv) {

        int x;

         Msq q;           //  Message queue object

         StatMsg m;    //  Message buffer

         size_t msz;     //  Message size

         char *pathname;   //  Pathname to query

         (void) argc;

         (void) argv;

//  Access the queue

         try {

                q.access(OxFEEDFOOD);

         } catch ( int e ) {

                errno = e;

                perror("Accessing statsrv queue");

         }

//  Issue server requests for each command line

//  argument. If the argument starts with                then

//  request a lstat(2) instead of stat(2)

         for ( x=1; x<argc; ++x ) {

//  Form the server request

               if ( 1strcasecmp(argv[x],"STOP") )

                      // STOP SERVER REQUEST :

                    m.request = StatMsg::stop;

               else {

                     // STAT(2) or LSTAT(2) REQUEST

                    if ( argv[x][0] == '$' ) {

                       m.request = StatMsg::lstat;

                        pathname = argv[x] + 1;       //  Skip

                   else {

                        m.request = StatMsg::stat;

                        pathname = argv[x];             //   Pathname

                   }

                    strncpy(m.u.path,pathname,sizeof m.u.path);

               }

//            Initialize other message components

               m.error = 0;                  //  Clear

               m.PID = getpid();         //  Our process ID

               m,msgtyp = 1;              //   Send to the server

//   Send the request to the server

         try {

              q.send(m,sizeof m);         //  Send the message

         } catch ( int e ) {

              errno = e;

              perror("s.send()");

              return 1;                           //  Bail out

         }

//   If the request is to stop, then  exit loop

         if ( m.request == StatMsg::stop )

              break;                              //  There will be no reply

//   Wait for the response

         try {

                 q.recv(m,msz,sizeof m,getpid(),0);

         } catch ( int e ) {

                 errno = e;

                 perror("Receiving from queue");

                 return 1;

         }

//   Report response

          printf("RESPONSE %14s : ",pathname);

          if ( m.error != 0 )

               printf(" ERROR: %s\n",strerror(m.error));

          else

               printf(" SIZE: %ld bytes\n",(long)m.u.stbuf.st_size);

    }         

//   Exit client program

q.dispose();                               //  Reset object

return 0;

}

//   End statcln.cc

 

   To make the files do the following:                            

                            $ make

                            CC -c -Wall -fhandle -exceptions msqveri.cc

                            CC -c -Wall -fhandle -exceptions msqcr.cc

                            CC -c -Wall -fhandle -exceptions msqac.cc

                            CC -c -Wall -fhandle -exceptions msqdest.cc

                            CC -c -Wall -fhandle -exceptions msqstat.cc

                           CC -c -Wall -fhandle -exceptions msqchg.cc

                           CC -c -Wall -fhandle -exceptions msqsend.cc

                           CC -c -Wall -fhandle -exceptions msqrecv.cc

                           ar r libmsq.a msqveri.o msqcr.o msqac.o msqdest.o msqstat.o msqchg.o msqsend.o msqrecv.o

                           CC -c -Wall -fhandle -exceptions statserv.cc

                           CC -o statsrv statsrv.o -L. -lmsq -lstdc++

                           CC -c -Wall -fhandle -exceptions statcln.cc

                           CC -o statcln statcln.o -L. -lmsq -lstdc++

                           $

   Once the executibles are prepared, start up the server program as follows:

                           $ ./statsrv &

                           $ Queue permissions were: 0600

                           Queue permissions now: 0666

   The misplaced $ character is due to the shell issuing a prompt to the user before the server program wrote its output to the terminal. The server displays before (0600) and after (0666) set of permission bits.

   With the server ready for requests, you can now issue requests on the ./statcln command line:

                           $ ./statcln & /etc/hosts STOP

                           RESPONSE  /etc/hosts   :       SIZE:    279 bytes

                           [1] 12543 Exit 0

   In this example, the first argument /etc/hosts requested the system library call stat() of the hosts file from the server. The response from the server showed that the file's size was 279 bytes. This can be verified by the ls -l /etc/hosts command. The STOP argument caused the program ./statcln to request the server shut down, which it did.

   Shared Memory

   When multiple processes cooperate, they often need to share tables of data. UNIX provides this in the form shared memory facility. Figure 4 shows how one shared memory region can be shared by three processes. Although the concept of sharing memory is a simple one, a number of complications can occur. For example in Figure 4 the shared region may be attached to each process' memory space at a different memory address. This means that if memory addresses are used within the shared table, they will not be usable in all processes. Memory offsets must be used instead. This is the reason that shared libraries must be compiled to use position-independent code. Another complication is the problem of synchronization between three processes. If multiple processes are changing areas of shared memory region, how can a given process know that a particular component of data is complete? Even the process of replacing an integer value is not atomic on many CPU platforms. Thus for synchronization it is better to use semaphores. Program listing 2 gives a program that illustrates the use of shared memory. It is in a modular form and is designed to allow shell programs like csh or bash to share global variables using shared memory. This is the key in MPI languages. Here global variables like MPI_INT or MPI_LONG etc are constantly needed. Note that this differs from exported shell environment variables which cannot be altered by child processes. In this case any process using this program can inquire or alter the value of a global variable.

Figure 4: A memory region shared with three processes.

Program Listing 2: Source files for the glovar utility program.

globcr. c - The globvar Source Module That Calls shmget () to Create Shared Memory

/* globcr.c */

#include "globvar.h"

/* Create a new shared memory variable pool */

void

create_vars(int shm_size) {

   int z;                                  /* Status code */

   int semid;                           /* Semaphore IPC ID

   int offset;                           /* Byte offset */

   union semun un;                /* Union of semctl() args */

   struct shmid_ds shminfo;   /* Shared memory info */:

   /* Create shared memory region */

   z=shmget(IPC_PRIVATE,shm_size,IPC_CREAT|0600);

         if ( z == -1 ) {

             fprintf(stderr,"%s: shmmget(,%d,IPC_CREAT)\n",strerror(errno),shm_size);

             exit(l);

          }

          shmid = z;                  /* IPC ID */

   /* Create semaphore for this region */

   z=semget(IPC_PRIVATE,1,IPC_CREAT|0600);

           if (z == -1) {

              fprintf(stderr,"%s: semget(,IPC_CREAT)\n",strerror(errno));

              exit(l);

           }

           semid = z;                  /* IPC ID */

   /* Discover the actual size of the region */

   z=shmctl(shmid,IPC_STAT,&shminfo);

            if (z == -1) {

                      fprintf(stderr,"%s: shmctl(%d,IPC_STAT)\n",strerror(errno),shmid);

                       exit(l);

            }

            shm_size = shminfo.shm_segsz;    /* Actual size of the memory region  */

   /* Initialize binary semaphore to value of 1 */

             un.val = 1;

             z=semctl(semid,0,SETVAL,un.val);

             if (z == -1) {

                         fprintf(stderr,"%s: semctl(%d,0,SETVAL)\n",strerror(errno),semid);

                         exit(l);

             }

   /* Attach shared memory, and initialize it */

   attach_vars();                                  /* Attach shared memory */

   globvars->semid = semid;  /* Place semaphore ID into shared memory */

   offset = (int)                ( &globvars->vars[0] - (char *)globvars );

   globvars->size = shm_size - offset;

   globvars->vars[0] = globvars->vars[1] = 0;

}

 

globat. c - The Source Module That Calls shmat () to Attach Shared Memory

/* globat.c */

#include "globvar.h"

/* Attach the shared variable pool */

void

attach_vars(void)

/* Attach shared memory region */

globvars = (GlobVars *)shmat(shmid,0,0);

        if ( (void *)(globvars) == (void *)(-l)) {

          fprintf(stderr,"%s: shmat(%d,0,0)\n",strerror(errno),shmid);

          exit(l);

         }

}

 

globdest. c - The Source Module That Calls shmdt () and Destroys the Shared Memory

/* globdest.c */

#include "globvar.h"

/* Destroy the shared memory variable pool */

void

destroy_vars(void) {

       int z;                     /* Status code */

       int semid;             /* Semaphore IPC ID */

       union semun un;  /* Union of semctl() args */

        /* Lock the shared memory region */:

       glob_lock();

       semid = globvars->semid;    /* Semaphore IPC ID */

       /* Destroy locking semaphore */

       z=semctl(semid,0,IPG_RMID,un);

       if (z == -1) {

            fprintf(stderr,"%s: semctl(%d,O,IPC_RMID)\n",strerror(errno),semid);

            exit(l);

       }

       /* Detach shared memory */

       z=shmdt(globvars);

       if (z == -1) {

              fprintf(stderr,"%s: shmdt(2)\n",strerror(errno));

              exit(l);

        }

       /* Destroy shared memory */

      z=shmct1(shmid,IPC_RMID,NULL);

      if (z == -1) {

         fprintf(stderr,"%s: shmct1(%d,IPC_RMID)\n",strerror(errno),shmid);

      exit(l);

      }

}

 

globvar. h - The Global globvar Utility Definitions

/* globvar.h */

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

#define GLOBVARENV "GLOBVAR" /* Environment variable*/

typedef struct {

    int semid;               /* Semaphore's IPC ID */

    int size;                  /* Size of the vars[] array */

    char vars[l];           /* Start of variable storage */

} GlobVars;

extern     int shmid;    /* IPC ID of shared memory region */

extern     int shm_size;   /* Size of shared memory region */

extern                GlobVars *globvars; /* Shared memory region */

extern     int semid; /* IPC ID of the locking semaphore set */

extern void create_vars(int shm_size);

extern void attach_vars(void);

extern char *get_var(const char *varname);

extern void set_var(const char *varname,const char *value);

extern void destroy_vars(void);

extern void glob_lock(void);

extern void glob-unlock(void);

extern void unset_var(const char *varname);

#ifndef HAVE_SEMUN     /* Does sys/sem.h define this? */

union semun {

          int   val;                                 /* Value */

          struct   semid_ds *buf;         /* IPC_STAT info */

          u_short  *array;                    /* Array of values */

};

#endif

/* End globvar.h */

 

globlk. c - The Semaphore Locking Routines

/* globlk.c */

#include "globvar.h"

static struct sembuf wait { 0, -1, SEM_UNDO };

static struct sembuf notify { 0, +1, SEM_UNDO };

/* Perform a semaphore operation */

static void

do_semop(struct sembuf *op) {

    int z;         /* status code  */

    do {

        z = semop(globvars->semid,op,l);

    } while (z == -1 && errno == EINTR);

    if ( z ) {

             fprintf(stderr,"%s: semop(2)\n",strerror(errno));

             exit(l);

    }

}

/* Wait on semaphore to lock shared memory */

void

glob_lock(void) {

    do_semop(&wait);

}

/* Notify semaphore to unlock shared memory */

void

glob_unlock(void) {

   do_semop(&notify);

}

 

globget. c - The Source Module That Looks Up a Global Variable in Shared Memory

/* globget.c */

#include "globvar.h"

/* Return the string pointer for a variable's value */

char *

get_var(const char *varname) {

    char *cp;          /* Scanning pointer */

    int nlen = strlen(varname);     /* Length of variable name */

    for ( cp = &g1obvars->vars[0]; *cp; cp += strlen(cp) + 1 )

     if ( !strncmp(varname,cp,nlen) && cp[nlen] == '=' )

          return cp + n1en + 1;  /* Pointer to it's value */

    return NULL;   /* Variable not found */

}

 

globvar.c - The Main Program for the globvar Utility

/* globvar.c */

#include "globvar.h"

int shmid = -1;  /* IPC ID of shared memory */

GlobVars *globvars = NULL;    /* Shared memory region */

/* Usage instructions */

static void

usage(void) {

     puts("globvar [-i] [-s size] [-e] [-ul [-r] [-c]" " var ... var=value...");

     puts("Options:");

     puts("                -i                Initialize new globvar pool");

     puts("                -s size                Size of this pool, in bytes");

     puts("                -e                Dump all values (after changes)");

     puts("                -u                Unset all named variables");

     puts("                -r                Remove this pool of values");

     puts("                -c                Clear all variables");

     puts("                -h                This info.");

/* Main program */

int

main(int arge,char **argv) {

int rc = 0;                   /* Return code */

int optch;                    /* Option character */

int cmdopt_i = 0;             /* -i to create var pool */

int cmdopt_c = 0;             /* -c to clear variables */

int cmdopt_r = 0;             /* -r to remove pool */

int cmdopt_e = 0;             /* -D to dump the variables */

int cmdopt_u = 0;             /* -u to unset named variables */

int cmdopt_h = 0;             /* -h usage option */

int cmdopt_s = 4096;          /* Default for -s */

char *cp, *ep;                 /* Character pointers */

unsigned long  ul;              /* Converted ulong */

const char cmdopts[]     "hirs:ecu";

/* Parse command line options  */

while ( (optch = getopt(argc,argv,cmdopts)) != -1 )

switch ( optch )  {

                case 'c' :                /* -c to clear variables */

                    cmdopt_c = 1;

                    break;

                case 'i' :                /* -i initialize a new pool */

                  cmdopt_i = 1;

                    break;

                case 'e' : /* -e to dump all variables like env */

                    cmdopt_e = 1;

                     break;

                case 'r' :                /* -r to remove the pool */

                     cmdopt_r = 1;

                     break;

                case 's' :                /* -s size; affects -i */

                    ul = strtoul(optarg,&ep,0);

                      if ( *ep ) {

                             fprintf(stderr,"Bad size: -s %s\n",optarg);

                             rc = 1;

                      } else {

                                cmdopt_s = (int) ul;

                       break;

                case 'u' :   /* -u to unset all listed variables */

                       cmdopt_u = 1;

                       break;

                case 'h' :   /* -h to request help */

                       cmdopt_h = 1;

                        break;

                default :

                        rc = 1;

                }

                /* Give usage display if errors or -h : */

                if ( cmdopt_h | | rc  ) {

                     usage();

                      if ( rc )

                         return rc,

              }

                /* Create/Access global variable pool */

                if ( cmdopt_i ) {         

                    /* Create a new shared memory variable pool */

                    create_vars(cmdopt_s);

                    printf("%d\n",shmid);

               } else if ( (cp = getenv(GLOBVARENV)) != NULL ) {

                       /* Extract IPC key from GLOBVAR environment variable */

                       ul = strtoul(cp,&ep,0);

                       if (*ep ) {

                                fprintf(stderr,"%s has bad IPC key\n",cp);

                                return 1;

       &nbs