Semaphores are a programming construct designed by
E. W. Dijkstra in the late 1960s. Dijkstra's model was the operation of
railroads: consider a stretch of railroad in which there is a single track over
which only one train at a time is allowed. Guarding this track is a semaphore. A
train must wait before entering the single track until the semaphore is in a
state that permits travel. When the train enters the track, the semaphore
changes state to prevent other trains from entering the track. A train that is
leaving this section of track must again change the state of the semaphore to
allow another train to enter. In the computer version, a semaphore appears to be
a simple integer. A process (or a thread) waits for permission to proceed by
waiting for the integer to become 0. The signal if it proceeds signals that this
by performing incrementing the integer by 1. When it is finished, the process
changes the semaphore's value by subtracting one from it.

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 man semaphore and also Chapter 30.3) and
POSIX semaphores (see below briefly). Threads library semaphores must be used
with mapped memory . A semaphore set consists of a control structure and an
array of individual semaphores. A set of semaphores can contain up to 25
elements.

In a similar fashion to message queues, the semaphore set must be initialized
using semget(); the semaphore creator can change its ownership or
permissions using semctl(); and semaphore operations are performed via
the semop() function. These are now discussed below:

The function semget() initializes or gains access to a semaphore. It
is prototyped by:

int semget(key_t key, int nsems, int semflg);

When the call succeeds, it returns the semaphore ID (semid).

The key argument is a access 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.

semctl() changes permissions and other characteristics of a
semaphore set. It is prototyped as follows:

int semctl(int semid, int semnum, int cmd, union semun arg);

It must be called with a valid semaphore ID, semid. 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 (see below).

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 ().

The fourth argument union semun arg is optional, depending upon the
operation requested. If required it is of type union semun, which must
be explicitly declared by the application program as:

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 semaphore number

The operation to be performed

Control flags, if any.

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() call, and is set to
10 by default. The operation to be performed is determined as follows:

A positive integer increments the semaphore value by that amount.

A negative integer decrements the semaphore value by that amount. An
attempt to set a semaphore to a value less than zero fails or blocks,
depending on whether IPC_NOWAIT is in effect.

A value of zero means to wait for the semaphore value to reach zero.

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

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.

This function takes a pointer, sops, 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. To increment or decrement a semaphore
requires 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:

the semaphore operations can all finish, so the call succeeds,

the process receives a signal, or

the semaphore set is removed.

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() 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() 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.

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

POSIX semaphores are much lighter weight than are System V semaphores. A
POSIX semaphore structure defines a single semaphore, not an array of up to
twenty five semaphores. The POSIX semaphore functions are:

sem_open() -- Connects to, and optionally creates, a named semaphore

sem_init() -- Initializes a semaphore structure (internal to the
calling program, so not a named semaphore).

sem_close() -- Ends the connection to an open semaphore.

sem_unlink() -- Ends the connection to an open semaphore and causes
the semaphore to be removed when the last process closes it.

sem_destroy() -- Initializes a semaphore structure (internal to the
calling program, so not a named semaphore).

sem_getvalue() -- Copies the value of the semaphore into the
specified integer.

sem_wait(), sem_trywait() -- Blocks while the semaphore is held by
other processes or returns an error if the semaphore is held by another process.

sem_post() -- Increments the count of the semaphore.

The basic operation of these functions is essence the same as described
above, except note there are more specialised functions, here. These are not
discussed further here and the reader is referred to the online man
pages for further details.

The following suite of programs can be used to investigate interactively a
variety of semaphore ideas (see exercises below).

The semaphore must be initialised with the semget.c program.
The effects of controlling the semaphore queue and sending and receiving
semaphore can be investigated with semctl.c and semop.c
respectively.

Write 2 programs that will communicate both ways (i.e each
process can read and write) when run concurrently via semaphores.

Exercise 12764

Modify the semaphore.c program to handle synchronous semaphore
communication semaphores.

Exercise 12765

Write 3 programs that communicate together via semaphores according to the
following specifications: sem_server.c -- a program that can
communicate independently (on different semaphore tracks) with two clients
programs. sem_client1.c -- a program that talks to
sem_server.c on one track. sem_client2.c -- a program that
talks to sem_server.c on another track to sem_client1.c.

Exercise 12766

Compile the programs semget.c, semctl.c and
semop.c and then

investigate and understand fully the operations of the flags (access,
creation etc. permissions) you can set interactively in the programs.

Use the prgrams to:

Send and receive semaphores of 3 different semaphore tracks.

Inquire about the state of the semaphore queue with semctl.c.
Add/delete a few semaphores (using semop.c and perform the inquiry
once more.