/************************************************************************** File: ~liux/public_html/courses/cop4610/examples/bounded-buffer.cc Purpose: This program creates two shared memory segments and three semophares and initiliaze them properly. Then it starts a number of producers and consumers. The bounded-buffer aglorithm given in the book by Silberschatz and Galvin is used to solve the problem. Shared variables: type item = var buffer = full, empty, mutex: semaphore; nextp, nextc: item; full :=0; empty := n; mutex :=1; Producer process repeat produce an item in nextp wait(empty); wait(mutex); signal(mutex); signal(full); until false; Consumer process repeat wait(full) wait(mutex); remove an item from buffer to nextc signal(mutex); signal(empty); consume the item in nextc until false; Author: Xiuwen Liu On the web: http://www.cs.fsu.edu/~liux/courses/cop4610/examples/bounded-buffer.c ***************************************************************************/ #include #include #include #include #include #include #include #include #include "bounded-buffer.h" struct child_info Child_rec[MAX_CHILDREN]; void catch_sig(int sig); void start_system(int nchild); void start_child(int index); void kill_system(int nchild); int Please_die = 0; /* loop control */ int R_flag = 0; int runningTime = 60; int bufferSize = 8; /* * * FUNCTION: main * * DESCRIPTION: * * This program starts a system of cooperating processes. To ensure * synchronization, the system allocates two shared memory * segments and three semophares and initialize properly for * producer/consumer problem. * * After starting the system, this process waits for a period of * time and kills the child processes it started. * */ int main (int argc, char *argv[]) { register int ii; int nchild; int pid; int status; int kk; struct admin *shmaddr; int semid; int shmid, shmid_1; key_t key, key_1; /* * We will catch any of the following signals. We do * this so that we can gracefully exit and clean up the * the system(i.e. all of the children). Note: there are * better choices for signal handling system calls in UNIX. * These are being used because of their simplicity. */ nchild = MAX_CHILDREN; signal (SIGTERM, catch_sig); signal (SIGINT, catch_sig); signal (SIGQUIT, catch_sig); signal (SIGALRM, catch_sig); /* Create shared memory segments */ key = (key_t)getpid(); key_1 = key+1; shmid = shmget(key, sizeof(struct admin), 00600|IPC_CREAT); if (shmid < 0) { fprintf(stderr,"Error: cannot get adminstrative shared memory segment.\n"); perror("Shared memory: "); exit(-1); } shmid_1 = shmget(key_1, sizeof(struct buffer_record) * bufferSize, 00600|IPC_CREAT); if (shmid_1 < 0) { fprintf(stderr,"Error: cannot get shared circular buffer.\n"); perror("Shared memory: "); printf("Trying to destroy allocated shared memory.\n"); if (shmctl(shmid, IPC_RMID, NULL) < 0) { fprintf(stderr,"Error: Cannot remove administrative shared memory.\n"); } exit(-1); } /* Semphore */ semid = semget(key, 3, 00600|IPC_CREAT); if (semid < 0) { fprintf(stderr,"Error: cannot get semphamores. Returned status = %d\n", semid); perror("Semaphores: "); printf("Trying to destroy allocated shared memories.\n"); if (shmctl(shmid, IPC_RMID, NULL) < 0) { fprintf(stderr,"Error: Cannot remove administrative shared memory.\n"); } if (shmctl(shmid_1, IPC_RMID, NULL) < 0) { fprintf(stderr,"Error: Cannot remove shared circular buffer.\n"); } printf("Done.\n"); exit(-1); } shmaddr = (struct admin *)shmat(shmid, (void *)0,0); shmaddr->size = bufferSize; shmaddr->csize = 0; shmaddr->nextp = 0; shmaddr->nextc = 0; shmaddr->seqnu = 0; arg.val = 1; if (semctl(semid,MUTEX_SEM,SETVAL, arg) < 0) { fprintf(stderr,"Error: cannot set mutex semphare.\n"); exit(-1); } arg.val = 0; if (semctl(semid,FULL_SEM,SETVAL, arg) < 0) { fprintf(stderr,"Error: cannot set full semphare.\n"); exit(-1); } arg.val = bufferSize; if (semctl(semid,EMPTY_SEM,SETVAL, arg) < 0) { fprintf(stderr,"Error: cannot set empty semphare.\n"); exit(-1); } start_system(nchild); alarm(runningTime); /* * Loop until it receives the alarm signal */ while (Please_die == 0); kill_system(nchild); /* destroy shared memories */ shmdt((char *)shmaddr); if (shmctl(shmid, IPC_RMID, NULL) < 0) { fprintf(stderr,"Error: Cannot remove administrative shared memory.\n"); } if (shmctl(shmid_1, IPC_RMID, NULL) < 0) { fprintf(stderr,"Error: Cannot remove shared circular buffer.\n"); } if (semctl(semid, 0, IPC_RMID) < 0) { fprintf(stderr,"Error: Cannot remove semaphore\n"); } return 0; } /* * * FUNCTION: catch_sig * * DESCRIPTION: * * This function runs at interrupt level. Note: At this level a * minimal amount of work should be done. */ void catch_sig(int sig) { Please_die = 1; /*printf("A signal was caught.\n"); */ } /* * * FUNCTION: start_system * * DESCRIPTION: * * Start every process in the system as defined by the initfile. * */ void start_system(int nchild) { register int ii; int k; int j; printf("\n\n<<<<< SYSTEM INITIALIZATION >>>>>\n\n"); for (ii = 0; ii < nchild; ii++) { k = rand()%3; if (k==0) { /* This is a producer */ Child_rec[ii].argv[0] = "consumer"; Child_rec[ii].nice_value = 5; } else { Child_rec[ii].argv[0] = "producer"; Child_rec[ii].nice_value = 0; } Child_rec[ii].argv[1] = (char *)NULL; start_child(ii); } } /* * * FUNCTION: start_child * * DESCRIPTION: * * Start a single child process. * */ void start_child(int index) { register int pid; switch(pid = fork()) { case -1: fprintf(stderr, "Unable to fork child"); break; case 0: (void) nice(Child_rec[index].nice_value); execvp(Child_rec[index].argv[0], Child_rec[index].argv); /* * If we get here then the exec failed. */ fprintf(stderr, "Unable to exec %s\n", Child_rec[index].argv[0]); break; default: Child_rec[index].pid = pid; break; } } /* * * FUNCTION: kill_system * * DESCRIPTION: * * This function kills all remaining children. An enhancement * to this function would be to be give the children a chance to * die gracefully. That is send a SIGTERM that could be followed * by a SIGKILL if the child does not terminate. * */ void kill_system(int nchild) { register int ii; int status; printf("\n<<<<< SYSTEM TERMINATION START>>>>>\n"); for (ii = 0; ii < nchild; ii++) { if (Child_rec[ii].pid > 0) { kill(Child_rec[ii].pid, SIGKILL); (void) wait(&status); } } printf("\n<<<<< SYSTEM TERMINATION COMPLETED>>>>>\n"); }