Semaphores and Mutexes

The POSIX thread library contains functions for working with semaphores and mutexes. There is much more to say than what is mentioned here. A good place to find more information is linux.die.net.

The functions should all be compiled and linked with -pthread.


What is a semaphore in LINUX?

A semaphore is fundamentally an integer whose value is never allowed to fall below 0. There are two operations on a semaphore: wait and post. The post operation increment the semaphore by 1, and the wait operations does the following: If the semaphore has a value > 0, the semaphore is decremented by 1. If the semaphore has value 0, the caller will be blocked (busy-waiting or more likely on a queue) until the semaphore has a value larger than 0, and then it is decremented by 1.

We declare a semaphore as:

sem_t sem;

where sem_t is a typedef defined in a header file as (apparently) a kind of unsigned char.

An example of this might be that we have a set of N interchangeable resources. We start with semaphore S = N. We use a resource, so there are now N-1 available (wait), and we return it when we are done (post). If the semaphore has value 0, there are no resources available, and we have to wait (until someone does a post).

Semaphores are thus used to coordinate concurrent processes.

This is what some people call a "counted semaphore". There is a similar notion called a "binary semaphore" which is limited to the values 0 and 1.

A semaphore may be named or unnamed. These notes assume we are using named semaphores.


sem_init()

Prototype: int sem_init(sem_t * sem, int pshared, unsigned int value);

Library: #include <semaphore.h>

Purpose: This initializes the semaphore *sem. The initial value of the semaphore will be value. If pshared is 0, the semaphore is shared among all threads of a process (and hence need to be visible to all of them such as a global variable). If pshared is not zero, the semaphore is shared but should be in shared memory.

Notes:


sem_wait()

Prototype: int sem_wait(sem_t * sem);

Library: #include <semaphore.h>

Purpose: This implements the wait function described above on the semaphore *sem.

Notes:


sem_post()

Prototype: int sem_post(sem_t * sem);

Library: #include <semaphore.h>

Purpose: This implements the post function described above on the semaphore *sem.

Note: On success, the return value is 0, and on failure, the return value is -1 (and the value of the semaphore is unchanged).


sem_destroy()

Prototype: int sem_destroy(sem_t * sem);

Library: #include <semaphore.h>

Purpose: This destroys the semaphore *sem, so *sem becomes uninitialized.

Notes:


What is a mutex in LINUX?

A mutex (named for "mutual exclusion") is a binary semaphore with an ownership restriction: it can be unlocked (the post operation) only by whoever locked it (the wait operation). Thus a mutex offers a somewhat stronger protection than an ordinary semaphore.

We declare a mutex as:

pthread_mutex_t mutex;


pthread_mutex_init()

Prototype:

int pthread_mutex_init(pthread_mutex_t * restrict mutex, 
                            const pthread_mutexattr_t * restrict attr);

Library: #include <pthread.h>

Purpose: This initializes *mutex with the attributes specified by attr. If attr is NULL, a default set of attributes is used. The initial state of *mutex will be "initialized and unlocked".

Notes:


pthread_mutex_destroy()

Prototype: int pthread_mutex_destroy(pthread_mutex_t * restrict mutex);

Library: #include <pthread.h>

Purpose: This destroys the mutex object *mutex, so *mutex becomes uninitialized.

Notes:


pthread_mutex_lock()

Prototype: int pthread_mutex_lock(pthread_mutex_t * mutex);

Library: #include <pthread.h>

Purpose: This locks *mutex. If necessary, the caller is blocked until *mutex is unlocked (by someone else) and then &mutex is locked. When the function call ends, *mutex will be in a locked state.

Notes:


pthread_mutex_unlock()

Prototype: int pthread_mutex_unlock(pthread_mutex_t * mutex);

Library: #include <pthread.h>

Purpose: This unlocks *mutex.

Notes: