To work with a named semaphore, we employ the following functions:
The sem_open() function opens or creates a semaphore, initializes the semaphore if it is created by the call, and returns a handle for use in later calls.
The sem_post(sem) and sem_wait(sem) functions respectively increment and decrement a semaphore’s value.
The sem_getvalue() function retrieves a semaphore’s current value.
The sem_close() function removes the calling process’s association with a semaphore that it previously opened.
The sem_unlink() function removes a semaphore name and marks the semaphore for deletion when all processes have closed it.
SUSv3 doesn’t specify how named semaphores are to be implemented. Some UNIX implementations create them as files in a special location in the standard file system. On Linux, they are created as small POSIX shared memory objects with names of the form sem.
name, in a dedicated tmpfs file system (A Virtual Memory File System: tmpfs) mounted under the directory /dev/shm
. This file system has kernel persistence--the semaphore objects that it contains will persist, even if no process currently has them open, but they will be lost if the system is shut down.
Named semaphores are supported on Linux since kernel 2.6.
The sem_open() function creates and opens a new named semaphore or opens an existing semaphore.
#include <fcntl.h> /* Defines O_* constants */
#include <sys/stat.h> /* Defines mode constants */
#include <semaphore.h>
sem_t *sem_open
(const char *name, int oflag, ...
/* mode_t mode, unsigned int value */ );
Returns pointer to semaphore on success, or SEM_FAILED
on error
The name argument identifies the semaphore. It is specified according to the rules given in Section 51.1.
The oflag argument is a bit mask that determines whether we are opening an existing semaphore or creating and opening a new semaphore. If oflag is 0, we are accessing an existing semaphore. If O_CREAT
is specified in oflag, then a new semaphore is created if one with the given name doesn’t already exist. If oflag specifies both O_CREAT
and O_EXCL
, and a semaphore with the given name already exists, then sem_open() fails.
If sem_open() is being used to open an existing semaphore, the call requires only two arguments. However, if O_CREAT
is specified in flags, then two further arguments are required: mode and value. (If the semaphore specified by name already exists, then these two arguments are ignored.) These arguments are as follows:
The mode argument is a bit mask that specifies the permissions to be placed on the new semaphore. The bit values are the same as for files (Table 15-4, in Permissions on Regular Files), and, as with open(), the value in mode is masked against the process umask (The Process File Mode Creation Mask: umask()). SUSv3 doesn’t specify any access mode flags (O_RDONLY
, O_WRONLY
, and O_RDWR
) for oflag. Many implementations, including Linux, assume an access mode of O_RDWR
when opening a semaphore, since most applications using semaphores must employ both sem_post() and sem_wait(), which involve reading and modifying a semaphore’s value. This means that we should ensure that both read and write permissions are granted to each category of user—owner, group, and other—that needs to access the semaphore.
The value argument is an unsigned integer that specifies the initial value to be assigned to the new semaphore. The creation and initialization of the semaphore are performed atomically. This avoids the complexities required for the initialization of System V semaphores (Semaphore Initialization).
Regardless of whether we are creating a new semaphore or opening an existing semaphore, sem_open() returns a pointer to a sem_t value, and we employ this pointer in subsequent calls to functions that operate on the semaphore. On error, sem_open() returns the value SEM_FAILED
. (On most implementations, SEM_FAILED
is defined as either ((sem_t *) 0) or ((sem_t *) -1); Linux defines it as the former.)
SUSv3 states that the results are undefined if we attempt to perform operations (sem_post(), sem_wait(), and so on) on a copy of the sem_t variable pointed to by the return value of sem_open(). In other words, the following use of sem2 is not permissible:
sem_t *sp, sem2 sp = sem_open(...); sem2 = *sp; sem_wait(&sem2);
When a child is created via fork(), it inherits references to all of the named semaphores that are open in its parent. After the fork(), the parent and child can use these semaphores to synchronize their actions.
The program in Example 53-1 provides a simple command-line interface to the sem_open() function. The command format for this program is shown in the usageError() function.
The following shell session log demonstrates the use of this program. We first use the umask command to deny all permissions to users in the class other. We then exclusively create a semaphore and examine the contents of the Linux-specific virtual directory that contains named semaphores.
$umask 007
$./psem_create -cx /demo 666
666 means read+write for all users $ls -l /dev/shm/sem.*
-rw-rw---- 1 mtk users 16 Jul 6 12:09 /dev/shm/sem.demo
The output of the ls command shows that the process umask overrode the specified permissions of read plus write for the user class other.
If we try once more to exclusively create a semaphore with the same name, the operation fails, because the name already exists.
$ ./psem_create -cx /demo 666
ERROR [EEXIST File exists] sem_open Failed because of O_EXCL
Example 53-1. Using sem_open() to open or create a POSIX named semaphore
psem/psem_create.c
#include <semaphore.h> #include <sys/stat.h> #include <fcntl.h> #include "tlpi_hdr.h" static void usageError(const char *progName) { fprintf(stderr, "Usage: %s [-cx] name [octal-perms [value]]\n", progName); fprintf(stderr, " -c Create semaphore (O_CREAT)\n"); fprintf(stderr, " -x Create exclusively (O_EXCL)\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int flags, opt; mode_t perms; unsigned int value; sem_t *sem; flags = 0; while ((opt = getopt(argc, argv, "cx")) != -1) { switch (opt) { case 'c': flags |= O_CREAT; break; case 'x': flags |= O_EXCL; break; default: usageError(argv[0]); } } if (optind >= argc) usageError(argv[0]); /* Default permissions are rw-------; default semaphore initialization value is 0 */ perms = (argc <= optind + 1) ? (S_IRUSR | S_IWUSR) : getInt(argv[optind + 1], GN_BASE_8, "octal-perms"); value = (argc <= optind + 2) ? 0 : getInt(argv[optind + 2], 0, "value"); sem = sem_open(argv[optind], flags, perms, value); if (sem == SEM_FAILED) errExit("sem_open"); exit(EXIT_SUCCESS); }psem/psem_create.c
When a process opens a named semaphore, the system records the association between the process and the semaphore. The sem_close() function terminates this association (i.e., closes the semaphore), releases any resources that the system has associated with the semaphore for this process, and decreases the count of processes referencing the semaphore.
#include <semaphore.h>
int sem_close
(sem_t *sem);
Returns 0 on success, or -1 on error
Open named semaphores are also automatically closed on process termination or if the process performs an exec().
Closing a semaphore does not delete it. For that purpose, we need to use sem_unlink().
The sem_unlink() function removes the semaphore identified by name and marks the semaphore to be destroyed once all processes cease using it (this may mean immediately, if all processes that had the semaphore open have already closed it).
#include <semaphore.h>
int sem_unlink
(const char *name);
Returns 0 on success, or -1 on error
Example 53-2 demonstrates the use of sem_unlink().
Example 53-2. Using sem_unlink() to unlink a POSIX named semaphore
psem/psem_unlink.c
#include <semaphore.h> #include "tlpi_hdr.h" int main(int argc, char *argv[]) { if (argc != 2 || strcmp(argv[1], "--help") == 0) usageErr("%s sem-name\n", argv[0]); if (sem_unlink(argv[1]) == -1) errExit("sem_unlink"); exit(EXIT_SUCCESS); }psem/psem_unlink.c