Answers/Solutions to Exercises in Chapter 11, Exercise 2

E2: Write a simple C program that forks one or more child processes. Use the logging functions from the Appendix D. Do not create a shared memory segment for logi. Create a single log before the very first fork() so all the child processes will inherit it.
Check the log and the sequential numbers of messages from all the processes involved.

S2: We are using the sample solution from Exercise 1, we just "deleted" the references to logi from shm1. Note that some of the entries in the log have "duplicated" sequential numbers, as all the child processes use their "own" numbers, starting with the last message number from th eparent just prior to the fork of the child.

#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) {
     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;
   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

Output in the log:

message number = 0, process id = 25404, time and date = 22:40:15 05/07/04
 registered atexit FinalAtexit()

message number = 1, process id = 25404, time and date = 22:40:15 05/07/04
 registered atexit RemoveShmAtexit()

message number = 2, process id = 25404, time and date = 22:40:15 05/07/04
 registered atexit RemoveSemAtexit()

message number = 3, process id = 25404, time and date = 22:40:15 05/07/04
 created shm1 with id 3801 of size 4

message number = 4, process id = 25404, time and date = 22:40:15 05/07/04
 attached shm1 with id 3801 at address ff2e0000

message number = 5, process id = 25404, time and date = 22:40:15 05/07/04
 created shm2 with id 1902 of size 9

message number = 6, process id = 25404, time and date = 22:40:15 05/07/04
 attached shm2 with id 1902 at address ff1f0000

message number = 7, process id = 25404, time and date = 22:40:15 05/07/04
 created semaphore semid=1572864

message number = 8, process id = 25404, time and date = 22:40:15 05/07/04
 locked semaphore semid=1572864

message number = 9, process id = 25404, time and date = 22:40:15 05/07/04
 shm2 content 1:25404

message number = 10, process id = 25404, time and date = 22:40:15 05/07/04
 detached from shm2

message number = 11, process id = 25404, time and date = 22:40:15 05/07/04
 Unlocked semaphore semid=1572864

message number = 12, process id = 25404, time and date = 22:40:15 05/07/04
 I, parent, forked process pid=25405

message number = 12, process id = 25405, time and date = 22:40:15 05/07/04
 I am a child process pid=25405

message number = 13, process id = 25404, time and date = 22:40:15 05/07/04
 I, parent, forked process pid=25406

message number = 13, process id = 25406, time and date = 22:40:15 05/07/04
 I am a child process pid=25406

message number = 13, process id = 25405, time and date = 22:40:15 05/07/04
 locked semaphore semid=0

message number = 14, process id = 25404, time and date = 22:40:15 05/07/04
 I, parent, forked process pid=25407

message number = 14, process id = 25405, time and date = 22:40:15 05/07/04
 attached shared memory segment with id 1902 at address ff1f0000

message number = 14, process id = 25407, time and date = 22:40:15 05/07/04
 I am a child process pid=25407

message number = 15, process id = 25405, time and date = 22:40:15 05/07/04
 shm2 content 1:25404

message number = 16, process id = 25405, time and date = 22:40:15 05/07/04
 created new shm2 with id 103 of size 12

message number = 17, process id = 25405, time and date = 22:40:15 05/07/04
 attached new shm2 with id 103 at address ff1e0000

message number = 18, process id = 25405, time and date = 22:40:15 05/07/04
 removed shared memory with id = 1902 

message number = 19, process id = 25405, time and date = 22:40:15 05/07/04
 shm2 content 2:25404,25405

message number = 20, process id = 25405, time and date = 22:40:15 05/07/04
 detached from shm2

message number = 21, process id = 25405, time and date = 22:40:15 05/07/04
 child process pid=25405 terminates

message number = 14, process id = 25406, time and date = 22:40:15 05/07/04
 locked semaphore semid=0

message number = 15, process id = 25406, time and date = 22:40:15 05/07/04
 attached shared memory segment with id 103 at address ff1f0000

message number = 15, process id = 25404, time and date = 22:40:15 05/07/04
 received notification of child process 25405 terminated

message number = 16, process id = 25406, time and date = 22:40:15 05/07/04
 shm2 content 2:25404,25405

message number = 17, process id = 25406, time and date = 22:40:15 05/07/04
 created new shm2 with id 2002 of size 16

message number = 18, process id = 25406, time and date = 22:40:15 05/07/04
 attached new shm2 with id 2002 at address ff1e0000

message number = 19, process id = 25406, time and date = 22:40:15 05/07/04
 removed shared memory with id = 103 

message number = 20, process id = 25406, time and date = 22:40:15 05/07/04
 shm2 content 3:25404,25405,25406

message number = 21, process id = 25406, time and date = 22:40:15 05/07/04
 detached from shm2

message number = 22, process id = 25406, time and date = 22:40:15 05/07/04
 child process pid=25406 terminates

message number = 15, process id = 25407, time and date = 22:40:15 05/07/04
 locked semaphore semid=0

message number = 16, process id = 25407, time and date = 22:40:15 05/07/04
 attached shared memory segment with id 2002 at address ff1f0000

message number = 17, process id = 25407, time and date = 22:40:15 05/07/04
 shm2 content 3:25404,25405,25406

message number = 18, process id = 25407, time and date = 22:40:15 05/07/04
 created new shm2 with id 203 of size 20

message number = 19, process id = 25407, time and date = 22:40:15 05/07/04
 attached new shm2 with id 203 at address ff1e0000

message number = 20, process id = 25407, time and date = 22:40:15 05/07/04
 removed shared memory with id = 2002 

message number = 21, process id = 25407, time and date = 22:40:15 05/07/04
 shm2 content 4:25404,25405,25406,25407

message number = 22, process id = 25407, time and date = 22:40:15 05/07/04
 detached from shm2

message number = 23, process id = 25407, time and date = 22:40:15 05/07/04
 child process pid=25407 terminates

message number = 16, process id = 25404, time and date = 22:40:15 05/07/04
 locked semaphore semid=0

message number = 17, process id = 25404, time and date = 22:40:15 05/07/04
 attached shared memory segment with id 203 at address ff1f0000

message number = 18, process id = 25404, time and date = 22:40:15 05/07/04
 shm2 content 4:25404,25405,25406,25407

message number = 19, process id = 25404, time and date = 22:40:15 05/07/04
 detached from shm2

message number = 20, process id = 25404, time and date = 22:40:15 05/07/04
 Unlocked semaphore semid=1572864

message number = 21, process id = 25404, time and date = 22:40:15 05/07/04
 received notification of child process 25407 terminated

message number = 22, process id = 25404, time and date = 22:40:15 05/07/04
 locked semaphore semid=0

message number = 23, process id = 25404, time and date = 22:40:15 05/07/04
 attached shared memory segment with id 203 at address ff1f0000

message number = 24, process id = 25404, time and date = 22:40:15 05/07/04
 shm2 content 4:25404,25405,25406,25407

message number = 25, process id = 25404, time and date = 22:40:15 05/07/04
 detached from shm2

message number = 26, process id = 25404, time and date = 22:40:15 05/07/04
 Unlocked semaphore semid=1572864

message number = 27, process id = 25404, time and date = 22:40:15 05/07/04
 received notification of child process 25406 terminated

message number = 28, process id = 25404, time and date = 22:40:15 05/07/04
 locked semaphore semid=0

message number = 29, process id = 25404, time and date = 22:40:15 05/07/04
 attached shared memory segment with id 203 at address ff1f0000

message number = 30, process id = 25404, time and date = 22:40:15 05/07/04
 shm2 content 4:25404,25405,25406,25407

message number = 31, process id = 25404, time and date = 22:40:15 05/07/04
 detached from shm2

message number = 32, process id = 25404, time and date = 22:40:15 05/07/04
 Unlocked semaphore semid=1572864

message number = 33, process id = 25404, time and date = 22:40:15 05/07/04
 Removed semaphore semid=1572864

message number = 34, process id = 25404, time and date = 22:40:15 05/07/04
 going to remove shared memory with id = 3801

message number = 35, process id = 25404, time and date = 22:40:15 05/07/04
 removed shared memory with id = 3801 

message number = 36, process id = 25404, time and date = 22:40:15 05/07/04
 going to remove shared memory id = 203

message number = 37, process id = 25404, time and date = 22:40:15 05/07/04
 removed shared memory with id = 203 

message number = 38, process id = 25404, time and date = 22:40:15 05/07/04
 the program is finished

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