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