Les sémaphores ne sont que des variables normales utilisées pour coordonner les activités de plusieurs processus dans un système informatique. Ils sont utilisés pour imposer une exclusion mutuelle, éviter les conditions de concurrence et mettre en œuvre la synchronisation entre les processus.
Le processus d'utilisation des sémaphores prévoit deux opérations : attendre (P) et signaler (V). L'opération d'attente décrémente la valeur du sémaphore et l'opération de signal incrémente la valeur du sémaphore. Lorsque la valeur du sémaphore est nulle, tout processus effectuant une opération d'attente sera bloqué jusqu'à ce qu'un autre processus effectue une opération de signal.
Les sémaphores sont utilisés pour implémenter les sections critiques, qui sont des régions de code qui doivent être exécutées par un seul processus à la fois. En utilisant des sémaphores, les processus peuvent coordonner l'accès aux ressources partagées, telles que la mémoire partagée ou les périphériques d'E/S.
Un sémaphore est un type particulier de données de synchronisation qui ne peut être utilisé que via des primitives de synchronisation spécifiques. Lorsqu'un processus effectue une opération d'attente sur un sémaphore, l'opération vérifie si la valeur du sémaphore est>0. Si tel est le cas, il décrémente la valeur du sémaphore et laisse le processus continuer son exécution ; sinon, cela bloque le processus sur le sémaphore. Une opération de signal sur un sémaphore active un processus bloqué sur le sémaphore le cas échéant, ou incrémente la valeur du sémaphore de 1. En raison de cette sémantique, les sémaphores sont également appelés sémaphores de comptage. La valeur initiale d'un sémaphore détermine le nombre de processus qui peuvent passer l'opération d'attente.
Les sémaphores sont de deux types :
- Sémaphore binaire –
Ceci est également connu sous le nom de verrouillage mutex. Il ne peut avoir que deux valeurs – 0 et 1. Sa valeur est initialisée à 1. Il est utilisé pour mettre en œuvre la solution de problèmes de sections critiques avec plusieurs processus. - Compter le sémaphore –
Sa valeur peut s'étendre sur un domaine sans restriction. Il est utilisé pour contrôler l’accès à une ressource comportant plusieurs instances.
Voyons maintenant comment cela se produit.
essayer la structure des données
Tout d’abord, examinez deux opérations qui peuvent être utilisées pour accéder et modifier la valeur de la variable sémaphore.

Quelques points concernant le fonctionnement P et V :
- L'opération P est également appelée opération d'attente, de veille ou d'arrêt, et l'opération V est également appelée opération de signal, de réveil ou de réveil.
- Les deux opérations sont atomiques et le(s) sémaphore(s) sont toujours initialisés à un. Ici, atomique signifie cette variable sur laquelle la lecture, la modification et la mise à jour se produisent en même temps/moment sans préemption, c'est-à-dire entre la lecture, la modification et la mise à jour, aucune autre opération susceptible de modifier la variable n'est effectuée.
- Une section critique est entourée des deux opérations pour mettre en œuvre la synchronisation des processus. Voir l'image ci-dessous. La section critique du processus P se situe entre les opérations P et V.

Voyons maintenant comment il met en œuvre l’exclusion mutuelle. Supposons qu'il y ait deux processus P1 et P2 et qu'un sémaphore s est initialisé à 1. Maintenant, si P1 entre dans sa section critique alors la valeur du sémaphore s devient 0. Maintenant, si P2 veut entrer dans sa section critique alors il attendra que s> 0, cela ne peut se produire que lorsque P1 termine sa section critique et appelle l'opération V sur les sémaphores.
De cette façon, l’exclusion mutuelle est réalisée. Regardez l'image ci-dessous pour plus de détails qui est un sémaphore binaire.

Mise en œuvre: Sémaphores binaires
arbre binaire de traversée par correspondanceC++
struct semaphore { enum value(0, 1); // q contains all Process Control Blocks (PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; } ; P(sémaphore s) { if (s.value == 1) { s.value = 0; } else { // ajoute le processus à la file d'attente q.push(P) sleep(); } } V(sémaphore s) { if (s.q est vide) { s.value = 1; } else { // sélectionne un processus dans la file d'attente Process p = q.front(); // supprime le processus d'attente tel qu'il a été // envoyé pour CS q.pop(); réveille toi); } } // Ce code est modifié par Susobhan Akhuli> C #include #include #include struct semaphore{ Queueq; valeur entière ; } ; void P(struct sémaphore s) { if (s.value == 1) { s.value = 0; } else { sq.push(P); dormir(); } } void V(sémaphore s) { if (s.q est vide) { s.value = 1; } else { // Récupère un processus à partir du processus de file d'attente p = q.front(); // Supprime le processus d'attente q.pop(); réveille toi); } } int main() { printf('C'est Hemish !!'); // Ce code est contribué par Himesh Singh Chauhan return 0; } // Ce code est modifié par Susobhan Akhuli> Java import java.util.*; class Semaphore { public enum Value { Zero, One } public Queueq = nouvelle liste liée(); valeur de valeur publique = Value.One ; public void P(Semaphore s, Processus p) { if (s.value == Value.One) { s.value = Value.Zero; } else { // ajoute le processus à la file d'attente q.add(p); p.Sleep(); } } public void V(Semaphore s) { if (s.q.size() == 0) { s.value = Value.One; } else { // sélectionne un processus dans la file d'attente Process p = q.peek(); // supprime le processus d'attente car il // a été envoyé pour CS q.remove(); p.Wakeup(); } } }> Python3 from enum import Enum from queue import Queue class Semaphore: class Value(Enum): Zero = 0 One = 1 def __init__(self): self.q = Queue() self.value = Semaphore.Value.One def P(self, s, p): if s.value == Semaphore.Value.One: s.value = Semaphore.Value.Zero else: # add the process to the waiting queue s.q.put(p) p.Sleep() def V(self, s): if s.q.qsize() == 0: s.value = Semaphore.Value.One else: # select a process from waiting queue p = s.q.queue[0] # remove the process from waiting as it has # been sent for CS s.q.get() p.Wakeup()>
C# using System.Collections.Generic; class Semaphore { public enum value { Zero, One } public Queueq = nouvelle file d'attente(); public void P(Semaphore s, Processus p) { if (s.value == value.One) { s.value = value.Zero; } else { // ajoute le processus à la file d'attente q.Enqueue(p); p.Sleep(); } } public void V(Semaphore s) { if (s.q.Count == 0) { s.value = value.One; } else { // sélectionne un processus dans la file d'attente Process p = q.Peek(); // supprime le processus d'attente car il a été // envoyé pour CS q.Dequeue(); p.Wakeup(); } } }> Javascript class Semaphore { constructor() { this.value = 0; // q contains all Process Control Blocks (PCBs) // corresponding to processes got blocked // while performing down operation. this.q = []; } P() { if (this.value == 1) { this.value = 0; } else { // add the process to the waiting queue this.q.push(P); sleep(); } } V() { if (this.q.length == 0) { this.value = 1; } else { // select a process from waiting queue let p = this.q.shift(); // remove the process from waiting as it has been // sent for CS wakeup(p); } } }> La description ci-dessus concerne un sémaphore binaire qui ne peut prendre que deux valeurs 0 et 1 et assurer une exclusion mutuelle. Il existe un autre type de sémaphore appelé sémaphore de comptage qui peut prendre des valeurs supérieures à un.
Supposons maintenant qu'il existe une ressource dont le nombre d'instances est de 4. Maintenant, nous initialisons S = 4 et le reste est le même que pour le sémaphore binaire. Chaque fois que le processus veut cette ressource, il appelle P ou attend une fonction et quand cela est fait, il appelle V ou une fonction de signal. Si la valeur de S devient nulle, alors un processus doit attendre que S devienne positif. Par exemple, supposons qu'il y ait 4 processus P1, P2, P3, P4 et qu'ils appellent tous une opération d'attente sur S (initialisé avec 4). Si un autre processus P5 veut la ressource, il doit alors attendre que l'un des quatre processus appelle la fonction signal et que la valeur du sémaphore devienne positive.
Java pgm
Limites :
- L'une des plus grandes limitations du sémaphore est l'inversion des priorités.
- Deadlock, supposons qu’un processus essaie de réveiller un autre processus qui n’est pas en état de veille. Par conséquent, un blocage peut bloquer indéfiniment.
- Le système d'exploitation doit garder une trace de tous les appels pour attendre et signaler le sémaphore.
Problème dans cette implémentation d'un sémaphore :
Le principal problème avec les sémaphores est qu'ils nécessitent une attente occupée. Si un processus se trouve dans la section critique, alors les autres processus essayant d'entrer dans la section critique attendront jusqu'à ce que la section critique ne soit occupée par aucun processus. Chaque fois qu'un processus attend, il vérifie en permanence la valeur du sémaphore (regardez cette ligne pendant que (s==0); en fonctionnement P) et gaspille le cycle du processeur.
Il existe également un risque de verrouillage tournant car les processus continuent de tourner en attendant le verrouillage. Afin d'éviter cela, une autre implémentation est fournie ci-dessous.
Mise en œuvre: Sémaphore de comptage
RPC struct Semaphore { int value; // q contains all Process Control Blocks(PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; } ; P(Sémaphore s) { s.value = s.value - 1; si (s.valeur< 0) { // add process to queue // here p is a process which is currently executing q.push(p); block(); } else return; } V(Semaphore s) { s.value = s.value + 1; if (s.value <= 0) { // remove process p from queue Process p = q.pop(); wakeup(p); } else return; }> Java import java.util.LinkedList; import java.util.Queue; // semaphore class class Semaphore { // our value int value; Queueq; public Sémaphore (int valeur) { this.value = valeur ; q = nouvelle LinkedList(); } public void P(Processus p) { valeur--; si (valeur< 0) { q.add(p); p.block(); } } public void V() { value++; if (value <= 0) { Process p = q.remove(); p.wakeup(); } } }> Python3 import heapq # Global Variable to track the Processes going into Critical Section COUNTER=1 class Semaphore: def __init__(self,value): # Value of the Semaphore passed to the Constructor self.value=value # The Waiting queue which will be using the heapq module of Python self.q=list() def getSemaphore(self): ''' Function to print the Value of the Semaphore Variable ''' print(f'Semaphore Value: {self.value}') def block(process): print(f'Process {process} Blocked.') def wakeup(process): print(f'Process {process} Waked Up and Completed it's work.') def P(s): global COUNTER s.value=s.value-1 if(s.value<0): heapq.heappush(s.q,COUNTER) block(COUNTER) else: print(f'Process {COUNTER} gone inside the Critical Section.') COUNTER+=1 return def V(s): global COUNTER s.value=s.value+1 if(s.value<=0): p=heapq.heappop(s.q) wakeup(p) COUNTER-=1 else: print(f'Process {COUNTER} completed it's work.') COUNTER-=1 return # Can Pass the Value of the Counting Semaphore to the Class Constructor # Example for Counting Semaphore value as 2 s1=Semaphore(2) s1.getSemaphore() P(s1) s1.getSemaphore() P(s1) s1.getSemaphore() P(s1) s1.getSemaphore() V(s1) s1.getSemaphore() V(s1) s1.getSemaphore() V(s1) s1.getSemaphore() # This Code is Contributed by Himesh Singh Chauhan> C# using System.Collections.Generic; public class Semaphore { public int value; // q contains all Process Control Blocks(PCBs) // corresponding to processes got blocked // while performing down operation. Queueq = nouvelle file d'attente(); public void P(Processus p) { valeur--; si (valeur< 0) { // add process to queue q.Enqueue(p); p.block(); } } public void V() { value++; if (value <= 0) { // remove process p from queue Process p = q.Dequeue(); p.wakeup(); } } }> Javascript // Define a Semaphore object function Semaphore() { this.value = 0; this.q = []; // Initialize an array to act as a queue } // Implement the P operation Semaphore.prototype.P = function(p) { this.value = this.value - 1; if (this.value < 0) { // Add process to queue this.q.push(p); // Assuming block() and wakeup() functions are defined elsewhere block(); } }; // Implement the V operation Semaphore.prototype.V = function() { this.value = this.value + 1; if (this.value <= 0) { // Remove process p from queue var p = this.q.shift(); // Assuming wakeup() function is defined elsewhere wakeup(p); } }; // This code is contributed by Susobhan Akhuli> Dans cette implémentation, chaque fois que le processus attend, il est ajouté à une file d'attente de processus associés à ce sémaphore. Cela se fait via l'appel système block() sur ce processus. Lorsqu'un processus est terminé, il appelle la fonction de signal et un processus dans la file d'attente reprend. Il utilise l'appel système wakeup().
Avantages des sémaphores :
- Un mécanisme simple et efficace pour la synchronisation des processus
- Prend en charge la coordination entre plusieurs processus
- Fournit un moyen flexible et robuste de gérer les ressources partagées.
- Il peut être utilisé pour implémenter des sections critiques dans un programme.
- Il peut être utilisé pour éviter les conditions de course.
Inconvénients des sémaphores :
- Cela peut entraîner une dégradation des performances en raison de la surcharge associée aux opérations d'attente et de signal.
- Peut entraîner un blocage en cas de mauvaise utilisation.
- Il a été proposé par Dijkstra en 1965. Il s'agit d'une technique très importante pour gérer des processus simultanés en utilisant une simple valeur entière, connue sous le nom de sémaphore. Un sémaphore est simplement une variable entière partagée entre les threads. Cette variable est utilisée pour résoudre le problème de la section critique et pour réaliser la synchronisation des processus dans l'environnement multitraitement.
- Cela peut entraîner des problèmes de performances dans un programme s’il n’est pas utilisé correctement.
- Cela peut être difficile à déboguer et à maintenir.
- Il peut être sujet à des conditions de concurrence critique et à d’autres problèmes de synchronisation s’il n’est pas utilisé correctement.
- Il peut être vulnérable à certains types d’attaques, comme les attaques par déni de service.