Les Semaphore
Les sémaphores sont des variables qui permettent de synchroniser plusieurs tâches concurrentes. Le concept a été inventé dans les années 60 par Edsger Dijkstra. Le livre gratuit The Little Book of Semaphores est un excellent moyen d’approfondir vos connaissances avec les sémaphores et la programmation concurrente en général. Pour les plus téméraires, vous pouvez aussi lire l’article original EWD123 de Dijkstra.
FreeRTOS propose deux types de sémaphores avec l’interface semphr. h :
- Counting semaphores
- Binary semaphores
Le counting semaphore est le sémaphore traditionnel avec un compteur représenté
par un entier non signé. On crée un sémaphore avec la fonction xSemaphoreCreateCounting :
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount);
Le uxMaxCount est la valeur maximum que peut prendre le compteur. Ce n’est
pas habituel de fixer une limite supérieure pour un sémaphore, et
FreeRTOS impose cette limite à cause de la manière dont est implémenté
le sémaphore. Dans la mesure du possible, on préfère utiliser des
queues plutôt que des sémaphores pour synchroniser des tâches.
Quand le sémaphore est créé, on utilise la fonction xSemaphoreTake pour
décrémenter le sémaphore. C’est l’équivalent de la fonction P de
Dijkstra ou de la méthode acquire() de Java.
xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
Le paramètre xTicksToWait permet de définir le temps à attendre avant de retourner
si le sémaphore est à zéro. Ça permet de tester si un sémaphore est disponible ou non.
Pour incrémenter le sémaphore, in utilise la méthode xSemaphoreGive().
C’est l’équivalent de la fonction V de
Dijkstra ou de la méthode release() de Java.
xSemaphoreGive(SemaphoreHandle_t xSemaphore);
Le binary semaphore est un sémaphore binaire. C’est comme un counting semaphore
avec un uxMaxCount de 1.
On crée un sémaphore binaire avec la fonction xSemaphoreCreateBinary:
SemaphoreHandle_t xSemaphoreCreateBinary(void);
Exemple :
xSemaphoreHandle xSemaphore = NULL;
void vATask(void* pvParameters) {
// Create the semaphore to guard a shared resource.
vSemaphoreCreateBinary(xSemaphore);
if (xSemaphore != NULL) {
if (xSemaphoreGive(xSemaphore) != pdTRUE) {
// We would expect this call to fail because we cannot give
// a semaphore without first "taking" it!
}
// Obtain the semaphore - don't block if the semaphore is not
// immediately available.
if (xSemaphoreTake(xSemaphore,(portTickType) 0)) {
// We now have the semaphore and can access the shared resource.
// ...
// We have finished accessing the shared resource so can free the
// semaphore.
if (xSemaphoreGive(xSemaphore) != pdTRUE) {
// We would not expect this call to fail because we must have
// obtained the semaphore to get here.
}
}
}
}
Lors de son initialisation, le compteur du sémaphore binaire vaut zéro.
Pour interagir avec le sémaphore binaire, nous utilisons les mêmes fonctions qu’avec le counting semaphore :
xSemaphoreTakepour décrémenter le sémaphore binairexSemaphoreGivepour incrémenter le sémaphore binaire