logo

Semafore în sincronizarea proceselor

Semaforele sunt doar variabile normale folosite pentru a coordona activitățile mai multor procese dintr-un sistem informatic. Sunt folosite pentru a impune excluderea reciprocă, pentru a evita condițiile de cursă și pentru a implementa sincronizarea între procese.

Procesul de utilizare a Semaforelor oferă două operații: așteptare (P) și semnal (V). Operația de așteptare decrește valoarea semaforului, iar operația de semnal crește valoarea semaforului. Când valoarea semaforului este zero, orice proces care efectuează o operație de așteptare va fi blocat până când un alt proces efectuează o operație de semnal.



Semaforele sunt folosite pentru a implementa secțiuni critice, care sunt regiuni de cod care trebuie executate de un singur proces la un moment dat. Prin utilizarea semaforelor, procesele pot coordona accesul la resursele partajate, cum ar fi memoria partajată sau dispozitivele I/O.

Un semafor este un tip special de date de sincronizare care poate fi utilizat numai prin primitive de sincronizare specifice. Când un proces efectuează o operație de așteptare pe un semafor, operația verifică dacă valoarea semaforului este>0. Dacă da, scade valoarea semaforului și lasă procesul să-și continue execuția; în caz contrar, blochează procesul pe semafor. O operațiune de semnal pe un semafor activează un proces blocat pe semafor, dacă există, sau crește valoarea semaforului cu 1. Datorită acestor semantici, semaforele sunt numite și semafore de numărare. Valoarea inițială a unui semafor determină câte procese pot trece peste operația de așteptare.

Semaforele sunt de două tipuri:



  1. Semafor binar -
    Acest lucru este cunoscut și sub numele de blocare mutex. Poate avea doar două valori – 0 și 1. Valoarea sa este inițializată la 1. Este folosit pentru a implementa soluția problemelor secțiunilor critice cu procese multiple.
  2. Semafor de numărare -
    Valoarea sa poate varia pe un domeniu nerestricționat. Este folosit pentru a controla accesul la o resursă care are mai multe instanțe.

Acum hai să vedem cum face asta.

În primul rând, uitați-vă la două operațiuni care pot fi utilizate pentru a accesa și modifica valoarea variabilei semaforului.

cate saptamani pe luna

Operare P-și-V-în-OS



Câteva puncte privind funcționarea P și V:

  1. Operația P se mai numește și operațiune de așteptare, repaus sau oprire, iar operațiunea V se mai numește și operație de semnal, trezire sau trezire.
  2. Ambele operațiuni sunt atomice, iar semaforul (semaforul) este întotdeauna inițializat la unul. Aici atomic înseamnă acea variabilă pe care citirea, modificarea și actualizarea se întâmplă în același timp/moment fără preempționare, adică între citire, modificare și actualizare nu este efectuată nicio altă operație care ar putea schimba variabila.
  3. O secțiune critică este înconjurată de ambele operațiuni pentru a implementa sincronizarea procesului. Vezi imaginea de mai jos. Secțiunea critică a procesului P se află între operația P și V.

harta vs set

Acum, să vedem cum implementează excluderea reciprocă. Să fie două procese P1 și P2 și un semafor s este inițializat ca 1. Acum, dacă presupunem că P1 intră în secțiunea sa critică, atunci valoarea semaforului s devine 0. Acum, dacă P2 dorește să intre în secțiunea sa critică, atunci va aștepta până la s> 0, acest lucru se poate întâmpla numai atunci când P1 își termină secțiunea critică și apelează operația V pe semaforul s.

În acest fel se realizează excluderea reciprocă. Priviți imaginea de mai jos pentru detalii care este un semafor binar.


Implementare: Semafoare binare

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.valoare == 1) { s.valoare = 0;  } else { // adaugă procesul la coada de așteptare q.push(P) sleep();  } } V(semafor s) { dacă (s.q este gol) { s.valoare = 1;  } else { // selectează un proces din coada de așteptare Process p = q.front();  // elimină procesul din așteptare așa cum a fost // trimis pentru CS q.pop();  trezire(p);  } } // Acest cod este modificat de Susobhan Akhuli>
C
#include  #include #include struct semaphore{  Queueq;  valoare int; }; void P(struct semafor s) { if (s.valoare == 1) { s.valoare = 0;  } else { s.q.push(P);   dormi();  } } void V(semafor s) { dacă (s.q este gol) { s.valoare = 1;  } else { // Obține un proces din Procesul Waiting Queue p = q.front();  // Eliminați procesul din așteptare q.pop();  trezire(p);  } } int main() { printf('Acesta este Hemish!!');    // Acest cod este contribuit de Himesh Singh Chauhan return 0; } // Acest cod este modificat de Susobhan Akhuli>
Java
import java.util.*; class Semaphore {  public enum Value { Zero, One }  public Queueq = noua Lista Linked();  public Value value = Value.One;  public void P(Semafor s, Process p) { if (s.value == Value.One) { s.value = Value.Zero;  } else { // adaugă procesul la coada de așteptare q.add(p);  p.Somn();  } } public void V(Semafor s) { if (s.q.size() == 0) { s.value = Value.One;  } else { // selectează un proces din coada de așteptare Process p = q.peek();  // elimină procesul din așteptare, deoarece // a fost trimis pentru CS q.remove();  p. Trezire();  } } }>>>Python3 
using System.Collections.Generic; class Semaphore {  public enum value { Zero, One }  public Queueq = coadă nouă();  public void P(Semafor s, Process p) { if (s.valoare == valoare.Unu) { s.valoare = valoare.Zero;  } else { // adaugă procesul la coada de așteptare q.Enqueue(p);  p.Somn();  } } public void V(Semafor s) { if (s.q.Count == 0) { s.value = value.One;  } else { // selectează un proces din coada de așteptare Process p = q.Peek();  // elimină procesul din așteptare așa cum a fost // trimis pentru CS q.Dequeue();  p. Trezire();  } } }>>>JavascriptDescrierea de mai sus este pentru semafor binar care poate lua doar două valori 0 și 1 și asigură excluderea reciprocă. Există un alt tip de semafor numit semafor de numărare care poate lua valori mai mari decât unu.

Acum să presupunem că există o resursă al cărei număr de instanțe este 4. Acum inițializam S = 4 și restul este același ca pentru semaforul binar. Ori de câte ori procesul dorește acea resursă, apelează P sau așteaptă funcția și când este gata apelează V sau funcția semnal. Dacă valoarea lui S devine zero, atunci un proces trebuie să aștepte până când S devine pozitiv. De exemplu, să presupunem că există 4 procese P1, P2, P3, P4 și toate apelează la operația de așteptare pe S (inițializat cu 4). Dacă un alt proces P5 dorește resursa, atunci ar trebui să aștepte până când unul dintre cele patru procese apelează funcția semnal și valoarea semaforului devine pozitivă.

Limitări:

  1. Una dintre cele mai mari limitări ale semaforului este inversarea priorității.
  2. Deadlock, să presupunem că un proces încearcă să trezească un alt proces care nu este în stare de repaus. Prin urmare, un blocaj se poate bloca pe termen nelimitat.
  3. Sistemul de operare trebuie să țină evidența tuturor apelurilor pentru a aștepta și a semnala semaforul.

Problemă în această implementare a unui semafor:

subșir java

Principala problemă cu semaforele este că necesită o așteptare ocupată, dacă un proces este în secțiunea critică, atunci alte procese care încearcă să intre în secțiunea critică vor aștepta până când secțiunea critică nu este ocupată de niciun proces. Ori de câte ori un proces așteaptă, atunci verifică în mod continuu valoarea semaforului (uitați-vă la această linie în timp ce (s==0); în funcționarea P) și risipă ciclul CPU.

Există, de asemenea, șansa de blocare, deoarece procesele continuă să se rotească în timp ce așteaptă blocarea. Pentru a evita acest lucru, o altă implementare este furnizată mai jos.

Implementare: Semafor de numărare

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.valoare = s.valoare - 1;  dacă (valoarea s< 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 Semafor(int value) { this.value = value;  q = noua Lista Linked();  } public void P(Proces p) { valoare--;  dacă (valoare< 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 = coadă nouă();  public void P(Proces p) { valoare--;  dacă (valoare< 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>

În această implementare, ori de câte ori procesul așteaptă, acesta este adăugat la o coadă de așteptare de procese asociate cu acel semafor. Acest lucru se face prin apelul de sistem block() pe acel proces. Când un proces este finalizat, apelează funcția de semnal și un proces din coadă este reluat. Utilizează apelul de sistem wakeup().

Avantajele semaforelor:

  • Un mecanism simplu și eficient pentru sincronizarea proceselor
  • Sprijină coordonarea între mai multe procese
  • Oferă o modalitate flexibilă și robustă de a gestiona resursele partajate.
  • Poate fi folosit pentru a implementa secțiuni critice într-un program.
  • Poate fi folosit pentru a evita condițiile de cursă.

Dezavantajele semaforelor:

  • Poate duce la degradarea performanței din cauza supraîncărcării asociate cu operațiunile de așteptare și semnal.
  • Poate duce la blocaj dacă este utilizat incorect.
  • A fost propusă de Dijkstra în 1965, care este o tehnică foarte semnificativă pentru gestionarea proceselor concurente prin utilizarea unei valori întregi simple, care este cunoscută sub numele de semafor. Un semafor este pur și simplu o variabilă întreagă care este partajată între fire. Această variabilă este utilizată pentru a rezolva problema secțiunii critice și pentru a realiza sincronizarea procesului în mediul multiprocesare.
  • Poate cauza probleme de performanță într-un program dacă nu este utilizat corespunzător.
  • Poate fi dificil de depanat și întreținut.
  • Poate fi predispus la condiții de cursă și alte probleme de sincronizare dacă nu este utilizat corect.
  • Poate fi vulnerabil la anumite tipuri de atacuri, cum ar fi atacurile de tip denial of service.