Unix-Linux 4 Semaphores Semaphore data structure shared by multiple processes normally used to synchronize access to a common, non-sharable resources non-negative number stored in the kernel semaphore system calls – atomic actions o test o decrement o increment o block invoking process o notify queued, waiting process Unix Semaphore Sets System Semaphore Data Structure -- <sys/sem.h> o struct semid_ds { struct ipc_perm sem_perm; // permissions struct sem *sem_base // ptr to 1st semaphore in set ushort_t sem_nsems; // number of semaphores in set time_t sem_otime; // last semop time long sem_pad1; // time_t expansion reserve space time_t sem_ctime; // last modification time long sem_pad2; // time_t expansion long sem_pad3[ 4 ]; // reserve area }; o struct sem { ushort pid_t ushort ushort }; array of struct sem semval; sempid semncnt; semzcnt; // semaphore value // pid of last operation // number of pid‟s waiting for semval > cval // number of pid‟s waiting for semval = 0 Creating & Accessing Semaphore Sets o int semget( key_t key, int nsems, int semflg ); key value is used by the system to generate by a unique semaphore identifier nsems is the number of semaphores in the system; it is used to allocate the array of sem structures semflg is used to specify the access permissions &/or special creation conditions; access permissions may be combined with the IPC_CREAT and/or IPC_EXCL flags o semget system call successful returns semaphore identifier (non-negative integer) unsuccessful returns -1 & sets errno key == IPC_PRIVATE or key does not have an associated semaphore identifier && IPC_CREAT has been specified a new set of semaphores is created the array of sem structures is not initialized the semaphore set for the indicated key already exists && IPC_CREAT has been specified semget system call returns the associated semaphore identifier When using semget to access an established semaphore set, the value of nsems can be set to zero (don’t care value) o single character key_t ftok( const char *path, int id ); path to an existing accessible file successful returns a unique but reproducible key value unsuccessful returns -1 int sem1, sem2, sem3; key_t ipc_key = ftok(“.”, „S‟); if((sem1 = semget( ipc_key, 3, IPC_CREAT | 0666)) == -1 ) perror(“semget: IPC_CREAT | 0666): creates set of three semaphores unless system limits have been reached if((sem2 = semget( ipc_key, 3, IPC_CREAT | IPC_EXCL | 0666)) == -1 ) perror(“semget: IPC_CREAT | IPC_EXCL | 0666): attempts to create another set of three semaphores using the same key value, and including the specification IPC_EXCL; the system call fails if((sem3 = semget( IPC_PRIVATE, 3, 0666)) == -1 ) perror(“semget: IPC_PRIVATE): creates set of three semaphores using IPC_PRIVATE instead of the ipc_key the semaphore identifier generated will be private to the calling process Semaphores need to be manually deleted by the user $ipcrm semaphore_identifier Semaphore Control int semctl( int semid, int semnum, int cmd, /* union semun arg */ … ); semid – valid semaphore identifier semnum – number of semaphore in semaphore set cmd – integer command value -- <sys/ipc.h> <sys/sem.h> /* union semun arg */ -- <sys/sem.h> See Unix page 173-174 Linux pages 241-242 union semun { int val; arg.array = my_very_own_array; struct semid_ds * buf; arg.buf = pointer_to_my_very_own_structure; ushort *array; }; int semctl( int semid, int semnum, int cmd, union semun arg ); union semun arg; Semaphore Commands (cmd) IPC_STAT o o o return current values of the struct semid_ds requires a user-generated storage structure requires read permission for the semaphore set IPC_SET o o if the effective ID of the accessing process is that of a superuser, or the same as the ID value stored in sem_perm.cuid or sem_perm.uid then the following members of struct ipc_perm may be changed sem_perm.uid sem_perm.gid sem_perm.mode requires a user-generated structure of type semid_ds the structure must be populated with the current values of the struct semid_ds the desired values must be modified in the user-generated structure the values may established via the IPC_SET command IPC_RMID o remove the semaphore set associated with the semaphore identifier GETALL o o o return the current values of the semaphore set requires an struct sem[ sem_nsems ] array to be allocated requires read permission for the semaphore set SETALL o o o requires an struct sem[ sem_nsems ] array to be allocated & populated with values initialize all semaphores in the set to values stored in that array requires write/alter permission for the semaphore set GETVAL o o return the current value of the individual senum index requires read permission for the semaphore SETVAL o o set the value of the individual semaphore referenced by the senum index to the value specified in arg.val requires write/alter permission for the specified semaphore GETPID o o return the process ID from the struct sem_perm within the struct semid_ds requires read permission for the semaphore GETNCNT o o return # processes waiting for the semaphore referenced by the semnum index to increase in value requires read permission for the semaphore GETZCNT o o return # processes waiting for the semaphore referenced by the semnum index to become zero requires read permission for the semaphore code fragment struct semid_ds { struct ipc_perm sem_perm; // permissions struct sem *sem_base // ptr to 1st semaphore in set ushort_t sem_nsems; // number of semaphores in set time_t sem_otime; // last semop time long sem_pad1; // time_t expansion reserve space time_t sem_ctime; // last modification time long sem_pad2; // time_t expansion long sem_pad3[ 4 ]; // reserve area }; struct sem { ushort semval; // semaphore value pid_t sempid // pid of last operation ushort semncnt; // number of pid‟s waiting for semval > cval ushort semzcnt; // number of pid‟s waiting for semval = 0 }; union semun { int val; struct semid_ds *buf; ushort *array; } Initial values for the three semaphores struct semid_ds static ushort union semun sem_buf;; sem_array[3] = {3, 1, 4}; arg; sem_id = semget(ipc_key, 3, IPC_CREAT, | 0666); // define arg.buf such that the current values in struct semid_ds are returned arg.buf = &sem.buf; semctl(sem_id, 0, IPC_STAT, arg); // define arg.array such that the values in sem_array[3] = {3, 1, 4} are changed arg.array = sem_array; semctl(sem_id, 0, SETALL, arg); for(i = 0; i < 3; ++i) Print the values for the three semaphores { sem_value = semctl(sem_id, i, GETVAL, 0); printf( “Semaphore %d : value %d”, I, sem_value); } // remove the semaphore set semctl(sem_id, 0, IPC_RMID, 0); See Linux pages 177-178 for using dbx to view the content of sem_buf Semaphore Operations Individual Semaphores int semop( int semid, struct sembuf *sops, size_t nsops ); pointer to an array of operations to be performed <sys/sem.h> o struct semid_ds { struct ipc_perm struct sem ushort_t time_t long time_t long long }; o struct sem { ushort pid_t ushort ushort }; number of elements in the array sem_perm; // permissions *sem_base // ptr to 1st semaphore in set sem_nsems; // number of semaphores in set sem_otime; // last semop time sem_pad1; // time_t expansion reserve space sem_ctime; // last modification time sem_pad2; // time_t expansion sem_pad3[ 4 ]; // reserve area array of struct sem semval; sempid semncnt; semzcnt; // semaphore value // pid of last operation // number of pid‟s waiting for semval > cval // number of pid‟s waiting for semval = 0 IPC_NOWAIT semaphore operation fails return immediately SEM_UNDO if a blocked operation fails, allows an operation to be undone process – number of resources currently held system – number of resources currently free sem_op value negative process is attempting to o decrement the semaphore o record the acquisition of a resource affiliated with the semaphore the acquiring process must have alter, i.e., write, permission on the semaphore set semval >= abs(semop) semval = semval – abs(semop) semval >= abs(semop) & SEM_UNDO set semval = semval – abs(semop) semadj = semadj + abs(semop) semval < abs(semop) increment semncnt; block until semval >= abs(semop) adjust semncnt semval = semval – abs(semop) semid is removed return -1 set errno to EIDRM signal is caught adjust semncnt set errno to EINTR semval < abs(semop) & IPC_NOWAIT is set return immediately set nerrno to EAGAIN positive process is attempting to o increment the semaphore o record the release, i.e., return, of a resource affiliated with the semaphore the acquiring process must have alter, i.e., write, permission on the semaphore set semval = semval + sem_op SEM_UNDO is set semval = semval + sem_op semadj = semadj – sem_op sem_op == 0 (zero) test (semval == 0) the accessing process must have read permission on the semaphore set semval == 0 all resources are currently allocated semval == 0 return immediately semval != 0 & IPC_NOWAIT set return -1 & set errno to EAGAIN semval != 0 increment semzcnt block until semval == 0 adjust semzcnt & return semid is removed return -1 & set errno to EIDRM signal is caught adjust semzcnt & set errno to EINTR Producer-Consumer argv[1] holds the # of seconds to sleep between consumer & producer cycles Semaphore Section #define BUFFER “./buffer” // non-sharable resource union semun { struct sembuf int val; { struct semid_ds *buf; ushort sem_num; ushort *array; short sem_op; } short sem_flg; } main (int argc, char *argv[ ]) { sem_flg : SEM_UNDO allows rollback FILE *fptr; static struct sembuf acquire = {0, -1, SEM_UNDO}; // acquire operation defined static struct sembuf release = {0, 1, SEM_UNDO); // release operation defined static ushort start_val[2] = {1, 0}; sem_op : -1 decrements semaphore pid_t c_pid; 1 increments semaphore key_t ipc_key; int semid, producer = 0, i, n, p_sleep, c_sleep; union semun arg; enum{READ, MADE}; ipc_key = ftok(“.”, „S‟); sem_num = 0; placeholder, dynamic change indicates which semaphore is being referenced first pass – producer : semaphore set is created if(( semid = semget(ipc_key, 2, IPC_CREAT | IPC_EXCL | 0666_)) != -1) { producer = 1; provides initial values or the two semaphores arg.array = start_val; if ( semctl(semid, 0, SETALL, arg ) == -1 ){perror … exit(1); } } else if (( semid = semget(ipc_key, 2, 0)) == -1){perror … exit(2);} } second pass – consumer : gain access to the semaphore set Producer Section switch(producer) { case 1: p_sleep = atoi(argv[1]); // producer sleep time srand((unsigned) getpid( )); // determine seed value for random generator for( i = 0; I < 10; i++) struct sembuf { { ushort sem_num; sleep(p_sleep); short sem_op; n = rand( ) % 99 + 1; short sem_flg; printf(“A. Number [%2d] generated by producer\n”, n); } acquire.sem_num = READ; static struct sembuf acquire = {READ, -1, SEM_UNDO}; if ( semop(semid, &acquire, 1) == -1) { perror(“ waiting for consumer “); exit(3);} if (( fptrf = fopen(BUFFER, “w”)) == NULL ) {perror(BUFFER); exit(4);} fprintf(fptr, “%d\n”, n); fclose(fptr); release.sem_num = MADE; static struct sembuf release = {MADE, 1, SEM_UNDO}; printf(“b. Number [%2d] deposited by producer\n”, n); if (semop(semid, &release, 1) == -1) { perror(“ … “); exit(5);} } sleep(5); // waiting for final consumption to occur if (semctl(semid, 0, IPC_RMID, 0) == -1 (perror(“ … “); exit(6);} printf(“Semaphore Removed\n”); break; } Consumer Section case 0; c_sleep = atoi(argv[1]); // consumer sleep time c_pid = getpid( ); while (1) { sleep(c_sleep); acquire.sem_num = MADE; if ( semop(semid, &acquire, 1) == -1) {perror(“waiting for item to be produced”); exit(7);} if (( fptr = fopen(BUFFER, “r”)) == NULL) {perror(BUFFER); exit(8);} fscanf(fptr, “%d”, &n); fclose(fptr); release.sem_num = READ; if (semop(semid, &release, 1) == -1) {perror(“ … “); exit(9);} printf(“C. Number [&2d] obtained by consumer %6d\n”, n, c_pid); } } exit(0); } Entry Critical Exit Acquire READ Release MADE Producer READ MADE 1 0 0 0 0 1 Consumer READ MADE 0 1 0 0 1 0 Acquire MADE Release READ