Semaforer är bara normala variabler som används för att koordinera aktiviteterna för flera processer i ett datorsystem. De används för att framtvinga ömsesidig uteslutning, undvika rasförhållanden och implementera synkronisering mellan processer.
Processen att använda Semaforer ger två operationer: vänta (P) och signal (V). Vänteoperationen minskar värdet på semaforen, och signaloperationen ökar värdet på semaforen. När värdet på semaforen är noll, kommer varje process som utför en vänteoperation att blockeras tills en annan process utför en signaloperation.
Semaforer används för att implementera kritiska sektioner, som är kodregioner som endast måste exekveras av en process åt gången. Genom att använda semaforer kan processer koordinera åtkomst till delade resurser, såsom delat minne eller I/O-enheter.
En semafor är en speciell typ av synkroniseringsdata som endast kan användas genom specifika synkroniseringsprimitiver. När en process utför en vänteoperation på en semafor, kontrollerar operationen om värdet på semaforen är>0. Om så är fallet, minskar den värdet på semaforen och låter processen fortsätta sin exekvering; annars blockerar det processen på semaforen. En signaloperation på en semafor aktiverar en process som blockeras på semaforen om någon, eller ökar värdet på semaforen med 1. På grund av denna semantik kallas semaforer också för räkne semaforer. Det initiala värdet för en semafor bestämmer hur många processer som kan komma förbi vänteoperationen.
Semaforer är av två typer:
- Binär semafor –
Detta är också känt som ett mutex-lås. Den kan bara ha två värden – 0 och 1. Dess värde initieras till 1. Den används för att implementera lösningen av kritiska sektionsproblem med flera processer. - Räkna semafor –
Dess värde kan sträcka sig över en obegränsad domän. Den används för att kontrollera åtkomst till en resurs som har flera instanser.
Låt oss nu se hur det gör det.
Titta först på två operationer som kan användas för att komma åt och ändra värdet på semaforvariabeln.

Några punkter angående P- och V-drift:
- P-drift kallas också för vänta-, vilo- eller neddrift, och V-drift kallas också för signal-, väcknings- eller upp-operation.
- Båda operationerna är atomära och semafor(er) initieras alltid till en. Här betyder atom den variabeln på vilken läsning, modifiering och uppdatering sker samtidigt/ögonblick utan förköp, dvs. emellan läsning, modifiering och uppdatering utförs ingen annan operation som kan ändra variabeln.
- En kritisk sektion omges av båda operationerna för att implementera processsynkronisering. Se bilden nedan. Den kritiska delen av Process P är mellan P- och V-drift.

Låt oss nu se hur det implementerar ömsesidig uteslutning. Låt det finnas två processer P1 och P2 och en semafor s initieras som 1. Om nu antar att P1 går in i sin kritiska sektion så blir värdet på semafor s 0. Om P2 nu vill gå in i sin kritiska sektion kommer den att vänta tills s> 0, detta kan bara hända när P1 avslutar sin kritiska sektion och anropar V-operation på semafor s.
På så sätt uppnås ömsesidig utslagning. Titta på bilden nedan för detaljer som är en binär semafor.

Genomförande: Binära semaforer
struct semaphore { enum value(0, 1); // q contains all Process Control Blocks (PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; }; P(semafor s) { if (s.värde == 1) { s.värde = 0; } else { // lägg till processen i väntekön q.push(P) sleep(); } } V(semafor s) { if (s.q är tom) { s.value = 1; } else { // välj en process från väntande kö Process p = q.front(); // ta bort processen från att vänta eftersom den har blivit // skickad för CS q.pop(); vakna(p); } } // Denna kod är modifierad av Susobhan Akhuli> C #include #include #include struct semaphore{ Queueq; int värde; }; void P(struct semafor s) { if (s.value == 1) { s.value = 0; } annat { s.q.push(P); sova(); } } void V(semafor s) { if (s.q är tom) { s.value = 1; } else { // Hämta en process från Waiting Queue Process p = q.front(); // Ta bort processen från att vänta q.pop(); väckning(p); } } int main() { printf('Detta är Hemish!!'); // Denna kod är bidragit av Himesh Singh Chauhan retur 0; } // Denna kod är modifierad av Susobhan Akhuli> Java import java.util.*; class Semaphore { public enum Value { Zero, One } public Queueq = ny länkad lista(); public Value value = Value.One; public void P(Semaphore s, Process p) { if (s.value == Value.One) { s.value = Value.Noll; } else { // lägg till processen i väntekön q.add(p); p.Sömn(); } } public void V(Semaphore s) { if (s.q.size() == 0) { s.value = Value.One; } else { // välj en process från väntande kö Process p = q.peek(); // ta bort processen från att vänta eftersom den har // skickats för 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 = ny kö(); public void P(Semaphore s, Process p) { if (s.value == value.One) { s.value = value.Noll; } else { // lägg till processen i väntekön q.Enqueue(p); p.Sömn(); } } public void V(Semaphore s) { if (s.q.Count == 0) { s.value = value.One; } else { // välj en process från väntande kö Process p = q.Peek(); // ta bort processen från att vänta eftersom den har blivit // skickad för 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); } } }> Beskrivningen ovan är för binär semafor som bara kan ta två värden 0 och 1 och säkerställa ömsesidig uteslutning. Det finns en annan typ av semafor som kallas räkne semafor som kan ta värden större än ett.
typer av maskininlärning
Anta nu att det finns en resurs vars antal instanser är 4. Nu initialiserar vi S = 4 och resten är samma som för binär semafor. Närhelst processen vill ha den resursen anropar den P eller väntar på funktion och när den är klar anropar den V eller signalfunktion. Om värdet på S blir noll måste en process vänta tills S blir positivt. Anta till exempel att det finns 4 processer P1, P2, P3, P4, och alla anropar vänta-operation på S (initierad med 4). Om en annan process P5 vill ha resursen bör den vänta tills en av de fyra processerna anropar signalfunktionen och värdet på semaforen blir positivt.
Begränsningar:
- En av semaforens största begränsningar är prioritetsinversion.
- Dödläge, anta att en process försöker väcka en annan process som inte är i viloläge. Därför kan ett dödläge blockera på obestämd tid.
- Operativsystemet måste hålla reda på alla samtal för att vänta och signalera semaforen.
Problem i denna implementering av en semafor:
Det största problemet med semaforer är att de kräver upptagen väntan. Om en process är i den kritiska sektionen, kommer andra processer som försöker komma in i den kritiska sektionen att vänta tills den kritiska sektionen inte är upptagen av någon process. Närhelst någon process väntar kontrollerar den kontinuerligt efter semaforvärde (titta på den här raden medan (s==0); i P-drift) och slöseri med CPU-cykeln.
Det finns också en chans för spinlock eftersom processerna fortsätter att snurra medan de väntar på låset. För att undvika detta ges en annan implementering nedan.
Genomförande: Räkna semafor
CPP struct Semaphore { int value; // q contains all Process Control Blocks(PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; }; P(Semaphore s) { s.value = s.value - 1; om (s.värde< 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 Semaphore(int value) { this.value = värde; q = new LinkedList(); } public void P(Process p) { värde--; om (värde< 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 = ny kö(); public void P(Process p) { värde--; om (värde< 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> I denna implementering, närhelst processen väntar, läggs den till en väntande kö av processer associerade med den semaforen. Detta görs genom systemanropet block() på den processen. När en process är klar anropar den signalfunktionen och en process i kön återupptas. Den använder systemanropet wakeup().
Fördelar med semaforer:
- En enkel och effektiv mekanism för processsynkronisering
- Stöder samordning mellan flera processer
- Ger ett flexibelt och robust sätt att hantera delade resurser.
- Den kan användas för att implementera kritiska avsnitt i ett program.
- Den kan användas för att undvika tävlingsförhållanden.
Nackdelar med semaforer:
- Det kan leda till prestandaförsämring på grund av overhead i samband med väntan och signaloperationer.
- Kan resultera i dödläge om det används felaktigt.
- Det föreslogs av Dijkstra 1965 vilket är en mycket betydelsefull teknik för att hantera samtidiga processer genom att använda ett enkelt heltalsvärde, som är känt som en semafor. En semafor är helt enkelt en heltalsvariabel som delas mellan trådar. Denna variabel används för att lösa det kritiska avsnittsproblemet och för att uppnå processsynkronisering i multiprocessingmiljön.
- Det kan orsaka prestandaproblem i ett program om det inte används på rätt sätt.
- Det kan vara svårt att felsöka och underhålla.
- Den kan vara utsatt för tävlingsförhållanden och andra synkroniseringsproblem om den inte används på rätt sätt.
- Det kan vara sårbart för vissa typer av attacker, till exempel överbelastningsattacker.