Aller au contenu principal

Synchronisation entre processus

Progression

#Synchronisation entre processus

Dès que deux processus partagent une ressource, ils peuvent se nuire. Un producteur qui écrit trop vite dans un tampon plein, un gestionnaire de logs qui lit un fichier pendant qu'un autre le tronque : ces situations produisent des conditions de course. La synchronisation consiste à imposer un ordre compatible avec l'invariant que l'on veut préserver.

#1. Détecter les sections critiques

Identifier la section critique revient à repérer la zone où une ressource partagée est lue ou modifiée. Un compteur global, un fichier, une structure en mémoire partagée : tous nécessitent un protocole. Sans cette analyse préalable, l'ajout d'un mutex ou d'un sémaphore ressemble à de la magie et échoue dès que le programme évolue. Le bon réflexe consiste à écrire l'invariant attendu (par exemple « le tampon contient un nombre d'éléments compris entre 0 et N ») et à vérifier que chaque parcours du code le respecte.

#2. Primitives de synchronisation

Les sémaphores, hérités de Dijkstra, associent un compteur et une file d'attente. sem_wait décrémente ; si la ressource est indisponible, le processus est bloqué et le noyau l'endort. sem_post incrémente et réveille un dormeur éventuel. Les mutex (verrous) se concentrent sur l'exclusion mutuelle : un seul détenteur à la fois, sous peine d'entraînement d'attente active ou de blocage. Les moniteurs encapsulent ces verrous dans une abstraction plus riche comprenant des variables de condition. Sous Unix, on les retrouve via pthread_mutex, pthread_cond, mais aussi sem_open pour synchroniser des processus n'appartenant pas au même espace d'adressage.

cc
1sem_wait(&empty_slots);2pthread_mutex_lock(&lock);3put_item(buffer, item);4pthread_mutex_unlock(&lock);5sem_post(&filled_slots);

Ce fragment du producteur-consommateur montre la combinaison classique : le sémaphore régule le nombre d'éléments, le mutex protège l'accès réel à la structure.

#3. Prévenir l'interblocage

Un deadlock apparaît quand une attente circulaire se forme : A attend un verrou détenu par B, B attend un verrou détenu par C, et ainsi de suite jusqu'à revenir à A. On peut le prévenir en imposant un ordre global de prise de verrous (toujours acquérir lock_a avant lock_b), en utilisant des verrous temporisés (pthread_mutex_timedlock) pour abandonner au-delà d'un délai, ou en détectant périodiquement les cycles grâce à un graphe d'attente. Dans la pratique, la stratégie la plus fiable reste de limiter la durée des sections critiques et de documenter le protocole.

#Atelier

Créez deux processus qui écrivent chacun alternativement dans un même fichier via une zone de mémoire partagée et un sémaphore. Commencez sans synchronisation pour observer les lignes entremêlées, puis ajoutez les primitives nécessaires pour rétablir l'ordre. Terminez en relâchant volontairement un verrou et constatez que l'interblocage ne disparaît pas tout seul : il faut un plan de prévention.