System Interface Guide

System V Semaphores

Semaphores let processes query or alter status information. They are often used to monitor and control the availability of system resources such as shared memory segments. Semaphores can be operated on as individual units or as elements in a set.

Because System V IPC semaphores can be in a large array, they are extremely heavy weight. Much lighter weight semaphores are available in the threads library (see semaphore(3T)) and POSIX semaphores (see "POSIX Semaphores"). Threads library semaphores must be used with mapped memory (see "Memory Management Interfaces ").

A semaphore set consists of a control structure and an array of individual semaphores. A set of semaphores can contain up to 25 elements. The semaphore set must be initialized using semget(2). The semaphore creator can change its ownership or permissions using semctl(2). Any process with permission can use semctl(2) to do control operations.

Semaphore operations are performed via the semop(2) function. This function takes a pointer to an array of semaphore operation structures. Each structure in the array contains data about an operation to perform on a semaphore. Any process with read permission can test whether a semaphore has a zero value. Operations to increment or decrement a semaphore require write permission.

When an operation fails, none of the semaphores is altered. The process blocks (unless the IPC_NOWAIT flag is set), and remains blocked until:

Only one process at a time can update a semaphore. Simultaneous requests by different processes are performed in an arbitrary order. When an array of operations is given by a semop(2) call, no updates are done until all operations on the array can finish successfully.

If a process with exclusive use of a semaphore terminates abnormally and fails to undo the operation or free the semaphore, the semaphore stays locked in memory in the state the process left it. To prevent this, the SEM_UNDO control flag makes semop(2) allocate an undo structure for each semaphore operation, which contains the operation that returns the semaphore to its previous state. If the process dies, the system applies the operations in the undo structures. This prevents an aborted process from leaving a semaphore set in an inconsistent state.

If processes share access to a resource controlled by a semaphore, operations on the semaphore should not be made with SEM_UNDO in effect. If the process that currently has control of the resource terminates abnormally, the resource is presumed to be inconsistent. Another process must be able to recognize this to restore the resource to a consistent state.

When performing a semaphore operation with SEM_UNDO in effect, you must also have it in effect for the call that will perform the reversing operation. When the process runs normally, the reversing operation updates the undo structure with a complementary value. This ensures that, unless the process is aborted, the values applied to the undo structure are cancel to zero. When the undo structure reaches zero, it is removed.

Using SEM_UNDO inconsistently can lead to excessive resource consumption because allocated undo structures might not be freed until the system is rebooted.

Initializing a Semaphore Set

semget(2) initializes or gains access to a semaphore. When the call succeeds, it returns the semaphore ID (semid). The key argument is a value associated with the semaphore ID. The nsems argument specifies the number of elements in a semaphore array. The call fails when nsems is greater than the number of elements in an existing array; when the correct count is not known, supplying 0 for this argument ensures that it will succeed. The semflg argument specifies the initial access permissions and creation control flags.

The SEMMNI system configuration option determines the maximum number of semaphore arrays allowed. The SEMMNS option determines the maximum possible number of individual semaphores across all semaphore sets. Because of fragmentation between semaphore sets, it might not be possible to allocate all available semaphores.

The following code illustrates the semget() function.


#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/sem.h>
...
 	key_t		key;			/* key to pass to semget() */
 	int		semflg;			/* semflg to pass to semget() */
 	int		nsems;			/* nsems to pass to semget() */
 	int		semid;			/* return value from semget() */
		...
 	key = ...
 	nsems = ...
 	semflg = ...
 	...
 	if ((semid = semget(key, nsems, semflg)) == -1) {
 		perror("semget: semget failed");
 		exit(1);
 	} else
 		exit(0);
 ...

Controlling Semaphores

semctl(2) changes permissions and other characteristics of a semaphore set. It must be called with a valid semaphore ID. The semnum value selects a semaphore within an array by its index. The cmd argument is one of the following control flags.

GETVAL

Return the value of a single semaphore.  

SETVAL

Set the value of a single semaphore. In this case, arg is taken as arg.val, an int.

GETPID

Return the PID of the process that performed the last operation on the semaphore or array.

GETNCNT

Return the number of processes waiting for the value of a semaphore to increase.  

GETZCNT

Return the number of processes waiting for the value of a particular semaphore to reach zero.  

GETALL

Return the values for all semaphores in a set. In this case, arg is taken as arg.array, a pointer to an array of unsigned shorts.

SETALL

Set values for all semaphores in a set. In this case, arg is taken as arg.array, a pointer to an array of unsigned shorts.  

IPC_STAT

Return the status information from the control structure for the semaphore set and place it in the data structure pointed to by arg.buf, a pointer to a buffer of type semid_ds.

IPC_SET

Set the effective user and group identification and permissions. In this case, arg is taken as arg.buf.

IPC_RMID

Remove the specified semaphore set.  

A process must have an effective user identification of owner, creator, or superuser to perform an IPC_SET or IPC_RMID command. Read and write permission is required as for the other control commands.

The following code illustrates semctl(2).


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
...
 	register int				i;
 ...
 	i = semctl(semid, semnum, cmd, arg);
 	if (i == -1) {
 		perror("semctl: semctl failed");
 		exit(1);
...

Semaphore Operations

semop(2) performs operations on a semaphore set. The semid argument is the semaphore ID returned by a previous semget() call. The sops argument is a pointer to an array of structures, each containing the following information about a semaphore operation:

The sembuf structure specifies a semaphore operation, as defined in <sys/sem.h>.The nsops argument specifies the length of the array, the maximum size of which is determined by the SEMOPM configuration option; this is the maximum number of operations allowed by a single semop(2) call, and is set to 10 by default.

The operation to be performed is determined as follows:

There are two control flags that can be used with semop(2).

IPC_NOWAIT

Can be set for any operations in the array. Makes the function return without changing any semaphore value if any operation for which IPC_NOWAIT is set cannot be performed. The function fails if it tries to decrement a semaphore more than its current value, or tests a nonzero semaphore to be equal to zero.  

SEM_UNDO

Allows individual operations in the array to be undone when the process exits.  

The following code illustrates the semop(2) function.


#include				<sys/types.h>
#include				<sys/ipc.h>
#include				<sys/sem.h>
...
 	int				i;			/* work area */
 	int				nsops;	/* number of operations to do */
 	int				semid;	/* semid of semaphore set */
 	struct sembuf	*sops;	/* ptr to operations to perform */
 	...
 	if ((i = semop(semid, sops, nsops)) == -1) {
 		perror("semop: semop failed");
 	} else
 		(void) fprintf(stderr, "semop: returned %d\n", i);
 ...