The word semaphore comes from the Greek words sema which means signal and foros that means carrier. Semaphores have been used for railways for several decades to prevent two trains running on the same rail but in different directions from crashing into each other. We can also see a similar thing by the use of traffic lights in our streets and roads. Semaphores were first implemented for computers by Edgar Dijkstra in 1965.
What is a semaphore?
From the RTOS point of view it is a counter and an associated queue. The counter is an unsigned so it’s minimum value is 0 (zero), which also means that the semaphore is not available. If the value of the counter is larger than 0, then the semaphore is available. When the semaphore is created its initial counter value is set. The queue is used for queuing tasks that are waiting for the semaphore to become available. Normally you can specify when you create the semaphore in which order the tasks should be waiting, either in FIFO or in priority order.
Three Types of Semaphores
Three types of semaphores exist, binary semaphore, counting semaphore and mutex (mutual exclusion) semaphore.
For a binary semaphore the counting range of the counter is 0 – 1. That means that a binary semaphore can only be taken by one task at a time. For a counting semaphore the counting range of the counter is 0 – (2^32 – 1), which means that a counting semaphore can be taken by several tasks at the same time. The mutex semaphore is a special counting semaphore, in the way that it also has an ownership which means that only the task that owns the mutex can take it recursively. The mutex semaphore is mostly used for mutual exclusion, see also the article ”Managing Mutual Exclusion Mechanism for Real-Time Applications” which in detail describes the way this type of semaphore can be used. The rest of article
will therefore focus on binary and counting semaphores.
Usage of semaphores
Semaphores can be used in several ways, but the two most common situations are mutual exclusion and task synchronization. Mutual exclusion means that the semaphore is used to protect a common resource like a data buffer, device driver etc, for more details see:
”Managing Mutual Exclusion Mechanism for Real-Time Applications”
Task synchronization means that one task or an ISR is responsible for unblocking another task. This can of course also be done by using events (see Events) or queues (see Queues). The reasons for using semaphores can either be that they are faster than events and queues but also that they have a counting mechanism, which events do not have. So when should you use a binary semaphore and when should you use a counting semaphore? The question you should ask instead is: ”Is it acceptable or not that a synchronization event is missed or not?” Let us look at two examples.
A task should do something when a switch is changed from ON to OFF and vice versa. The switch generates an interrupt every time it is changed. The ISR should then trigger the task, which reads the status of the switch. In this case you should use a binary semaphore, as historical information is not interesting. If the switch has changed it’s status twice and the task did not have a chance to handle the first change, that information is now old, and should not be handled, just the second change is of interest.
Let us continue to use the switch, but let us change the usage of the task. In this example the task is responsible for logging all changes of the switch. In this case it is not acceptable that any information is lost, so in this case a counting semaphore should be used.