logo

Semafory v synchronizácii procesov

Semafory sú len normálne premenné používané na koordináciu aktivít viacerých procesov v počítačovom systéme. Používajú sa na vynútenie vzájomného vylúčenia, vyhýbanie sa rasovým podmienkam a implementáciu synchronizácie medzi procesmi.

java enums

Proces používania Semaforov poskytuje dve operácie: čakanie (P) a signál (V). Operácia čakania znižuje hodnotu semaforu a operácia signálu zvyšuje hodnotu semaforu. Keď je hodnota semaforu nula, každý proces, ktorý vykoná operáciu čakania, bude zablokovaný, kým iný proces nevykoná operáciu signálu.



Semafory sa používajú na implementáciu kritických sekcií, čo sú oblasti kódu, ktoré musí súčasne vykonávať iba jeden proces. Pomocou semaforov môžu procesy koordinovať prístup k zdieľaným zdrojom, ako je zdieľaná pamäť alebo I/O zariadenia.

Semafor je špeciálny druh synchronizačných údajov, ktoré možno použiť iba prostredníctvom špecifických synchronizačných primitív. Keď proces vykoná operáciu čakania na semafore, operácia skontroluje, či je hodnota semaforu>0. Ak áno, zníži hodnotu semaforu a nechá proces pokračovať vo vykonávaní; inak blokuje proces na semafore. Operácia signálu na semafore aktivuje proces zablokovaný na semafore, ak existuje, alebo zvýši hodnotu semaforu o 1. Kvôli tejto sémantike sa semafory nazývajú aj počítacie semafory. Počiatočná hodnota semaforu určuje, koľko procesov môže prejsť cez operáciu čakania.

Semafory sú dvoch typov:



  1. Binárny semafor –
    Toto je tiež známe ako zámok mutex. Môže mať iba dve hodnoty – 0 a 1. Jeho hodnota je inicializovaná na 1. Používa sa na implementáciu riešenia problémov kritických úsekov s viacerými procesmi.
  2. Počítací semafor –
    Jeho hodnota sa môže pohybovať v neobmedzenej doméne. Používa sa na riadenie prístupu k prostriedku, ktorý má viacero inštancií.

Teraz sa pozrime, ako sa to robí.

Najprv sa pozrite na dve operácie, ktoré možno použiť na prístup a zmenu hodnoty premennej semaforu.

P-and-V-operácia-v-OS



Niektoré body týkajúce sa prevádzky P a V:

  1. Operácia P sa tiež nazýva operácia čakania, spánku alebo vypnutia a prevádzka V sa tiež nazýva signál, prebudenie alebo prebudenie.
  2. Obe operácie sú atómové a semafor (semafory) je vždy inicializovaný na jeden. Atómový tu znamená, že premenná, na ktorej sa čítanie, modifikácia a aktualizácia deje v rovnakom čase/okamžiku bez predbežného zásahu, t.j. medzi čítaním, úpravou a aktualizáciou sa nevykoná žiadna iná operácia, ktorá by mohla zmeniť premennú.
  3. Kritická časť je obklopená oboma operáciami na implementáciu synchronizácie procesov. Pozrite si obrázok nižšie. Kritická časť procesu P je medzi operáciou P a V.

Teraz sa pozrime, ako implementuje vzájomné vylúčenie. Nech existujú dva procesy P1 a P2 a semafor s je inicializovaný ako 1. Ak teraz predpokladajme, že P1 vstúpi do svojej kritickej sekcie, potom bude hodnota semaforu s 0. Ak chce P2 vstúpiť do svojej kritickej sekcie, počká, kým s> 0, toto sa môže stať len vtedy, keď P1 dokončí svoj kritický úsek a zavolá operáciu V na semafore s.

Týmto spôsobom sa dosiahne vzájomné vylúčenie. Podrobnosti nájdete na obrázku nižšie, ktorý je binárnym semaforom.

vypnutie režimu vývojára v systéme Android


Implementácia: Binárne semafory

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.hodnota == 1) { s.hodnota = 0;  } else { // pridanie procesu do čakacej fronty q.push(P) sleep();  } } V(semafor s) { if (s.q je prázdne) { s.hodnota = 1;  } else { // výber procesu z čakacej fronty Proces p = q.front();  // odoberie proces z čakania tak, ako bol // odoslaný pre CS q.pop();  prebudenie(p);  } } // Tento kód upravil Susobhan Akhuli>
C
#include  #include #include struct semaphore{  Queueq;  int hodnota; }; void P(struct semafor s) { if (s.hodnota == 1) { s.hodnota = 0;  } else { s.q.push(P);   spánok ();  } } void V(semafor s) { if (s.q je prázdne) { s.hodnota = 1;  } else { // Získanie procesu z frontu čakania Proces p = q.front();  // Odstránenie procesu z čakania q.pop();  prebudenie(p);  } } int main() { printf('Toto je Hemiš!!');    // Tento kód prispel Himesh Singh Chauhan return 0; } // Tento kód upravil Susobhan Akhuli>
Java
import java.util.*; class Semaphore {  public enum Value { Zero, One }  public Queueq = nový LinkedList();  public Value value = Hodnota.Jedna;  public void P(Semafor s, Proces p) { if (s.hodnota == Hodnota.Jedna) { s.hodnota = Hodnota.Nula;  } else { // pridanie procesu do čakacej fronty q.add(p);  p.Spánok();  } } public void V(Semafor s) { if (s.q.size() == 0) { s.value = Hodnota.Jedna;  } else { // výber procesu z čakacej fronty Proces p = q.peek();  // odstráni proces z čakania, pretože // bol odoslaný pre 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 = nový front();  public void P(Semafor s, Proces p) { if (s.hodnota == hodnota.Jedna) { s.hodnota = hodnota.Nula;  } else { // pridanie procesu do čakacej fronty q.Enqueue(p);  p.Spánok();  } } public void V(Semafor s) { if (s.q.Count == 0) { s.value = hodnota.Jedna;  } else { // výber procesu z čakacej fronty Proces p = q.Peek();  // odstráni proces z čakania tak, ako bol // odoslaný pre 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);  }  } }>

Vyššie uvedený popis je pre binárny semafor, ktorý môže nadobudnúť iba dve hodnoty 0 a 1 a zabezpečiť vzájomné vylúčenie. Existuje ešte jeden typ semaforu nazývaný počítací semafor, ktorý môže nadobúdať hodnoty väčšie ako jedna.

Teraz predpokladajme, že existuje zdroj, ktorého počet inštancií je 4. Teraz inicializujeme S = 4 a zvyšok je rovnaký ako pre binárny semafor. Kedykoľvek proces chce tento zdroj, volá P alebo čaká na funkciu a keď je hotový, volá V alebo signálnu funkciu. Ak sa hodnota S stane nulou, proces musí počkať, kým sa hodnota S nestane kladnou. Predpokladajme napríklad, že existujú 4 procesy P1, P2, P3, P4 a všetky volajú operáciu čakania na S (inicializované 4). Ak iný proces P5 chce zdroj, potom by mal počkať, kým jeden zo štyroch procesov nezavolá signálnu funkciu a hodnota semaforu bude kladná.

Obmedzenia:

  1. Jedným z najväčších obmedzení semaforu je inverzia priority.
  2. Uviaznutie, predpokladajme, že proces sa pokúša prebudiť iný proces, ktorý nie je v stave spánku. Preto môže zablokovanie blokovať na neurčito.
  3. Operačný systém musí sledovať všetky volania na počkanie a signalizovať semafor.

Problém pri implementácii semaforu:

Hlavným problémom semaforov je, že vyžadujú rušné čakanie. Ak je proces v kritickej sekcii, potom ostatné procesy, ktoré sa pokúšajú vstúpiť do kritickej sekcie, budú čakať, kým kritická sekcia nebude obsadená žiadnym procesom. Kedykoľvek nejaký proces čaká, potom neustále kontroluje hodnotu semaforu (pozrite sa na tento riadok, kým (s==0); v prevádzke P) a plytvá cyklom CPU.

Existuje tiež možnosť zablokovania, pretože procesy sa točia počas čakania na uzamknutie. Aby sa tomu zabránilo, je nižšie uvedená iná implementácia.

podreťazec v jazyku Java

Implementácia: Počítací semafor

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.hodnota = s.hodnota - 1;  ak (s.hodnota< 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 hodnota) { this.value = hodnota;  q = new LinkedList();  } public void P(Proces p) { hodnota--;  ak (hodnota< 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 = nový front();  public void P(Proces p) { hodnota--;  ak (hodnota< 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 tejto implementácii vždy, keď proces čaká, je pridaný do čakacej fronty procesov spojených s týmto semaforom. Toto sa vykonáva prostredníctvom systémového volania block() v tomto procese. Po dokončení procesu zavolá funkciu signálu a jeden proces vo fronte sa obnoví. Používa systémové volanie wakeup().

Výhody semaforov:

  • Jednoduchý a efektívny mechanizmus na synchronizáciu procesov
  • Podporuje koordináciu medzi viacerými procesmi
  • Poskytuje flexibilný a robustný spôsob správy zdieľaných zdrojov.
  • Môže sa použiť na implementáciu kritických častí v programe.
  • Dá sa použiť na vyhnutie sa pretekárskym podmienkam.

Nevýhody semaforov:

  • Môže to viesť k zníženiu výkonu v dôsledku réžie spojenej s operáciami čakania a signálu.
  • Pri nesprávnom použití môže dôjsť k zablokovaniu.
  • Navrhol to Dijkstra v roku 1965, čo je veľmi významná technika na riadenie súbežných procesov pomocou jednoduchej celočíselnej hodnoty, ktorá je známa ako semafor. Semafor je jednoducho celočíselná premenná, ktorá je zdieľaná medzi vláknami. Táto premenná sa používa na vyriešenie problému kritickej sekcie a na dosiahnutie synchronizácie procesov v prostredí s viacerými procesormi.
  • Ak sa nepoužíva správne, môže to spôsobiť problémy s výkonom v programe.
  • Môže byť ťažké ladiť a udržiavať.
  • Ak sa nepoužíva správne, môže byť náchylný na pretekárske podmienky a iné problémy so synchronizáciou.
  • Môže byť zraniteľný voči určitým typom útokov, ako sú napríklad útoky odmietnutia služby.