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