Semaforai yra tik įprasti kintamieji, naudojami kelių procesų veiklai kompiuterinėje sistemoje koordinuoti. Jie naudojami siekiant užtikrinti abipusę atskirtį, išvengti lenktynių sąlygų ir įgyvendinti procesų sinchronizavimą.
Semaforų naudojimo procese numatytos dvi operacijos: laukimas (P) ir signalas (V). Laukimo operacija sumažina semaforo vertę, o signalo operacija padidina semaforo reikšmę. Kai semaforo reikšmė lygi nuliui, bet koks procesas, atliekantis laukimo operaciją, bus blokuojamas, kol kitas procesas neatliks signalo operacijos.
Semaforai naudojami kritinėms sekcijoms, kurios yra kodo sritys, kurias vienu metu turi vykdyti tik vienas procesas, įgyvendinti. Naudodami semaforus procesai gali koordinuoti prieigą prie bendrinamų išteklių, pvz., bendrinamos atminties ar įvesties/išvesties įrenginių.
Semaforas yra speciali sinchronizavimo duomenų rūšis, kurią galima naudoti tik naudojant tam tikrus sinchronizacijos primityvus. Kai procesas atlieka laukimo operaciją semafore, operacija patikrina, ar semaforo reikšmė yra>0. Jei taip, jis sumažina semaforo vertę ir leidžia procesui tęsti jo vykdymą; kitu atveju jis blokuoja procesą semafore. Signalo operacija semafore suaktyvina procesą, užblokuotą semafore, jei toks yra, arba padidina semaforo reikšmę 1. Dėl šios semaforijos semaforai dar vadinami skaičiavimo semaforais. Pradinė semaforo reikšmė nustato, kiek procesų gali praeiti laukimo operacija.
Semaforai yra dviejų tipų:
- Dvejetainis semaforas –
Tai taip pat žinoma kaip mutex užraktas. Jis gali turėti tik dvi reikšmes – 0 ir 1. Jo reikšmė inicijuojama į 1. Naudojama įgyvendinant kritinių sekcijų uždavinius su keliais procesais. - Semaforo skaičiavimas -
Jo vertė gali svyruoti neribotame domene. Jis naudojamas valdyti prieigą prie išteklių, turinčių kelis egzempliorius.
Dabar pažiūrėkime, kaip tai daroma.
Pirmiausia pažvelkite į dvi operacijas, kurias galima naudoti norint pasiekti ir pakeisti semaforo kintamojo reikšmę.
Kai kurie punktai, susiję su P ir V veikimu:
- P operacija taip pat vadinama laukimo, miego arba žemyn operacija, o V operacija taip pat vadinama signalo, pažadinimo arba kėlimo operacija.
- Abi operacijos yra atominės, o semaforas (-ai) visada inicijuojamas (-i) į vieną. Čia atominis reiškia tą kintamąjį, kurio skaitymas, modifikavimas ir atnaujinimas vyksta tuo pačiu metu/akimirką be išankstinio pasirinkimo, ty tarp skaitymo, modifikavimo ir atnaujinimo neatliekama jokia kita operacija, galinti pakeisti kintamąjį.
- Kritinę dalį supa abi operacijos, skirtos proceso sinchronizavimui. Žiūrėkite žemiau esantį paveikslėlį. Kritinė proceso P dalis yra tarp P ir V veikimo.
Dabar pažiūrėkime, kaip ji įgyvendina abipusę atskirtį. Tegul yra du procesai P1 ir P2, o semaforas s inicijuojamas kaip 1. Tarkime, kad P1 patenka į savo kritinę sekciją, tada semaforo s reikšmė tampa 0. Jei P2 nori įeiti į savo kritinę sekciją, jis lauks, kol s> 0, tai gali atsitikti tik tada, kai P1 baigia savo kritinę atkarpą ir iškviečia V operaciją semafore s.
Taip pasiekiama abipusė atskirtis. Norėdami gauti daugiau informacijos, žiūrėkite žemiau esantį paveikslėlį, kuris yra dvejetainis semaforas.
Įgyvendinimas: Dvejetainiai semaforai
struct semaphore { enum value(0, 1); // q contains all Process Control Blocks (PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; }; P(semaforo s) { if (s.value == 1) { s.value = 0; } else { // įtraukti procesą į laukiančią eilę q.push(P) sleep(); } } V(semaforas s) { if (s.q tuščias) { s.reikšmė = 1; } else { // pasirinkite procesą iš laukiančios eilės Procesas p = q.front(); // pašalinkite procesą iš laukimo, nes jis buvo išsiųstas // CS q.pop(); pabudimas(p); } } // Šį kodą modifikavo Susobhan Akhuli>
C #include #include #include struct semaphore{ Queueq; int vertė; }; void P(struktūrinis semaforas s) { if (s.value == 1) { s.value = 0; } else { s.q.push(P); miegas (); } } void V(semaforas s) { if (s.q tuščias) { s.value = 1; } else { // Gauti procesą iš laukimo eilės Process p = q.front(); // Pašalinti procesą iš laukimo q.pop(); pabudimas(p); } } int main() { printf('Tai yra hemish!!'); // Prie šio kodo prisidėjo Himesh Singh Chauhan return 0; } // Šį kodą modifikavo Susobhan Akhuli>
Java import java.util.*; class Semaphore { public enum Value { Zero, One } public Queueq = naujas LinkedList(); public Value value = Value.One; public void P(Semaforas s, Procesas p) { if (s.value == Value.One) { s.value = Value.Zero; } else { // įtraukti procesą į laukiančią eilę q.add(p); p.Miegas(); } } public void V(semaforas s) { if (s.q.size() == 0) { s.value = Value.One; } else { // pasirinkite procesą iš laukiančios eilės Process p = q.peek(); // pašalinkite procesą iš laukimo, nes // jis buvo išsiųstas CS q.remove(); p.Pabudimas(); } } }>
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 = nauja eilė(); public void P(Semaforas s, Procesas p) { if (s.value == value.One) { s.value = value.Zero; } else { // įtraukti procesą į laukiančią eilę q.Enqueue(p); p.Miegas(); } } public void V(Semaforas s) { if (s.q.Count == 0) { s.value = value.One; } else { // pasirinkite procesą iš laukiančios eilės Process p = q.Peek(); // pašalinkite procesą iš laukimo, nes jis buvo išsiųstas CS q.Dequeue(); p.Pabudimas(); } } }>
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); } } }>
Aukščiau pateiktas aprašymas skirtas dvejetainiam semaforui, kuris gali turėti tik dvi reikšmes 0 ir 1 ir užtikrinti abipusį pašalinimą. Yra dar vienas semaforo tipas, vadinamas skaičiavimo semaforu, kurio reikšmės gali būti didesnės nei viena.
Dabar tarkime, kad yra išteklių, kurių egzempliorių skaičius yra 4. Dabar inicijuojame S = 4, o likusi dalis yra tokia pati kaip dvejetainio semaforo atveju. Kai tik procesas nori to resurso, jis iškviečia P arba laukia funkcijos, o baigęs – iškviečia V arba signalo funkciją. Jei S reikšmė tampa nuliu, procesas turi laukti, kol S taps teigiamas. Pavyzdžiui, tarkime, kad yra 4 procesai P1, P2, P3, P4 ir visi jie iškviečia laukimo operaciją S (inicijuota 4). Jei resurso nori kitas procesas P5, jis turėtų palaukti, kol vienas iš keturių procesų iškvies signalo funkciją ir semaforo reikšmė taps teigiama.
Apribojimai:
jquery spustelėjus
- Vienas iš didžiausių semaforo apribojimų yra prioritetinė inversija.
- Aklavietė, tarkime, kad procesas bando pažadinti kitą procesą, kuris nėra miego būsenoje. Todėl aklavietė gali užsiblokuoti neribotą laiką.
- Operacinė sistema turi sekti visus skambučius laukti ir signalizuoti semaforą.
Šio semaforo įgyvendinimo problema:
Pagrindinė semaforų problema yra ta, kad jiems reikia labai daug laukti. Jei procesas yra kritinėje sekcijoje, kiti procesai, bandantys patekti į kritinę sekciją, lauks, kol kritinės dalies neužims joks procesas. Kai koks nors procesas laukia, jis nuolat tikrina semaforo vertę (pažiūrėkite į šią eilutę, kol (s = = 0); P operacijoje) ir eikvoja procesoriaus ciklą.
Taip pat yra sukimosi galimybė, nes procesai sukasi laukdami užrakto. Siekiant to išvengti, toliau pateikiamas kitas įgyvendinimas.
Įgyvendinimas: Skaičiavimo semaforas
CPP struct Semaphore { int value; // q contains all Process Control Blocks(PCBs) // corresponding to processes got blocked // while performing down operation. Queueq; }; P(semaforo s) { s.value = s.value - 1; if (s.value< 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 Semaforas(int reikšmė) { this.value = vertė; q = naujas LinkedList(); } public void P(Procesas p) { reikšmė--; jei (vertė< 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 = nauja eilė(); public void P(Process p) { vertė--; jei (vertė< 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>
Šiame įgyvendinime, kai procesas laukia, jis įtraukiamas į laukiančių procesų, susietų su tuo semaforu, eilę. Tai atliekama naudojant sistemos iškvietimo bloką () šiame procese. Kai procesas baigiamas, jis iškviečia signalo funkciją ir vienas procesas eilėje atnaujinamas. Jis naudoja sistemos skambutį wakeup().
Semaforų pranašumai:
- Paprastas ir efektyvus procesų sinchronizavimo mechanizmas
- Palaiko kelių procesų koordinavimą
- Tai lankstus ir patikimas būdas valdyti bendrinamus išteklius.
- Jis gali būti naudojamas įgyvendinant svarbias programos dalis.
- Jis gali būti naudojamas norint išvengti lenktynių sąlygų.
Semaforų trūkumai:
- Tai gali pabloginti našumą dėl papildomų išlaidų, susijusių su laukimo ir signalo operacijomis.
- Jei naudojamas neteisingai, gali atsirasti aklavietė.
- Ją Dijkstra pasiūlė 1965 m., o tai yra labai reikšmingas būdas valdyti tuo pačius procesus naudojant paprastą sveikojo skaičiaus reikšmę, kuri yra žinoma kaip semaforas. Semaforas yra tiesiog sveikasis kintamasis, kuris dalijamas tarp gijų. Šis kintamasis naudojamas norint išspręsti kritinės sekcijos problemą ir pasiekti proceso sinchronizavimą kelių apdorojimo aplinkoje.
- Jei netinkamai naudojama, tai gali sukelti programos veikimo problemų.
- Gali būti sunku derinti ir prižiūrėti.
- Jei naudojamas netinkamai, gali kilti lenktynių sąlygos ir kitos sinchronizacijos problemos.
- Jis gali būti pažeidžiamas tam tikrų tipų atakoms, pvz., paslaugų atsisakymo atakoms.