logo

Semàfors en sincronització de processos

Els semàfors són només variables normals que s'utilitzen per coordinar les activitats de múltiples processos en un sistema informàtic. S'utilitzen per fer complir l'exclusió mútua, evitar condicions de carrera i implementar la sincronització entre processos.

El procés d'ús de semàfors proporciona dues operacions: espera (P) i senyal (V). L'operació d'espera disminueix el valor del semàfor i l'operació de senyal augmenta el valor del semàfor. Quan el valor del semàfor és zero, qualsevol procés que realitzi una operació d'espera es bloquejarà fins que un altre procés faci una operació de senyal.



Els semàfors s'utilitzen per implementar seccions crítiques, que són regions de codi que només s'han d'executar per un procés alhora. Mitjançant l'ús de semàfors, els processos poden coordinar l'accés als recursos compartits, com ara la memòria compartida o els dispositius d'E/S.

Un semàfor és un tipus especial de dades de sincronització que només es poden utilitzar mitjançant primitives de sincronització específiques. Quan un procés realitza una operació d'espera en un semàfor, l'operació comprova si el valor del semàfor és>0. Si és així, disminueix el valor del semàfor i deixa que el procés continuï la seva execució; en cas contrari, bloqueja el procés al semàfor. Una operació de senyal en un semàfor activa un procés bloquejat al semàfor si n'hi ha, o augmenta el valor del semàfor en 1. A causa d'aquesta semàntica, els semàfors també s'anomenen semàfors de recompte. El valor inicial d'un semàfor determina quants processos poden superar l'operació d'espera.

operadors javascript

Els semàfors són de dos tipus:



  1. Semàfor binari -
    Això també es coneix com a bloqueig mutex. Només pot tenir dos valors: 0 i 1. El seu valor s'inicialitza a 1. S'utilitza per implementar la solució de problemes de seccions crítiques amb múltiples processos.
  2. Semàfor de recompte -
    El seu valor pot variar en un domini sense restriccions. S'utilitza per controlar l'accés a un recurs que té múltiples instàncies.

Ara vegem com ho fa.

Primer, mireu dues operacions que es poden utilitzar per accedir i canviar el valor de la variable semàfor.

Operació P-i-V-en-OS



Alguns punts sobre el funcionament P i V:

  1. L'operació P també s'anomena operació d'espera, repòs o baixada, i l'operació V també s'anomena operació de senyal, despertador o activació.
  2. Les dues operacions són atòmiques i el semàfor(s) sempre s'inicialitza a un. Aquí atòmic significa aquella variable en què la lectura, la modificació i l'actualització es produeixen al mateix temps/moment sense cap prevenció, és a dir, entre la lectura, la modificació i l'actualització no es realitza cap altra operació que pugui canviar la variable.
  3. Una secció crítica està envoltada per ambdues operacions per implementar la sincronització de processos. Vegeu la imatge de sota. La secció crítica del procés P es troba entre les operacions P i V.

Ara, vegem com implementa l'exclusió mútua. Sigui dos processos P1 i P2 i un semàfor s s'inicialitza com a 1. Ara si suposem que P1 entra a la seva secció crítica, el valor del semàfor s passa a 0. Ara si P2 vol entrar a la seva secció crítica, esperarà fins a s.> 0, això només pot passar quan P1 acaba la seva secció crítica i crida a l'operació V al semàfor s.

D'aquesta manera s'aconsegueix l'exclusió mútua. Mireu la imatge de sota per obtenir detalls que és un semàfor binari.


Implementació: Semàfors binaris

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(semàfor s) { if (s.valor == 1) { s.valor = 0;  } else { // afegeix el procés a la cua d'espera q.push(P) sleep();  } } V(semàfor s) { si (s.q està buit) { s.valor = 1;  } else { // seleccioneu un procés de la cua d'espera Process p = q.front();  // elimina el procés de l'espera tal com ha estat // enviat per CS q.pop();  despertar (p);  } } // Aquest codi ha estat modificat per Susobhan Akhuli>
C
#include  #include #include struct semaphore{  Queueq;  valor int; }; void P(struct semàfor s) { if (s.valor == 1) { s.valor = 0;  } else { s.q.push(P);   dormir();  } } void V(semàfor s) { si (s.q està buit) { s.valor = 1;  } else { // Obteniu un procés del procés de la cua d'espera p = q.front();  // Elimina el procés de l'espera q.pop();  despertar (p);  } } int main() { printf('Això és Hemish!!');    // Aquest codi és aportat per Himesh Singh Chauhan return 0; } // Aquest codi és modificat per Susobhan Akhuli>>>Java>>Python 3 
using System.Collections.Generic; class Semaphore {  public enum value { Zero, One }  public Queueq = cua nova();  public void P(Semàfor s, Procés p) { if (s.valor == valor.Un) { s.valor = valor.Zero;  } else { // afegeix el procés a la cua d'espera q.Enqueue(p);  p.Sleep();  } } public void V(Semàfor s) { if (s.q.Count == 0) { s.valor = valor.Un;  } else { // seleccioneu un procés de la cua d'espera Process p = q.Peek();  // elimina el procés de l'espera tal com ha estat // enviat per CS q.Dequeue();  p.Despertar();  } } }>>>JavascriptLa descripció anterior és per al semàfor binari que només pot prendre dos valors 0 i 1 i garantir l'exclusió mútua. Hi ha un altre tipus de semàfor anomenat semàfor de recompte que pot prendre valors superiors a un.

Ara suposem que hi ha un recurs el nombre d'instàncies del qual és 4. Ara inicialitzem S = 4 i la resta és el mateix que per al semàfor binari. Sempre que el procés vol aquest recurs crida a P o espera la funció i quan està acabat crida a la funció V o senyal. Si el valor de S esdevé zero, un procés ha d'esperar fins que S esdevingui positiu. Per exemple, suposem que hi ha 4 processos P1, P2, P3, P4 i tots criden a l'operació d'espera a S (inicialitzat amb 4). Si un altre procés P5 vol el recurs, hauria d'esperar fins que un dels quatre processos cridi la funció senyal i el valor del semàfor esdevingui positiu.

Limitacions:

  1. Una de les majors limitacions del semàfor és la inversió de prioritat.
  2. Bloqueig, suposem que un procés intenta despertar un altre procés que no es troba en estat de repòs. Per tant, un bloqueig es pot bloquejar indefinidament.
  3. El sistema operatiu ha de fer un seguiment de totes les trucades per esperar i senyalitzar el semàfor.

Problema en aquesta implementació d'un semàfor:

El principal problema dels semàfors és que requereixen una espera ocupada. Si un procés es troba a la secció crítica, altres processos que intenten entrar a la secció crítica estaran esperant fins que la secció crítica no estigui ocupada per cap procés. Sempre que un procés espera, comprova contínuament el valor del semàfor (mireu aquesta línia mentre (s==0); en funcionament P) i malbarata el cicle de la CPU.

ordenació d'inserció en java

També hi ha una possibilitat de bloqueig de rotació, ja que els processos continuen girant mentre esperen el bloqueig. Per evitar-ho, es proporciona una altra implementació a continuació.

Implementació: Semàfor de recompte

CPP
struct Semaphore {  int value;  // q contains all Process Control Blocks(PCBs)  // corresponding to processes got blocked  // while performing down operation.  Queueq; }; P(Semàfor s) { s.valor = s.valor - 1;  si (s.valor< 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;  Semàfor públic (valor int) { this.value = valor;  q = new LinkedList();  } public void P(Procés p) { valor--;  si (valor< 0) {  q.add(p);  p.block();  }  }  public void V()  {  value++;  if (value <= 0) {  Process p = q.remove();  p.wakeup();  }  } }>
Python 3
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 = cua nova();  public void P(Procés p) { valor--;  si (valor< 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>

En aquesta implementació sempre que el procés espera, s'afegeix a una cua d'espera de processos associats a aquest semàfor. Això es fa mitjançant la trucada del sistema block() en aquest procés. Quan un procés es completa, crida a la funció de senyal i es reprèn un procés de la cua. Utilitza la crida al sistema wakeup().

Avantatges dels semàfors:

  • Un mecanisme senzill i eficaç per a la sincronització de processos
  • Admet la coordinació entre múltiples processos
  • Proporciona una manera flexible i robusta de gestionar els recursos compartits.
  • Es pot utilitzar per implementar seccions crítiques en un programa.
  • Es pot utilitzar per evitar condicions de carrera.

Desavantatges dels semàfors:

  • Pot provocar una degradació del rendiment a causa de la sobrecàrrega associada a les operacions d'espera i senyal.
  • Pot provocar un bloqueig si s'utilitza incorrectament.
  • Va ser proposat per Dijkstra l'any 1965, que és una tècnica molt significativa per gestionar processos concurrents mitjançant l'ús d'un valor enter simple, que es coneix com a semàfor. Un semàfor és simplement una variable entera que es comparteix entre fils. Aquesta variable s'utilitza per resoldre el problema de la secció crítica i per aconseguir la sincronització de processos en l'entorn de multiprocessament.
  • Pot causar problemes de rendiment en un programa si no s'utilitza correctament.
  • Pot ser difícil de depurar i mantenir.
  • Pot ser propens a les condicions de carrera i altres problemes de sincronització si no s'utilitza correctament.
  • Pot ser vulnerable a certs tipus d'atacs, com ara atacs de denegació de servei.