Chapter 47. System V Semaphores

This chapter describes System V semaphores. Unlike the IPC mechanisms described in previous chapters, System V semaphores are not used to transfer data between processes. Instead, they allow processes to synchronize their actions. One common use of a semaphore is to synchronize access to a block of shared memory, in order to prevent one process from accessing the shared memory at the same time as another process is updating it.

A semaphore is a kernel-maintained integer whose value is restricted to being greater than or equal to 0. Various operations (i.e., system calls) can be performed on a semaphore, including the following:

The last two of these operations may cause the calling process to block. When lowering a semaphore value, the kernel blocks any attempt to decrease the value below 0. Similarly, waiting for a semaphore to equal 0 blocks the calling process if the semaphore value is not currently 0. In both cases, the calling process remains blocked until some other process alters the semaphore to a value that allows the operation to proceed, at which point the kernel wakes the blocked process. Figure 47-1 shows the use of a semaphore to synchronize the actions of two processes that alternately move the semaphore value between 0 and 1.

Using a semaphore to synchronize two processes

Figure 47-1. Using a semaphore to synchronize two processes

In terms of controlling the actions of a process, a semaphore has no meaning in and of itself. Its meaning is determined only by the associations given to it by the processes using the semaphore. Typically, processes agree on a convention that associates a semaphore with a shared resource, such as a region of shared memory. Other uses of semaphores are also possible, such as synchronization between parent and child processes after fork(). (In Avoiding Race Conditions by Synchronizing with Signals, we looked at the use of signals to accomplish the same task.)

The general steps for using a System V semaphore are the following:

Most operating systems provide some type of semaphore primitive for use in application programs. However, System V semaphores are rendered unusually complex by the fact that they are allocated in groups called semaphore sets. The number of semaphores in a set is specified when the set is created using the semget() system call. While it is common to operate on a single semaphore at a time, the semop() system call allows us to atomically perform a group of operations on multiple semaphores in the same set.

Because System V semaphores are created and initialized in separate steps, race conditions can result if two processes try to perform these steps at the same time. Describing this race condition and how to avoid it requires that we describe semctl() before describing semop(), which means that there is quite a lot of material to cover before we have all of the details required to fully understand semaphores.

In the meantime, we provide Example 47-1 as a simple example of the use of the various semaphore system calls. This program operates in two modes:

  • Given a single integer command-line argument, the program creates a new semaphore set containing a single semaphore, and initializes the semaphore to the value supplied in the command-line argument. The program displays the identifier of the new semaphore set.

  • Given two command-line arguments, the program interprets them as (in order) the identifier of an existing semaphore set and a value to be added to the first semaphore (numbered 0) in that set. The program carries out the specified operation on that semaphore. To enable us to monitor the semaphore operation, the program prints messages before and after the operation. Each of these messages begins with the process ID, so that we can distinguish the output of multiple instances of the program.

The following shell session log demonstrates the use of the program in Example 47-1. We begin by creating a semaphore that is initialized to 0:

$ ./svsem_demo 0
Semaphore ID = 98307                    ID of new semaphore set

We then execute a background command that tries to decrease the semaphore value by 2:

$ ./svsem_demo 98307 -2 &
23338: about to semop at  10:19:42
[1] 23338

This command blocked, because the value of the semaphore can’t be decreased below 0. Now, we execute a command that adds 3 to the semaphore value:

$ ./svsem_demo 98307 +3
23339: about to semop at  10:19:55
23339: semop completed at 10:19:55
23338: semop completed at 10:19:55
[1]+  Done              ./svsem_demo 98307 -2

The semaphore increment operation succeeded immediately, and caused the semaphore decrement operation in the background command to proceed, since that operation could now be performed without leaving the semaphore’s value below 0.