Answers/Solutions to Exercises in Chapter 11, Exercise 1

E1: A shared memory segment cannot be extended. Write a C program that has as an argument an attached shared memory segment, and some data. It will create and attach a new and bigger shared memory segment. It will copy the data from the old shared memory segment to the new one and will also store there the additional data. It will then detach from the old memory segment and destroy it. It essentially does the same as realloc() when it cannot physically extend a memory segment. Though, there are some synchronization problems realloc() does not have to deal with. First, it must "lock" the segment for the duration so no other process can use it. Then it has to make sure that all other processes know that the segment "has moved" and `"where". Try to solve
this using semaphores and shared memory segments only.

S1: A sample (UNIX) program in C is below. The program creates two share memory segments, shm1 and shm2. shm1 is used to hold two pieces of data: the sequential number of the last log entry (logi) and so it is available to all processes and as a result the entries in the log are properly numbered, no matter which process creates it. The other piece of data is the id of shm2. Since shm2 will be destroyed and created many times, the id changes. In this way, every process will know the current correct id of shm2 to be able to access it. The access to shm1 and shm2 is "guarded" by a semaphore. The logging functions lock the log for the duration of writing the entry, so the entries are complete.
shm2 contains a number representing the number of "registered" processes. Each process "adds" its pid to the list in shm2. So each process must extend (see SHMRealloc() function in the code) shm2. Function SHMShow() is used to display the contents of shm2. The program forks (spawns) three child processes.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include "log.h"

char logfile[300];
int logindex = 0;     
int* logi = &logindex;

// The structure of shm1
struct SHM1_STRUCT
{
  int shmId2;  // id of  shm2
  int logi;    // for sequential numbers in log messages
};
typedef struct SHM1_STRUCT SHM1;

// The structure of shm2
// int pid pid pid .... pid
// int=number of registered processes
// pid pid ....  pid their pid's
  
int shmId1 = -1; // id of shm1
int shmId2 = -1; // id of shm2
SHM1 *shmAddr1;  // address of attached shm1
int *shmAddr2;   // address of attached shm2
int semId = -1;  // id of semaphore
pid_t pids[3]={-1,-1,-1}; // array of pid's of child processes
int nchildren=0;          // no of live child processes
int parent=1;             // identification of the parent process

// create and lock a semaphore array of size 1, i.e. a single
// semaphore. We will use it in "binary" mode
// This function is only used by the parent process
// function CreateAndLockSem ---------------------------------------- 
int CreateAndLockSem()
{
  int size = 1;
  int semid = -1;
  union semun {
   int             val;
   struct semid_ds *buf;
   ushort_t        *array;
  } semarg ;
  int i, xerrno;
 
  // create a semaphore array of size 'size', i.e. 1 
  if ((semid = semget(IPC_PRIVATE,size,IPC_CREAT|S_IRUSR|S_IWUSR)) == -1)
    Sys_exit("semget error errno=%d\n",errno);
  else
    Msg("created semaphore semid=%d\n",semid);
  
  // set the semaphore to its initial value 0 meaning "locked"
  semarg.val = 0;
  if (semctl(semid,0,SETVAL,semarg) == -1) {
    xerrno = errno;  // remember errno 
    semctl(semid,0,IPC_RMID,semarg);    // remove semaphore
    semid=-1;
    errno = xerrno;  // recall remembered errno 
    Sys_exit("semctl(SETVAL) error\n");
  }else
    Msg("locked semaphore semid=%d\n",semid);
  
  return semid;
}// end CreateAndLockSem


// lock semaphore of the given id. This function will be used by both,
// the parent and the child processes. If it is a child process, it must
// exit using _exit() rather than exit().
// function LockSem ------------------------------------------ 
void LockSem(int semid)
{
  union semun {
   int             val;
   struct semid_ds *buf;
   ushort_t        *array;
  } semarg ;
  struct sembuf lockSEM[1] = {{0,-1,SEM_UNDO}};
  int i;

  /* wait in suspension until either successfully locked or woken up by 
     a signal,
     when locked, the value is decreased by 1 to 0 */

  while((semid=semop(semid,lockSEM,1)) == -1) {
   if (errno == EINTR)                // signal interrupt
     continue;
   else{
     if (parent)
       Sys_exit("semop(lockSEM) error\n");
     else
       Sys__exit("semop(lockSEM) error\n");
   }
  }//endwhile
  Msg("locked semaphore semid=%d\n",semid);
}// end LockSem



// unlock semaphore of the given id. This function will be used by both,
// the parent and the child processes. If it is a child process, it must
// exit using _exit() rather than exit().
// function UnlockSem ---------------------------------------------- 
void UnlockSem(int semid)
{
  union semun {
   int             val;
   struct semid_ds *buf;
   ushort_t        *array;
  } semarg ;
  int i;
  struct sembuf unlockSEM[1] = {{0,1,SEM_UNDO}};

  // unlock semaphor, when unlocked, the value is increased by 1 to 1 
  while(semop(semid,unlockSEM,1) == -1) {
   if (errno == EINTR)                // signal interrupt
     continue;
   else{
     if (parent)
       Sys_exit("semop(unlocksSEM) error\n");
     else
       Sys__exit("semop(unlocksSEM) error\n");
   }
  }//endwhile
  Msg("Unlocked semaphore semid=%d\n",semid);
}// end UnlockSem


// this function is used only by the parent process. However, it is used
// in exit(), so we cannot use exit() here. But we do not need to.
// function RemoveSem ------------------------------------- 
void RemoveSem(int semid)
{
  union semun {
   int             val;
   struct semid_ds *buf;
   ushort_t        *array;
  } semarg ;

  // remove semaphore 
  if (semctl(semid,0,IPC_RMID,semarg) == -1)
    Sys("semctl(IPC_RMID) error\n");
  else
    Msg("Removed semaphore semid=%d\n",semid);
}// end RemoveSem 


// only used by the parent and only during exit()
// function FinalAtexit ------------------------------------------------ 
void FinalAtexit()
{
    Msg("the program is finished\n");
}// end FinalAtexit 


// only used by the parent, and only during exit()
// function RemoveShmAtexit ------------------------------------------------ 
void RemoveShmAtexit()
{
  struct shmid_ds shmbuf;

  if (shmId1 >= 0) {  // check if installed and attached
    Msg("going to remove shared memory with id = %d\n",shmId1);
    logindex = *logi;
    logi = &logindex;
    if (shmctl(shmId1,IPC_RMID,&shmbuf) < 0)
      Sys("shmctl(%d,IPC_RMID) error\n",shmId1);
    else
      Msg("removed shared memory with id = %d \n",shmId1);
  }
  if (shmId2 >= 0) {  // check if installed and attached
    Msg("going to remove shared memory id = %d\n",shmId2);
    if (shmctl(shmId2,IPC_RMID,&shmbuf) < 0)
      Sys("shmctl(%d,IPC_RMID) error\n",shmId2);
    else
      Msg("removed shared memory with id = %d \n",shmId2);
  }
}// end RemoveShmAtexit 


// only used by the parent, and only during exit()
// function RemoveSemAtexit -------------------------------------
void RemoveSemAtexit()
{

if (semId == -1)   // semaphore not installed 
  return;

RemoveSem(semId);
semId = -1;

}// end RemoveSempahoreAtexit 



// only used by the parent, and only during exit(). Just a clean up,
// we do not want to have orphaned processes running.
// function TerminateChildrenAtexit ----------------------------------
void TernminateChildrenAtexit()
{
 int i;
 for(i = 0; i < 3; i++) {
  if (pids[i]!=-1) {
    sigsend(P_PID,pids[i],SIGKILL);
    Msg("terminated child process pid=%d in TerminateChildrenAtexit\n",
        pids[i]);
    pids[i]=-1;
  }
 }
}// end TerminateChildrenAtexit




// assuming shm2 is locked for us and we have correct shmId2
// and that we are attached to shm2
// only used by the child processes, so no exit()
// function SHMRealloc -----------------------------------------
void SHMRealloc(pid_t pid)
{

int npids, size, i;
struct shmid_ds shmbuf;
int shmId2_old;
int* shmAddr2_old;
pid_t *p1, *p2;

shmId2_old=shmId2;
shmAddr2_old=shmAddr2;

// calc mew size 
npids = *shmAddr2;
size=(npids+1)*sizeof(pid_t)+sizeof(int);

//create new shm2
if ((shmId2 = shmget(IPC_PRIVATE,size,S_IRUSR|S_IWUSR)) < 0)
  Sys__exit("shmget error\n");
else
  Msg("created new shm2 with id %d of size %d\n",shmId2,size);
  
// attach new shm2
if ((shmAddr2 = (int*) shmat(shmId2,0,0)) == (void*) -1)
  Sys__exit("shmat error (%x)\n",shmAddr2);
else 
  Msg("attached new shm2 with id %d at address %x\n",shmId2,shmAddr2);

// put new data in shm1
shmAddr1->shmId2=shmId2;

// copy old shm2 to new shm2
*shmAddr2=(npids+1);               // new number of pids
p1 = (pid_t*) (shmAddr2_old+1);    // get ready for pid in old shm2
p2 = (pid_t*) (shmAddr2+1);        // get ready for pid in new shm2
for(i=0; i < npids; i++) {
 *p2++=*p1++;                      // copy pid from old shm2 to new shm2
}//endfor

// add new pid
*p2 = pid;
 
// remove old shm2
if (shmctl(shmId2_old,IPC_RMID,&shmbuf) < 0)
  Sys__exit("shmctl(%d,IPC_RMID) error\n",shmId2_old);
else
  Msg("removed shared memory with id = %d \n",shmId2_old);

}// end SHMRealloc



// used by both, the child processes and the parent
// assuming attached to shm2
// function SHMShow ------------------------------------------------
void SHMShow()
{
 int i, npids;
 pid_t *q;
 char c=':';
 static char line[300];  // for simplicity, we are cheating here, knowing 
                         // that the print line will not exceed 300 chars

 sprintf(line,"shm2 content ");
 npids = *shmAddr2;
 sprintf(line+strlen(line),"%d",npids);
 q = (pid_t*)(shmAddr2+1);
 for(i = 0; i < npids; i++) {
  sprintf(line+strlen(line),"%c%d",c,*q++);
  c=',';
 }
 Msg("%s\n",line);
}// end SHMShow
 
 

// do the child process stuff, so no exit()
// function dochild  -----------------------------------------------
void dochild()
{
struct shmid_ds shmbuf;

// you inherited across fork meaningful semId, shmId1, shmAddr1
// shmId2, shmAddr2 may have changed
LockSem(semId);   // wait till you lock semaphore

// get shmId2 from shm1
shmId2=shmAddr1->shmId2;

// attach shm2
if ((shmAddr2 = (int*) shmat(shmId2,0,0)) == (void*) -1)
  Sys__exit("shmat error (%x)\n",shmAddr2);
else 
  Msg("attached shared memory segment with id %d at address %x\n",
       shmId2,shmAddr2);

// Show shm2
SHMShow();

// now add your pid to shm2
SHMRealloc(getpid());

// Show shm2
SHMShow();
 
// detach from shm2
if (shmdt((char*)shmAddr2)< 0)
  Sys__exit("shmdt() error\n");
else
  Msg("detached from shm2\n");

 Msg__exit("child process pid=%d terminates\n",getpid());
}//end dochild



// function main ----------------------------------------------------
int main(int argc,char* argv[])
{
  size_t size;
  pid_t* ptr;
  char* s;
  int i, status;
  pid_t pid;

  if (argc!=2) {
    fprintf(stderr,"usage -- %s <logfile>\n",argv[0]);
    exit(1);
  }
  
  logi = &logindex;
  if ((s = create_log(argv[1])) != NULL) {
    fprintf(stderr,"create log error: %s\n",s);
    exit(1);
  }

  // on exit announce the end of program
  if (atexit(FinalAtexit) < 0) 
    Sys_exit("can't register atexit FinalAtexit()\n");
  else
    Msg("registered atexit FinalAtexit()\n");
  
  
  // on exit remove shm1 and shm2 
  if (atexit(RemoveShmAtexit) < 0) 
    Sys_exit("can't register atexit RemoveShmAtexit()\n");
  else
    Msg("registered atexit RemoveShmAtexit()\n");
  
  // on exit remove the semaphore
  if (atexit(RemoveSemAtexit) < 0) 
    Sys_exit("can't register atexit RemoveSemAtexit()\n");
  else
    Msg("registered atexit RemoveSemAtexit()\n");

  // now we can start the actual processing
  
  // create shm1
  if ((shmId1 = shmget(IPC_PRIVATE,sizeof(SHM1),S_IRUSR|S_IWUSR)) < 0)
    Sys_exit("shmget error for shm1\n");
  else
    Msg("created shm1 with id %d of size %d\n",shmId1,sizeof(SHM1));
  
  // attach shm1
  if ((shmAddr1 = (SHM1*) shmat(shmId1,0,0)) == (void*) -1)
    Sys_exit("shmat error for shm1 (%x)\n",shmAddr1);
  else
    Msg("attached shm1 with id %d at address %x\n",shmId1,shmAddr1);
  
  //create shm2
  size=sizeof(int)+sizeof(pid_t)+1;
  if ((shmId2 = shmget(IPC_PRIVATE,size,S_IRUSR|S_IWUSR)) < 0)
    Sys_exit("shmget error for shm2\n");
  else
    Msg("created shm2 with id %d of size %d\n",shmId2,size);
  
  // attach shm2
  if ((shmAddr2 = (int*) shmat(shmId2,0,0)) == (void*) -1)
    Sys_exit("shmat error for shm2 (%x)\n",shmAddr2);
  else 
    Msg("attached shm2 with id %d at address %x\n",shmId2,shmAddr2);
  
  // put data in shm1
 
  shmAddr1->shmId2=shmId2;
  shmAddr1->logi=*logi;
  logi = &shmAddr1->logi;
  
  // put data in shm2
  *shmAddr2=1;                       // 1 registered process
  ptr=(pid_t*)(shmAddr2+1);
  *ptr=getpid();                     // and it is me

  // create and lock the semaphore protecting shm1
  semId=CreateAndLockSem();

  // show contents of shm2
  SHMShow();

  // detach from shm2
  if (shmdt((char*)shmAddr2)< 0)
    Sys_exit("shmdt() error\n");
  else
    Msg("detached from shm2\n");

  // and unlock the semaphore
  UnlockSem(semId);

  // now fork three process
  for(i=0; i < 3; i++) {
   if ((pids[i]=fork()) < 0) {
     Sys_exit("fork error\n");
   }else if (pids[i]==0) {
     Msg("I am a child process pid=%d\n",getpid());
     parent=0;  // remember, that you are not parent
     dochild(); // do child stuff, does not return, but exits using _exit()
   }else{
     Msg("I, parent, forked process pid=%d\n",pids[i]);
     nchildren++; // update the number of live child processes
   }
  }//endfor

 // now wait for all child processes to terminate
 while(nchildren > 0) {
   if ((pid = wait(&status)) == (pid_t) -1)
     if (errno == EINTR)      // interrupted system call 
       continue;
     else
       Sys_exit("waitpid error\n");
   Msg("received notification of child process %d terminated\n",pid);
   // we got a child terminating
   nchildren--;  // update child counter
   for(i=0; i < 3; i++) {  // find which one it was
    if (pids[i]==pid) {
      pids[i]=-1;
      break;
    }
   }//endfor
   // let'us see shm2 now
   LockSem(semId);
   shmId2=shmAddr1->shmId2;
   if ((shmAddr2 = (int*) shmat(shmId2,0,0)) == (void*) -1)
     Sys_exit("shmat error (%x)\n",shmAddr2);
   else 
     Msg("attached shared memory segment with id %d at address %x\n",
          shmId2,shmAddr2);
   SHMShow();
  
   // detach from shm2
   if (shmdt((char*)shmAddr2)< 0)
     Sys_exit("shmdt() error\n");
   else
     Msg("detached from shm2\n");
   UnlockSem(semId); // and unlock semaphore
 }// endwhile

  exit(1);  // will do cleanup
}// end main 

The output in the log:

message number = 0, process id = 25699, time and date = 22:44:35 05/07/04
 registered atexit FinalAtexit()

message number = 1, process id = 25699, time and date = 22:44:35 05/07/04
 registered atexit RemoveShmAtexit()

message number = 2, process id = 25699, time and date = 22:44:35 05/07/04
 registered atexit RemoveSemAtexit()

message number = 3, process id = 25699, time and date = 22:44:35 05/07/04
 created shm1 with id 3901 of size 8

message number = 4, process id = 25699, time and date = 22:44:35 05/07/04
 attached shm1 with id 3901 at address ff2e0000

message number = 5, process id = 25699, time and date = 22:44:35 05/07/04
 created shm2 with id 2102 of size 9

message number = 6, process id = 25699, time and date = 22:44:35 05/07/04
 attached shm2 with id 2102 at address ff1f0000

message number = 7, process id = 25699, time and date = 22:44:35 05/07/04
 created semaphore semid=1638400

message number = 8, process id = 25699, time and date = 22:44:35 05/07/04
 locked semaphore semid=1638400

message number = 9, process id = 25699, time and date = 22:44:35 05/07/04
 shm2 content 1:25699

message number = 10, process id = 25699, time and date = 22:44:35 05/07/04
 detached from shm2

message number = 11, process id = 25699, time and date = 22:44:35 05/07/04
 Unlocked semaphore semid=1638400

message number = 12, process id = 25699, time and date = 22:44:35 05/07/04
 I, parent, forked process pid=25700

message number = 13, process id = 25700, time and date = 22:44:35 05/07/04
 I am a child process pid=25700

message number = 14, process id = 25699, time and date = 22:44:35 05/07/04
 I, parent, forked process pid=25701

message number = 15, process id = 25701, time and date = 22:44:35 05/07/04
 I am a child process pid=25701

message number = 16, process id = 25700, time and date = 22:44:35 05/07/04
 locked semaphore semid=0

message number = 17, process id = 25702, time and date = 22:44:35 05/07/04
 I am a child process pid=25702

message number = 18, process id = 25700, time and date = 22:44:35 05/07/04
 attached shared memory segment with id 2102 at address ff1f0000

message number = 19, process id = 25699, time and date = 22:44:35 05/07/04
 I, parent, forked process pid=25702

message number = 20, process id = 25700, time and date = 22:44:35 05/07/04
 shm2 content 1:25699

message number = 21, process id = 25700, time and date = 22:44:35 05/07/04
 created new shm2 with id 303 of size 12

message number = 22, process id = 25700, time and date = 22:44:35 05/07/04
 attached new shm2 with id 303 at address ff1e0000

message number = 23, process id = 25700, time and date = 22:44:35 05/07/04
 removed shared memory with id = 2102 

message number = 24, process id = 25700, time and date = 22:44:35 05/07/04
 shm2 content 2:25699,25700

message number = 25, process id = 25700, time and date = 22:44:35 05/07/04
 detached from shm2

message number = 26, process id = 25700, time and date = 22:44:35 05/07/04
 child process pid=25700 terminates

message number = 27, process id = 25701, time and date = 22:44:35 05/07/04
 locked semaphore semid=0

message number = 28, process id = 25701, time and date = 22:44:35 05/07/04
 attached shared memory segment with id 303 at address ff1f0000

message number = 29, process id = 25699, time and date = 22:44:35 05/07/04
 received notification of child process 25700 terminated

message number = 30, process id = 25701, time and date = 22:44:35 05/07/04
 shm2 content 2:25699,25700

message number = 31, process id = 25701, time and date = 22:44:35 05/07/04
 created new shm2 with id 2202 of size 16

message number = 32, process id = 25701, time and date = 22:44:35 05/07/04
 attached new shm2 with id 2202 at address ff1e0000

message number = 33, process id = 25701, time and date = 22:44:35 05/07/04
 removed shared memory with id = 303 

message number = 34, process id = 25701, time and date = 22:44:35 05/07/04
 shm2 content 3:25699,25700,25701

message number = 35, process id = 25701, time and date = 22:44:35 05/07/04
 detached from shm2

message number = 36, process id = 25701, time and date = 22:44:35 05/07/04
 child process pid=25701 terminates

message number = 37, process id = 25702, time and date = 22:44:35 05/07/04
 locked semaphore semid=0

message number = 38, process id = 25702, time and date = 22:44:35 05/07/04
 attached shared memory segment with id 2202 at address ff1f0000

message number = 39, process id = 25702, time and date = 22:44:35 05/07/04
 shm2 content 3:25699,25700,25701

message number = 40, process id = 25702, time and date = 22:44:35 05/07/04
 created new shm2 with id 403 of size 20

message number = 41, process id = 25702, time and date = 22:44:35 05/07/04
 attached new shm2 with id 403 at address ff1e0000

message number = 42, process id = 25702, time and date = 22:44:35 05/07/04
 removed shared memory with id = 2202 

message number = 43, process id = 25702, time and date = 22:44:35 05/07/04
 shm2 content 4:25699,25700,25701,25702

message number = 44, process id = 25702, time and date = 22:44:35 05/07/04
 detached from shm2

message number = 45, process id = 25702, time and date = 22:44:35 05/07/04
 child process pid=25702 terminates

message number = 46, process id = 25699, time and date = 22:44:35 05/07/04
 locked semaphore semid=0

message number = 47, process id = 25699, time and date = 22:44:35 05/07/04
 attached shared memory segment with id 403 at address ff1f0000

message number = 48, process id = 25699, time and date = 22:44:35 05/07/04
 shm2 content 4:25699,25700,25701,25702

message number = 49, process id = 25699, time and date = 22:44:35 05/07/04
 detached from shm2

message number = 50, process id = 25699, time and date = 22:44:35 05/07/04
 Unlocked semaphore semid=1638400

message number = 51, process id = 25699, time and date = 22:44:35 05/07/04
 received notification of child process 25702 terminated

message number = 52, process id = 25699, time and date = 22:44:35 05/07/04
 locked semaphore semid=0

message number = 53, process id = 25699, time and date = 22:44:35 05/07/04
 attached shared memory segment with id 403 at address ff1f0000

message number = 54, process id = 25699, time and date = 22:44:35 05/07/04
 shm2 content 4:25699,25700,25701,25702

message number = 55, process id = 25699, time and date = 22:44:35 05/07/04
 detached from shm2

message number = 56, process id = 25699, time and date = 22:44:35 05/07/04
 Unlocked semaphore semid=1638400

message number = 57, process id = 25699, time and date = 22:44:35 05/07/04
 received notification of child process 25701 terminated

message number = 58, process id = 25699, time and date = 22:44:35 05/07/04
 locked semaphore semid=0

message number = 59, process id = 25699, time and date = 22:44:35 05/07/04
 attached shared memory segment with id 403 at address ff1f0000

message number = 60, process id = 25699, time and date = 22:44:35 05/07/04
 shm2 content 4:25699,25700,25701,25702

message number = 61, process id = 25699, time and date = 22:44:35 05/07/04
 detached from shm2

message number = 62, process id = 25699, time and date = 22:44:35 05/07/04
 Unlocked semaphore semid=1638400

message number = 63, process id = 25699, time and date = 22:44:35 05/07/04
 Removed semaphore semid=1638400

message number = 64, process id = 25699, time and date = 22:44:35 05/07/04
 going to remove shared memory with id = 3901

message number = 65, process id = 25699, time and date = 22:44:35 05/07/04
 removed shared memory with id = 3901 

message number = 66, process id = 25699, time and date = 22:44:35 05/07/04
 going to remove shared memory id = 403

message number = 67, process id = 25699, time and date = 22:44:35 05/07/04
 removed shared memory with id = 403 

message number = 68, process id = 25699, time and date = 22:44:35 05/07/04
 the program is finished

Back to Answers/Solutions Index                          Back to Answers/Solutions for Chapter 11 Index