Semaforen zijn slechts normale variabelen die worden gebruikt om de activiteiten van meerdere processen in een computersysteem te coördineren. Ze worden gebruikt om wederzijdse uitsluiting af te dwingen, race-omstandigheden te vermijden en synchronisatie tussen processen te implementeren.
Het proces van het gebruik van semaforen biedt twee bewerkingen: wachten (P) en signaal (V). De wachtbewerking verlaagt de waarde van de semafoor, en de signaalbewerking verhoogt de waarde van de semafoor. Wanneer de waarde van de semafoor nul is, wordt elk proces dat een wachtoperatie uitvoert geblokkeerd totdat een ander proces een signaaloperatie uitvoert.
Semaforen worden gebruikt om kritieke secties te implementeren. Dit zijn codegebieden die slechts door één proces tegelijk moeten worden uitgevoerd. Door gebruik te maken van semaforen kunnen processen de toegang tot gedeelde bronnen, zoals gedeeld geheugen of I/O-apparaten, coördineren.
Een semafoor is een speciaal soort synchronisatiegegevens die alleen via specifieke synchronisatieprimitieven kunnen worden gebruikt. Wanneer een proces een wachtoperatie uitvoert op een semafoor, controleert de operatie of de waarde van de semafoor>0 is. Als dat zo is, wordt de waarde van de semafoor verlaagd en kan het proces zijn uitvoering voortzetten; anders blokkeert het het proces op de semafoor. Een signaalbewerking op een semafoor activeert een proces dat eventueel op de semafoor is geblokkeerd, of verhoogt de waarde van de semafoor met 1. Vanwege deze semantiek worden semaforen ook wel tellende semaforen genoemd. De initiële waarde van een semafoor bepaalt hoeveel processen voorbij de wachtoperatie kunnen komen.
Er zijn twee soorten seinpalen:
- Binaire Semafoor –
Dit wordt ook wel een mutex-slot genoemd. Het kan slechts twee waarden hebben: 0 en 1. De waarde ervan wordt geïnitialiseerd op 1. Het wordt gebruikt om de oplossing van kritieke sectieproblemen met meerdere processen te implementeren. - Semafoor tellen –
De waarde ervan kan zich uitstrekken over een onbeperkt domein. Het wordt gebruikt om de toegang te controleren tot een bron die meerdere exemplaren heeft.
Laten we nu eens kijken hoe het dat doet.
Bekijk eerst twee bewerkingen die kunnen worden gebruikt om toegang te krijgen tot de waarde van de semafoorvariabele en deze te wijzigen.

Enkele punten met betrekking tot P- en V-bediening:
- P-bediening wordt ook wel wacht-, slaap- of down-bediening genoemd, en V-bediening wordt ook signaal-, wek- of op-bediening genoemd.
- Beide bewerkingen zijn atomair en semafoor(en) worden altijd op één geïnitialiseerd. Hier betekent atomair die variabele waarop lezen, wijzigen en bijwerken op hetzelfde tijdstip/moment plaatsvindt zonder voorrang, dat wil zeggen dat er tussen het lezen, wijzigen en bijwerken geen andere bewerking wordt uitgevoerd die de variabele kan veranderen.
- Een kritieke sectie wordt omgeven door beide bewerkingen om processynchronisatie te implementeren. Zie onderstaande afbeelding. Het kritieke gedeelte van proces P bevindt zich tussen de P- en V-werking.

Laten we nu eens kijken hoe dit wederzijdse uitsluiting implementeert. Stel dat er twee processen P1 en P2 zijn en een semafoor s wordt geïnitialiseerd als 1. Stel nu dat P1 zijn kritieke sectie binnengaat, dan wordt de waarde van semafoor s 0. Als P2 nu zijn kritieke sectie wil betreden, zal hij wachten tot s> 0, dit kan alleen gebeuren als P1 zijn kritieke sectie beëindigt en de V-bewerking op semafoor s aanroept.
Op deze manier wordt wederzijdse uitsluiting bereikt. Kijk naar de onderstaande afbeelding voor details. Dit is een binaire semafoor.

Implementatie: Binaire semaforen
struct semaphore { enum value(0, 1); // q contains all Process Control Blocks (PCBs) // corresponding to processes got blocked // while performing down operation. QueueQ; }; P(semafoor s) { if (s.waarde == 1) { s.waarde = 0; } else {// voeg het proces toe aan de wachtrij q.push(P) sleep(); } } V(semafoor s) { if (s.q is leeg) { s.waarde = 1; } else {// selecteer een proces uit de wachtrij Process p = q.front(); // verwijder het wachten zoals het is verzonden // verzonden voor CS q.pop(); word wakker); } } // Deze code is aangepast door Susobhan Akhuli> C #include #include #include struct semaphore{ QueueQ; int-waarde; }; void P(struct semafoor s) { if (s.value == 1) { s.value = 0; } anders { s.q.push(P); slaap(); } } void V(semafoor s) { if (s.q is leeg) { s.value = 1; } else {// Haal een proces op uit het wachtrijproces p = q.front(); // Verwijder het wachtende proces q.pop(); word wakker); } } int main() { printf('Dit is Hemish!!'); // Deze code is bijgedragen door Himesh Singh Chauhan return 0; } // Deze code is gewijzigd door Susobhan Akhuli> Java import java.util.*; class Semaphore { public enum Value { Zero, One } public Queueq = nieuwe LinkedList(); openbare waardewaarde = Value.One; public void P(Semafoor s, Proces p) { if (s.value == Value.One) { s.value = Value.Zero; } else {// voeg het proces toe aan de wachtrij q.add(p); p.Slaap(); } } public void V(Semafoor s) { if (s.q.size() == 0) { s.value = Value.One; } else {// selecteer een proces uit de wachtrij Process p = q.peek(); // zorg ervoor dat het proces niet meer wacht zoals het is // verzonden voor 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 = nieuwe wachtrij(); public void P(Semafoor s, Proces p) { if (s.value == waarde.One) { s.value = waarde.Zero; } else {// voeg het proces toe aan de wachtrij q.Enqueue(p); p.Slaap(); } } public void V(Semafoor s) { if (s.q.Count == 0) { s.value = waarde.Een; } else {// selecteer een proces uit de wachtrij Process p = q.Peek(); // verwijder het wachten zoals het is verzonden // verzonden voor 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); } } }> De bovenstaande beschrijving geldt voor een binaire semafoor die slechts twee waarden 0 en 1 kan aannemen en wederzijdse uitsluiting garandeert. Er is nog een ander type semafoor, de tel-semafoor genaamd, die waarden groter dan één kan aannemen.
Stel nu dat er een bron is waarvan het aantal instanties 4 is. Nu initialiseren we S = 4 en de rest is hetzelfde als voor binaire semafoor. Telkens wanneer het proces die hulpbron wil, roept het P aan of wacht op de functie en wanneer het klaar is, roept het V of de signaalfunctie aan. Als de waarde van S nul wordt, moet een proces wachten totdat S positief wordt. Stel dat er bijvoorbeeld 4 processen P1, P2, P3, P4 zijn, en deze roepen allemaal de wachtoperatie op S aan (geïnitialiseerd met 4). Als een ander proces P5 de hulpbron wil, moet het wachten totdat een van de vier processen de signaalfunctie aanroept en de waarde van semafoor positief wordt.
Beperkingen:
inurl:.git/head
- Een van de grootste beperkingen van semafoor is prioriteitsomkering.
- Impasse: stel dat een proces probeert een ander proces wakker te maken dat zich niet in de slaapstand bevindt. Daarom kan een impasse voor onbepaalde tijd blokkeren.
- Het besturingssysteem moet alle oproepen bijhouden om te wachten en de semafoor te signaleren.
Probleem bij deze implementatie van een semafoor:
Het grootste probleem met semaforen is dat er druk moet worden gewacht. Als een proces zich in de kritieke sectie bevindt, zullen andere processen die de kritieke sectie proberen binnen te gaan, wachten totdat de kritieke sectie niet meer door een proces wordt bezet. Telkens wanneer een proces wacht, controleert het voortdurend op de semafoorwaarde (kijk naar deze regel terwijl (s==0); in P-bewerking) en verspilt de CPU-cyclus.
Er is ook een kans op spinlock, omdat de processen blijven draaien terwijl ze wachten op de vergrendeling. Om dit te voorkomen wordt hieronder een andere implementatie gegeven.
Implementatie: Semafoor tellen
CPP struct Semaphore { int value; // q contains all Process Control Blocks(PCBs) // corresponding to processes got blocked // while performing down operation. QueueQ; }; P(Semafoor s) { s.waarde = s.waarde - 1; als (s.waarde< 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; openbare semafoor (int-waarde) {this.value = waarde; q = nieuwe LinkedList(); } publieke leegte P(Proces p) {waarde--; als (waarde< 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 = nieuwe wachtrij(); publieke leegte P(Proces p) {waarde--; als (waarde< 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> In deze implementatie wordt het proces, wanneer het wacht, toegevoegd aan een wachtrij van processen die bij die semafoor horen. Dit wordt gedaan via de systeemaanroep block() in dat proces. Wanneer een proces is voltooid, roept het de signaalfunctie op en wordt één proces in de wachtrij hervat. Het maakt gebruik van de wakeup() systeemaanroep.
Voordelen van seinpalen:
- Een eenvoudig en effectief mechanisme voor processynchronisatie
- Ondersteunt de coördinatie tussen meerdere processen
- Biedt een flexibele en robuuste manier om gedeelde bronnen te beheren.
- Het kan worden gebruikt om kritische secties in een programma te implementeren.
- Het kan worden gebruikt om raceomstandigheden te vermijden.
Nadelen van seinpalen:
- Dit kan leiden tot prestatievermindering als gevolg van overhead die verband houdt met wacht- en signaalbewerkingen.
- Kan bij verkeerd gebruik tot een impasse leiden.
- Het werd in 1965 door Dijkstra voorgesteld, wat een zeer belangrijke techniek is om gelijktijdige processen te beheren door een eenvoudige gehele waarde te gebruiken, die bekend staat als een semafoor. Een semafoor is eenvoudigweg een geheel getalvariabele die tussen threads wordt gedeeld. Deze variabele wordt gebruikt om het kritieke sectieprobleem op te lossen en om processynchronisatie te bereiken in de multiprocessing-omgeving.
- Het kan prestatieproblemen in een programma veroorzaken als het niet correct wordt gebruikt.
- Het kan moeilijk zijn om fouten te debuggen en te onderhouden.
- Als het niet correct wordt gebruikt, kan het gevoelig zijn voor raceomstandigheden en andere synchronisatieproblemen.
- Het kan kwetsbaar zijn voor bepaalde soorten aanvallen, zoals denial-of-service-aanvallen.