#include #include #include #include #include #include #include #include #include union semun { long val; struct semid_ds *buf; ushort *array; }; /* Handle for dealing with semaphore */ static int sem_id = -1; /* Create n semaphores */ void semaphore_open(int n) { sem_id = semget( IPC_PRIVATE, n, IPC_CREAT | IPC_EXCL | 0600); if (sem_id < 0) { perror("semget"); exit(1); } } /* Set semaphore n to value v, no exclusion */ void semaphore_set(int n, int v) { union semun se; se.val = v; if (semctl(sem_id, n, SETVAL, se)==-1) { perror("semaphore_set"); exit(1); } } /* return value of semaphore n, no exclusion */ int semaphore_get(int n) { union semun se; int v; if ((v=semctl(sem_id, n, GETVAL, se))==-1) { perror("semaphore_set"); exit(1); } return(v); } /* Enter critical section, or block (sleep) until you can */ /* Works by trying to reduce semaphore n from 1 to 0 */ void semaphore_lock(int n) { struct sembuf op; op.sem_num = n; op.sem_op = -1; op.sem_flg = SEM_UNDO; while (semop(sem_id, &op, 1) < 0) { /* if we got woken up by an interupt/signal keep trying */ if (errno != EINTR) { /* something bad happened */ perror("semaphore_lock"); exit(-1); } } } /* Leave critical section, doesn't need to block */ /* Works by adding 1 to semaphore n */ void semaphore_unlock(int n) { struct sembuf op; op.sem_num = n; op.sem_op = 1; op.sem_flg = SEM_UNDO; while (semop(sem_id, &op, 1) < 0) { /* if we got woken up by an interupt/signal keep trying */ if (errno != EINTR) { /* something bad happened */ perror("semaphore_lock"); exit(-1); } } } /* Delay for a random ( < max) uS */ void small_delay( int max ) { int n; n = ( int )((( double )max)*rand()/(RAND_MAX+1.0)); usleep(n); } #define CHILDREN 10 /*** This code tries to illustrate two different uses of semaphores: *** one as a means for sharing a variable (int) amongst several process, *** second as a Mutual Exclusion mechanism. *** *** The usefulness of a MutEx mechanism is illustrated by having each *** of 10 children simultaneously trying to add values to a shared number. *** The version that does not use mutex ends with the wrong value because *** in between it reading, adding 1, and writing back the value, another *** process may happen to get in there and also perform the same task. *** The first process then destroys the second's work when it finishes *** writing back the value. ***/ int main(int argc, char **argv) { int pid[CHILDREN], i; /* Create 3 semaphores * 0 running count * 1 running count with mutex (mutual exclusion) * 2 mutex */ semaphore_open(3); /* initialise counts to zero and mutex to 1 */ semaphore_set(0, 0); semaphore_set(1, 0); semaphore_set(2, 1); printf("PARENT: Starting %d children...\n", CHILDREN); /* create 10 children */ for (i=0; i=CHILDREN) { /* I completed the fork loop, I must therefore be the parent */ /* Parent code */ int z; printf("PARENT: sleeping to wait for children.\n"); while (1) { int ppid, j, c; /* wait for the next child to die */ ppid = wait(NULL); if (ppid==-1) { perror("wait"); sleep(1); continue; } /* find the child */ j=0; while (j=CHILDREN) { printf("PARENT: Strange, pid %d died, but it wasn't one of my children.\n", ppid); } else /* mark it dead */ pid[j]=0; /* count how many remain */ c=0; for (j=0;j 0) c++; if (c==0) /* none left */ break; } /* Run out of children now */ z = semaphore_get(0); printf("PARENT: First Value : %d\n", z); z = semaphore_get(1); printf("PARENT: Second Value : %d (with mutex)\n", z); exit(0); } else { /* Child code */ int j, k, p; /* Which child am I? */ p = getpid(); /* make the random times more random */ srand(p); /* wait a little before starting */ small_delay(10000); printf("CHILD: pid %d starting my run.\n", p); /* Our job: to add 1 to the counters 100 times */ for (j=0; j< 100; j++) { /* reduce the chance that two will happen at the same time by waiting a little */ small_delay(100); /* first read, add 1, write the unprotected var */ k = semaphore_get(0); k++; /* this delay is what causes the problem as another process might slip in the gap */ small_delay(2); semaphore_set(0, k); /* Next lock, read, add, write, unlock var2 */ /* This is the same as above, only using exclusion */ semaphore_lock(2); /* semaphore 2 is our mutex */ k = semaphore_get(1); k++; small_delay(2); semaphore_set(1, k); semaphore_unlock(2); } printf("CHILD pid %d finished.\n", p); exit(0); } }