logo

Semaforji v sinhronizaciji procesov

Semaforji so samo običajne spremenljivke, ki se uporabljajo za usklajevanje dejavnosti več procesov v računalniškem sistemu. Uporabljajo se za uveljavljanje medsebojnega izključevanja, izogibanje tekmovalnim pogojem in izvajanje sinhronizacije med procesi.

Postopek uporabe semaforjev omogoča dve operaciji: čakanje (P) in signal (V). Operacija čakanja zmanjša vrednost semaforja, operacija signala pa poveča vrednost semaforja. Ko je vrednost semaforja enaka nič, bo vsak proces, ki izvede operacijo čakanja, blokiran, dokler drug proces ne izvede operacije signala.



Semaforji se uporabljajo za implementacijo kritičnih odsekov, ki so področja kode, ki jih mora izvajati samo en proces naenkrat. Z uporabo semaforjev lahko procesi koordinirajo dostop do skupnih virov, kot so skupni pomnilnik ali V/I naprave.

Semafor je posebna vrsta sinhronizacijskih podatkov, ki jih je mogoče uporabiti samo prek posebnih sinhronizacijskih primitivov. Ko proces izvede operacijo čakanja na semaforju, operacija preveri, ali je vrednost semaforja>0. Če je tako, zmanjša vrednost semaforja in pusti procesu nadaljevanje izvajanja; v nasprotnem primeru blokira proces na semaforju. Signalna operacija na semaforju aktivira proces, ki je blokiran na semaforju, če obstaja, ali poveča vrednost semaforja za 1. Zaradi te semantike se semaforje imenujejo tudi semaforji za štetje. Začetna vrednost semaforja določa, koliko procesov lahko preseže operacijo čakanja.

Semaforji so dveh vrst:



python rstrip
  1. Binarni semafor –
    To je znano tudi kot zaklepanje mutexa. Ima lahko samo dve vrednosti – 0 in 1. Njegova vrednost je inicializirana na 1. Uporablja se za implementacijo rešitve težav kritičnih odsekov z več procesi.
  2. Semafor za štetje –
    Njegova vrednost se lahko giblje v neomejeni domeni. Uporablja se za nadzor dostopa do vira, ki ima več primerkov.

Zdaj pa poglejmo, kako to počne.

Najprej si oglejte dve operaciji, ki ju je mogoče uporabiti za dostop in spreminjanje vrednosti spremenljivke semaforja.

P-in-V-operacija-v-OS



Nekaj ​​točk glede delovanja P in V:

  1. Operacija P se imenuje tudi operacija čakanja, spanja ali izklopa, operacija V pa se imenuje tudi operacija signala, bujenja ali delovanja.
  2. Obe operaciji sta atomski in semafor(i) se vedno inicializira na eno. Tu atomarno pomeni tisto spremenljivko, na kateri se branje, spreminjanje in posodabljanje zgodi istočasno/trenutek brez predhodne pravice, tj. med branjem, spreminjanjem in posodabljanjem se ne izvede nobena druga operacija, ki bi lahko spremenila spremenljivko.
  3. Kritični del je obdan z obema operacijama za izvedbo sinhronizacije procesa. Oglejte si spodnjo sliko. Kritični odsek procesa P je med operacijama P in V.

Zdaj pa poglejmo, kako izvaja medsebojno izključevanje. Naj obstajata dva procesa P1 in P2 in semafor s je inicializiran kot 1. Zdaj, če predpostavimo, da P1 vstopi v svoj kritični odsek, postane vrednost semaforja s 0. Zdaj, če P2 želi vstopiti v svoj kritični odsek, bo počakal, dokler s> 0, se to lahko zgodi le, ko P1 konča svoj kritični odsek in pokliče operacijo V na semaforju s.

Na ta način se doseže medsebojna izključitev. Oglejte si spodnjo sliko za podrobnosti, ki je binarni semafor.


Izvedba: Binarni semaforji

C++
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.value == 1) { s.value = 0;  } else { // dodaj proces v čakalno vrsto q.push(P) sleep();  } } V(semafor s) { if (s.q je prazen) { s.value = 1;  } else { // izberite proces iz čakalne vrste Proces p = q.front();  // odstrani proces iz čakanja, saj je bil // poslan za CS q.pop();  prebujanje (p);  } } // To kodo je spremenil Susobhan Akhuli>
C
#include  #include #include struct semaphore{  Queueq;  int vrednost; }; void P(struct semaphore s) { if (s.value == 1) { s.value = 0;  } else { s.q.push(P);   spanje();  } } void V(semafor s) { if (s.q je prazen) { s.value = 1;  } else { // Pridobite proces iz čakalne vrste Proces p = q.front();  // Odstrani proces iz čakanja q.pop();  prebujanje (p);  } } int main() { printf('To je Hemish!!');    // To kodo je prispeval Himesh Singh Chauhan return 0; } // To kodo je spremenil Susobhan Akhuli>
Java
import java.util.*; class Semaphore {  public enum Value { Zero, One }  public Queueq = nov LinkedList();  javna vrednost value = Value.One;  public void P(Semaphore s, Process p) { if (s.value == Value.One) { s.value = Value.Zero;  } else { // dodaj proces v čakalno vrsto q.add(p);  p.Spanje();  } } public void V(Semaphore s) { if (s.q.size() == 0) { s.value = Value.One;  } else { // izberi proces iz čakalne vrste Proces p = q.peek();  // odstrani proces iz čakanja, ker je bil // poslan za 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 = nova čakalna vrsta();  public void P(Semaphore s, Proces p) { if (s.value == value.One) { s.value = value.Zero;  } else { // dodaj proces v čakalno vrsto q.Enqueue(p);  p.Spanje();  } } public void V(Semaphore s) { if (s.q.Count == 0) { s.value = value.One;  } else { // izberi proces iz čakalne vrste Proces p = q.Peek();  // odstranite proces iz čakanja, saj je bil // poslan za 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);  }  } }>

Zgornji opis je za binarni semafor, ki lahko sprejme samo dve vrednosti 0 in 1 in zagotavlja medsebojno izključitev. Obstaja še en tip semaforja, imenovan semafor za štetje, ki lahko sprejme vrednosti, večje od ena.

Zdaj pa predpostavimo, da obstaja vir, katerega število primerkov je 4. Zdaj inicializiramo S = 4, ostalo pa je enako kot za binarni semafor. Kadarkoli proces želi ta vir, pokliče P ali čaka na funkcijo in ko je končan, pokliče V ali signalno funkcijo. Če vrednost S postane nič, mora proces počakati, dokler S ne postane pozitiven. Recimo, da obstajajo 4 procesi P1, P2, P3, P4 in vsi pokličejo operacijo čakanja na S (inicializirano s 4). Če drug proces P5 želi vir, mora počakati, da eden od štirih procesov pokliče signalno funkcijo in vrednost semaforja postane pozitivna.

Omejitve:

  1. Ena največjih omejitev semaforja je prednostna inverzija.
  2. Zastoj, recimo, da proces poskuša prebuditi drug proces, ki ni v stanju spanja. Zato lahko zastoj blokira za nedoločen čas.
  3. Operacijski sistem mora spremljati vse klice, da počaka in signalizira semafor.

Težava v tej izvedbi semaforja:

Glavna težava s semaforji je, da zahtevajo zasedeno čakanje. Če je proces v kritičnem odseku, bodo drugi procesi, ki poskušajo vstopiti v kritični odsek, čakali, dokler kritičnega odseka ne bo zasedel noben proces. Kadarkoli kateri koli proces čaka, nenehno preverja vrednost semaforja (poglejte to vrstico medtem ko (s==0); v operaciji P) in zapravlja cikel CPE.

Obstaja tudi možnost vrtenja, saj se procesi med čakanjem na zaklepanje še naprej vrtijo. Da bi se temu izognili, je spodaj na voljo druga izvedba.

Izvedba: Semafor za štetje

CPP
struct Semaphore {  int value;  // q contains all Process Control Blocks(PCBs)  // corresponding to processes got blocked  // while performing down operation.  Queueq; }; P(Semafor s) { s.vrednost = s.vrednost - 1;  če (s.vrednost< 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 = value;  q = nov LinkedList();  } public void P(Process p) { value--;  če (vrednost< 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 = nova čakalna vrsta();  public void P(Process p) { value--;  če (vrednost< 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>

V tej izvedbi je vsakič, ko proces čaka, dodan v čakalno vrsto procesov, povezanih s tem semaforjem. To se naredi prek sistemskega klica block() v tem procesu. Ko je proces končan, pokliče signalno funkcijo in en proces v čakalni vrsti se nadaljuje. Uporablja sistemski klic wakeup().

spremeni ime imenika linux

Prednosti semaforjev:

  • Preprost in učinkovit mehanizem za sinhronizacijo procesov
  • Podpira koordinacijo med več procesi
  • Zagotavlja prilagodljiv in robusten način za upravljanje skupnih virov.
  • Uporablja se lahko za izvajanje kritičnih odsekov v programu.
  • Uporablja se lahko za izogibanje tekmovalnim pogojem.

Slabosti semaforjev:

  • Lahko povzroči poslabšanje zmogljivosti zaradi stroškov, povezanih s čakanjem in signalnimi operacijami.
  • Če se nepravilno uporablja, lahko pride do zastoja.
  • Leta 1965 jo je predlagal Dijkstra, kar je zelo pomembna tehnika za upravljanje sočasnih procesov z uporabo preproste celoštevilske vrednosti, ki je znana kot semafor. Semafor je preprosto celoštevilska spremenljivka, ki si jo delijo niti. Ta spremenljivka se uporablja za reševanje problema kritičnega odseka in za doseganje sinhronizacije procesa v večprocesnem okolju.
  • Če se ne uporablja pravilno, lahko povzroči težave pri delovanju programa.
  • Težko ga je odpraviti in vzdrževati.
  • Če se ne uporablja pravilno, je lahko nagnjen k tekmovalnim pogojem in drugim težavam s sinhronizacijo.
  • Lahko je ranljiv za določene vrste napadov, kot so napadi z zavrnitvijo storitve.