Answers/Solutions to Exercises in Chapter 11, Exercise 4

E4: Repeat exercise 2 with a change that each child process will open its own and different log after the fork(). Compare the logs.

S4: The same program as in Exercise 2. Note that right after the fork, we create a new log. So each process has its own separate log. Compare the entries.

#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, contains shmId2

// 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
int *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);
    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;

// 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;

// 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);
  }
  
  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(int),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(int));
  
  // attach shm1
  if ((shmAddr1 = (int*) 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;
  
  // 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) {
     parent=0;  // remember, that you are not parent
     // create your own log
     sprintf(logfile+strlen(logfile),"_%d",i);
     create_log(logfile);
     Msg("I am a child process pid=%d\n",getpid());
     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;
   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

Outuput in 4 different logs.
log of the parent process:

message number = 0, process id = 28127, time and date = 23:17:36 05/07/04
 registered atexit FinalAtexit()

message number = 1, process id = 28127, time and date = 23:17:36 05/07/04
 registered atexit RemoveShmAtexit()

message number = 2, process id = 28127, time and date = 23:17:36 05/07/04
 registered atexit RemoveSemAtexit()

message number = 3, process id = 28127, time and date = 23:17:36 05/07/04
 created shm1 with id 4001 of size 4

message number = 4, process id = 28127, time and date = 23:17:36 05/07/04
 attached shm1 with id 4001 at address ff2e0000

message number = 5, process id = 28127, time and date = 23:17:36 05/07/04
 created shm2 with id 2302 of size 9

message number = 6, process id = 28127, time and date = 23:17:36 05/07/04
 attached shm2 with id 2302 at address ff1f0000

message number = 7, process id = 28127, time and date = 23:17:36 05/07/04
 created semaphore semid=1703936

message number = 8, process id = 28127, time and date = 23:17:36 05/07/04
 locked semaphore semid=1703936

message number = 9, process id = 28127, time and date = 23:17:36 05/07/04
 shm2 content 1:28127

message number = 10, process id = 28127, time and date = 23:17:36 05/07/04
 detached from shm2

message number = 11, process id = 28127, time and date = 23:17:36 05/07/04
 Unlocked semaphore semid=1703936

message number = 12, process id = 28127, time and date = 23:17:36 05/07/04
 I, parent, forked process pid=28128

message number = 13, process id = 28127, time and date = 23:17:36 05/07/04
 I, parent, forked process pid=28129

message number = 14, process id = 28127, time and date = 23:17:36 05/07/04
 I, parent, forked process pid=28130

message number = 15, process id = 28127, time and date = 23:17:36 05/07/04
 received notification of child process 28128 terminated

message number = 16, process id = 28127, time and date = 23:17:36 05/07/04
 locked semaphore semid=0

message number = 17, process id = 28127, time and date = 23:17:36 05/07/04
 attached shared memory segment with id 603 at address ff1f0000

message number = 18, process id = 28127, time and date = 23:17:36 05/07/04
 shm2 content 4:28127,28128,28129,28130

message number = 19, process id = 28127, time and date = 23:17:36 05/07/04
 detached from shm2

message number = 20, process id = 28127, time and date = 23:17:36 05/07/04
 Unlocked semaphore semid=1703936

message number = 21, process id = 28127, time and date = 23:17:36 05/07/04
 received notification of child process 28130 terminated

message number = 22, process id = 28127, time and date = 23:17:36 05/07/04
 locked semaphore semid=0

message number = 23, process id = 28127, time and date = 23:17:36 05/07/04
 attached shared memory segment with id 603 at address ff1f0000

message number = 24, process id = 28127, time and date = 23:17:36 05/07/04
 shm2 content 4:28127,28128,28129,28130

message number = 25, process id = 28127, time and date = 23:17:36 05/07/04
 detached from shm2

message number = 26, process id = 28127, time and date = 23:17:36 05/07/04
 Unlocked semaphore semid=1703936

message number = 27, process id = 28127, time and date = 23:17:36 05/07/04
 received notification of child process 28129 terminated

message number = 28, process id = 28127, time and date = 23:17:36 05/07/04
 locked semaphore semid=0

message number = 29, process id = 28127, time and date = 23:17:36 05/07/04
 attached shared memory segment with id 603 at address ff1f0000

message number = 30, process id = 28127, time and date = 23:17:36 05/07/04
 shm2 content 4:28127,28128,28129,28130

message number = 31, process id = 28127, time and date = 23:17:36 05/07/04
 detached from shm2

message number = 32, process id = 28127, time and date = 23:17:36 05/07/04
 Unlocked semaphore semid=1703936

message number = 33, process id = 28127, time and date = 23:17:36 05/07/04
 Removed semaphore semid=1703936

message number = 34, process id = 28127, time and date = 23:17:36 05/07/04
 going to remove shared memory with id = 4001

message number = 35, process id = 28127, time and date = 23:17:36 05/07/04
 removed shared memory with id = 4001 

message number = 36, process id = 28127, time and date = 23:17:36 05/07/04
 going to remove shared memory id = 603

message number = 37, process id = 28127, time and date = 23:17:36 05/07/04
 removed shared memory with id = 603 

message number = 38, process id = 28127, time and date = 23:17:36 05/07/04
 the program is finished

log of the 1st child process:

message number = 12, process id = 28128, time and date = 23:17:36 05/07/04
 I am a child process pid=28128

message number = 13, process id = 28128, time and date = 23:17:36 05/07/04
 locked semaphore semid=0

message number = 14, process id = 28128, time and date = 23:17:36 05/07/04
 attached shared memory segment with id 2302 at address ff1f0000

message number = 15, process id = 28128, time and date = 23:17:36 05/07/04
 shm2 content 1:28127

message number = 16, process id = 28128, time and date = 23:17:36 05/07/04
 created new shm2 with id 503 of size 12

message number = 17, process id = 28128, time and date = 23:17:36 05/07/04
 attached new shm2 with id 503 at address ff1e0000

message number = 18, process id = 28128, time and date = 23:17:36 05/07/04
 removed shared memory with id = 2302 

message number = 19, process id = 28128, time and date = 23:17:36 05/07/04
 shm2 content 2:28127,28128

message number = 20, process id = 28128, time and date = 23:17:36 05/07/04
 detached from shm2

message number = 21, process id = 28128, time and date = 23:17:36 05/07/04
 child process pid=28128 terminates

log of the 2nd child process:

message number = 13, process id = 28129, time and date = 23:17:36 05/07/04
 I am a child process pid=28129

message number = 14, process id = 28129, time and date = 23:17:36 05/07/04
 locked semaphore semid=0

message number = 15, process id = 28129, time and date = 23:17:36 05/07/04
 attached shared memory segment with id 503 at address ff1f0000

message number = 16, process id = 28129, time and date = 23:17:36 05/07/04
 shm2 content 2:28127,28128

message number = 17, process id = 28129, time and date = 23:17:36 05/07/04
 created new shm2 with id 2402 of size 16

message number = 18, process id = 28129, time and date = 23:17:36 05/07/04
 attached new shm2 with id 2402 at address ff1e0000

message number = 19, process id = 28129, time and date = 23:17:36 05/07/04
 removed shared memory with id = 503 

message number = 20, process id = 28129, time and date = 23:17:36 05/07/04
 shm2 content 3:28127,28128,28129

message number = 21, process id = 28129, time and date = 23:17:36 05/07/04
 detached from shm2

message number = 22, process id = 28129, time and date = 23:17:36 05/07/04
 child process pid=28129 terminates

log of the 3rd child process:

message number = 14, process id = 28130, time and date = 23:17:36 05/07/04
 I am a child process pid=28130

message number = 15, process id = 28130, time and date = 23:17:36 05/07/04
 locked semaphore semid=0

message number = 16, process id = 28130, time and date = 23:17:36 05/07/04
 attached shared memory segment with id 2402 at address ff1f0000

message number = 17, process id = 28130, time and date = 23:17:36 05/07/04
 shm2 content 3:28127,28128,28129

message number = 18, process id = 28130, time and date = 23:17:36 05/07/04
 created new shm2 with id 603 of size 20

message number = 19, process id = 28130, time and date = 23:17:36 05/07/04
 attached new shm2 with id 603 at address ff1e0000

message number = 20, process id = 28130, time and date = 23:17:36 05/07/04
 removed shared memory with id = 2402 

message number = 21, process id = 28130, time and date = 23:17:36 05/07/04
 shm2 content 4:28127,28128,28129,28130

message number = 22, process id = 28130, time and date = 23:17:36 05/07/04
 detached from shm2

message number = 23, process id = 28130, time and date = 23:17:36 05/07/04
 child process pid=28130 terminates

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