logo

Comunicare între procese (IPC)

Un proces poate fi de două tipuri:

  • Proces independent.
  • Proces de cooperare.

Un proces independent nu este afectat de execuția altor procese, în timp ce un proces de cooperare poate fi afectat de alte procese de execuție. Deși se poate crede că acele procese, care rulează independent, se vor executa foarte eficient, în realitate, există multe situații în care natura cooperativă poate fi utilizată pentru creșterea vitezei de calcul, a confortului și a modularității. Comunicarea între procese (IPC) este un mecanism care permite proceselor să comunice între ele și să își sincronizeze acțiunile. Comunicarea dintre aceste procese poate fi văzută ca o metodă de cooperare între ele. Procesele pot comunica între ele prin ambele:



  1. Memorie partajată
  2. Mesajul trece

Figura 1 de mai jos prezintă o structură de bază a comunicării între procese prin metoda memoriei partajate și prin metoda transmiterii mesajelor.

Un sistem de operare poate implementa ambele metode de comunicare. În primul rând, vom discuta despre metodele de comunicare cu memoria partajată și apoi despre transmiterea mesajelor. Comunicarea între procese care utilizează memoria partajată necesită ca procesele să partajeze o anumită variabilă și depinde complet de modul în care o va implementa programatorul. O modalitate de comunicare folosind memoria partajată poate fi imaginată astfel: Să presupunem că procesul1 și procesul2 se execută simultan și împărtășesc anumite resurse sau folosesc unele informații dintr-un alt proces. Process1 generează informații despre anumite calcule sau resurse utilizate și le păstrează ca înregistrare în memoria partajată. Când procesul2 trebuie să utilizeze informațiile partajate, va verifica înregistrarea stocată în memoria partajată și va lua notă de informațiile generate de proces1 și va acționa în consecință. Procesele pot folosi memoria partajată pentru extragerea de informații ca înregistrare dintr-un alt proces, precum și pentru livrarea oricărei informații specifice altor procese.
Să discutăm un exemplu de comunicare între procese folosind metoda memoriei partajate.



i) Metoda memoriei partajate

Ex: Problema Producător-Consumator
Există două procese: Producător și Consumator. Producătorul produce unele articole, iar Consumatorul consumă acel articol. Cele două procese împărtășesc un spațiu comun sau o locație de memorie cunoscută sub numele de buffer în care este stocat articolul produs de Producător și din care Consumatorul consumă articolul dacă este necesar. Există două versiuni ale acestei probleme: prima este cunoscută sub numele de problema bufferului nelimitat în care Producătorul poate continua să producă articole și nu există nicio limită în ceea ce privește dimensiunea tamponului, a doua este cunoscută sub numele de problema bufferului limitat în pe care Producătorul le poate produce până la un anumit număr de articole înainte de a începe să aștepte ca Consumatorul să le consume. Vom discuta problema bufferului mărginit. Mai întâi, producătorul și consumatorul vor împărtăși o memorie comună, apoi producătorul va începe să producă articole. Dacă articolul total produs este egal cu dimensiunea tamponului, producătorul va aștepta să îl consume de către Consumator. În mod similar, consumatorul va verifica mai întâi disponibilitatea articolului. Dacă nu este disponibil niciun articol, Consumatorul va aștepta ca Producătorul să-l producă. Dacă există articole disponibile, Consumatorul le va consuma. Pseudo-codul de demonstrat este furnizat mai jos:
Date partajate între cele două procese

C






#define buff_max 25> #define mod %> >struct> item{> >// different member of the produced data> >// or consumed data> >---------> >}> > >// An array is needed for holding the items.> >// This is the shared place which will be> >// access by both process> >// item shared_buff [ buff_max ];> > >// Two variables which will keep track of> >// the indexes of the items produced by producer> >// and consumer The free index points to> >// the next free index. The full index points to> >// the first full index.> >int> free_index = 0;> >int> full_index = 0;> >

>

>

Codul de proces al producătorului

C




item nextProduced;> > >while>(1){> > >// check if there is no space> >// for production.> >// if so keep waiting.> >while>((free_index+1) mod buff_max == full_index);> > >shared_buff[free_index] = nextProduced;> >free_index = (free_index + 1) mod buff_max;> >}>

>

>

Codul de proces al consumatorului

C




item nextConsumed;> > >while>(1){> > >// check if there is an available> >// item for consumption.> >// if not keep on waiting for> >// get them produced.> >while>((free_index == full_index);> > >nextConsumed = shared_buff[full_index];> >full_index = (full_index + 1) mod buff_max;> >}>

>

>

În codul de mai sus, Producătorul va începe să producă din nou când (free_index+1) mod buff max va fi gratuit, deoarece dacă nu este gratuit, aceasta înseamnă că există încă articole care pot fi consumate de către Consumator, deci nu este nevoie. pentru a produce mai mult. În mod similar, dacă indexul gratuit și indexul complet indică același indice, aceasta înseamnă că nu există articole de consumat.

Implementare generală C++:

C++




#include> #include> #include> #include> #define buff_max 25> #define mod %> struct> item {> >// different member of the produced data> >// or consumed data> >// ---------> };> // An array is needed for holding the items.> // This is the shared place which will be> // access by both process> // item shared_buff[buff_max];> // Two variables which will keep track of> // the indexes of the items produced by producer> // and consumer The free index points to> // the next free index. The full index points to> // the first full index.> std::atomic<>int>>free_index(0);> std::atomic<>int>>full_index(0);> std::mutex mtx;> void> producer() {> >item new_item;> >while> (>true>) {> >// Produce the item> >// ...> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >// Add the item to the buffer> >while> (((free_index + 1) mod buff_max) == full_index) {> >// Buffer is full, wait for consumer> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >}> >mtx.lock();> >// Add the item to the buffer> >// shared_buff[free_index] = new_item;> >free_index = (free_index + 1) mod buff_max;> >mtx.unlock();> >}> }> void> consumer() {> >item consumed_item;> >while> (>true>) {> >while> (free_index == full_index) {> >// Buffer is empty, wait for producer> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >}> >mtx.lock();> >// Consume the item from the buffer> >// consumed_item = shared_buff[full_index];> >full_index = (full_index + 1) mod buff_max;> >mtx.unlock();> >// Consume the item> >// ...> >std::this_thread::sleep_for(std::chrono::milliseconds(100));> >}> }> int> main() {> >// Create producer and consumer threads> >std::vectorthread>fire; fire.emplace_back(producător); fire.emplace_back(consumator); // Așteptați ca firele să se termine pentru (auto& thread : fire) { thread.join(); } returnează 0; }>>>

> 

Mărimea mesajului poate fi de dimensiune fixă ​​sau variabilă. Dacă este de dimensiune fixă, este ușor pentru un proiectant de sistem de operare dar complicat pentru un programator și dacă este de dimensiune variabilă atunci este ușor pentru un programator, dar complicat pentru proiectant de sistem de operare. Un mesaj standard poate avea două părți: antet și corp.
The parte antet este utilizat pentru stocarea tipului de mesaj, id-ului destinației, id-ului sursei, lungimea mesajului și informațiile de control. Informațiile de control conțin informații cum ar fi ce trebuie să faceți dacă epuizează spațiul tampon, numărul de secvență, prioritatea. În general, mesajul este trimis folosind stilul FIFO.

Mesaj care trece prin legătura de comunicare.
Link de comunicare directă și indirectă
Acum, vom începe discuția despre metodele de implementare a legăturilor de comunicare. În timpul implementării linkului, există câteva întrebări care trebuie reținute, cum ar fi:

  1. Cum se stabilesc legăturile?
  2. O legătură poate fi asociată cu mai mult de două procese?
  3. Câte legături pot exista între fiecare pereche de procese de comunicare?
  4. Care este capacitatea unei legături? Dimensiunea unui mesaj pe care linkul îl poate găzdui este fixă ​​sau variabilă?
  5. Un link este unidirecțional sau bidirecțional?

O legătură are o anumită capacitate care determină numărul de mesaje care pot locui temporar în ea pentru care fiecare legătură are o coadă asociată cu ea, care poate fi de capacitate zero, capacitate limitată sau capacitate nelimitată. La capacitate zero, expeditorul așteaptă până când receptorul îl informează pe expeditor că a primit mesajul. În cazurile cu capacitate diferită de zero, un proces nu știe dacă un mesaj a fost primit sau nu după operațiunea de trimitere. Pentru aceasta, expeditorul trebuie să comunice în mod explicit cu destinatarul. Implementarea legăturii depinde de situație, poate fi fie o legătură de comunicare directă, fie o legătură de comunicare indirectă.
Legături de comunicare directă sunt implementate atunci când procesele folosesc un identificator de proces specific pentru comunicare, dar este greu să identifici expeditorul din timp.
De exemplu, serverul de imprimare.
Comunicare indirectă se face printr-o cutie poștală partajată (port), care constă dintr-o coadă de mesaje. Expeditorul păstrează mesajul în căsuța poștală, iar destinatarul îl preia.

Mesaj care trece prin schimbul de mesaje.

Transmiterea mesajelor sincrone și asincrone:
Un proces care este blocat este unul care așteaptă un eveniment, cum ar fi o resursă care devine disponibilă sau finalizarea unei operațiuni I/O. IPC este posibil între procesele de pe același computer, precum și pe procesele care rulează pe computere diferite, adică în sistem în rețea/distribuit. În ambele cazuri, procesul poate fi sau nu blocat în timp ce trimiteți un mesaj sau încercați să primiți un mesaj, astfel încât transmiterea mesajului poate fi blocantă sau neblocantă. Blocarea este luată în considerare sincron și blocarea trimiterii înseamnă că expeditorul va fi blocat până când mesajul este primit de către destinatar. În mod similar, blocarea primirii are receptorul blocat până când un mesaj este disponibil. Neblocarea este luată în considerare asincron și Trimitere fără blocare are ca expeditorul să trimită mesajul și să continue. În mod similar, primirea fără blocare face ca receptorul să primească un mesaj valid sau nul. După o analiză atentă, putem ajunge la concluzia că pentru un expeditor este mai firesc să nu fie blocat după transmiterea mesajului, deoarece poate fi nevoie de a trimite mesajul către diferite procese. Cu toate acestea, expeditorul așteaptă o confirmare de la destinatar în cazul în care trimiterea eșuează. În mod similar, este mai natural ca un receptor să se blocheze după emiterea recepției, deoarece informațiile din mesajul primit pot fi folosite pentru execuții ulterioare. În același timp, dacă trimiterea mesajului continuă să eșueze, receptorul va trebui să aștepte la nesfârșit. De aceea luăm în considerare și cealaltă posibilitate de transmitere a mesajului. Practic, există trei combinații preferate:

  • Blocarea trimiterii și blocarea primirii
  • Trimitere fără blocare și primire fără blocare
  • Trimitere fără blocare și primire blocată (folosit cel mai mult)

În trecerea mesajului direct , Procesul care dorește să comunice trebuie să numească în mod explicit destinatarul sau expeditorul comunicării.
de exemplu. trimite (p1, mesaj) înseamnă a trimite mesajul către p1.
În mod similar, primiți (p2, mesaj) înseamnă a primi mesajul de la p2.
În această metodă de comunicare, legătura de comunicare se stabilește automat, care poate fi fie unidirecțională, fie bidirecțională, dar o legătură poate fi utilizată între o pereche de emițător și receptor și o pereche de emițător și receptor nu trebuie să aibă mai mult de o pereche de link-uri. Simetria și asimetria între trimitere și primire pot fi, de asemenea, implementate, adică fie ambele procese se vor numi unul pe celălalt pentru trimiterea și primirea mesajelor, fie numai expeditorul va numi receptorul pentru a trimite mesajul și nu este nevoie ca receptorul să numească expeditorul pentru primirea mesajului. Problema cu această metodă de comunicare este că, dacă numele unui proces se schimbă, această metodă nu va funcționa.
În trecerea indirectă a mesajelor , procesele folosesc cutii poștale (numite și porturi) pentru trimiterea și primirea mesajelor. Fiecare cutie poștală are un ID unic și procesele pot comunica numai dacă partajează o cutie poștală. Legătura stabilită numai dacă procesele au o cutie poștală comună și o singură legătură poate fi asociată cu mai multe procese. Fiecare pereche de procese poate partaja mai multe legături de comunicare și aceste legături pot fi unidirecționale sau bidirecționale. Să presupunem că două procese doresc să comunice prin transmiterea indirectă a mesajelor, operațiunile necesare sunt: ​​creați o cutie poștală, utilizați această cutie poștală pentru trimiterea și primirea mesajelor, apoi distrugeți cutia poștală. Primitivele standard utilizate sunt: Trimite un mesaj) ceea ce înseamnă trimiterea mesajului către căsuța poștală A. Primitiva pentru primirea mesajului funcționează, de asemenea, în același mod de ex. primit (A, mesaj) . Există o problemă cu implementarea acestei căsuțe poștale. Să presupunem că există mai mult de două procese care partajează aceeași cutie poștală și să presupunem că procesul p1 trimite un mesaj către cutia poștală, care proces va fi receptorul? Acest lucru poate fi rezolvat fie prin impunerea faptului că doar două procese pot partaja o singură cutie poștală, fie prin aplicarea faptului că un singur proces are permisiunea de a executa primirea la un moment dat sau selectând orice proces aleatoriu și anunță expeditorul despre destinatar. O cutie poștală poate fi privată unei singure perechi expeditor/receptor și poate fi, de asemenea, partajată între mai multe perechi expeditor/receptor. Portul este o implementare a unei astfel de cutii poștale care poate avea mai mulți expeditori și un singur receptor. Este folosit în aplicațiile client/server (în acest caz serverul este receptorul). Portul este deținut de procesul de recepție și creat de OS la cererea procesului de receptor și poate fi distrus fie la cererea aceluiași procesor receptor atunci când receptorul se termină singur. Implementarea faptului că un singur proces este permis pentru a executa primirea se poate face folosind conceptul de excludere reciprocă. Cutia poștală Mutex este creată care este partajată de n proces. Expeditorul nu blochează și trimite mesajul. Primul proces care execută recepția va intra în secțiunea critică și toate celelalte procese se vor bloca și vor aștepta.
Acum, să discutăm problema producător-consumator folosind conceptul de transmitere a mesajelor. Producătorul plasează articole (în interiorul mesajelor) în cutia poștală, iar consumatorul poate consuma un articol atunci când cel puțin un mesaj este prezent în cutia poștală. Codul este dat mai jos:
Cod Producător

C




void> Producer(>void>){> > >int> item;> >Message m;> > >while>(1){> > >receive(Consumer, &m);> >item = produce();> >build_message(&m , item ) ;> >send(Consumer, &m);> >}> >}>

>

>

Codul Consumatorului

C




void> Consumer(>void>){> > >int> item;> >Message m;> > >while>(1){> > >receive(Producer, &m);> >item = extracted_item();> >send(Producer, &m);> >consume_item(item);> >}> >}>

>

>

Exemple de sisteme IPC

  1. Posix: folosește metoda memoriei partajate.
  2. Mach : folosește transmiterea mesajelor
  3. Windows XP : folosește transmiterea de mesaje folosind apeluri procedurale locale

Comunicare în arhitectura client/server:
Există diferite mecanisme:

  • țeavă
  • Priză
  • Apeluri procedurale de la distanță (RPC)

Cele trei metode de mai sus vor fi discutate în articolele ulterioare, deoarece toate sunt destul de conceptuale și merită propriile articole separate.
Referinte:

  1. Concepte de sistem de operare de Galvin et al.
  2. Note de curs/ppt de Ariel J. Frank, Universitatea Bar-Ilan

Comunicarea între procese (IPC) este mecanismul prin care procesele sau firele de execuție pot comunica și schimba date între ele pe un computer sau printr-o rețea. IPC este un aspect important al sistemelor de operare moderne, deoarece permite diferitelor procese să lucreze împreună și să partajeze resurse, ceea ce duce la creșterea eficienței și flexibilității.

Avantajele IPC:

  1. Permite proceselor să comunice între ele și să partajeze resurse, ceea ce duce la creșterea eficienței și flexibilității.
  2. Facilitează coordonarea între mai multe procese, ceea ce duce la o performanță generală mai bună a sistemului.
  3. Permite crearea de sisteme distribuite care pot acoperi mai multe computere sau rețele.
  4. Poate fi folosit pentru a implementa diverse protocoale de sincronizare și comunicare, cum ar fi semafore, conducte și prize.

Dezavantajele IPC:

  1. Mărește complexitatea sistemului, făcând mai dificilă proiectarea, implementarea și depanarea.
  2. Poate introduce vulnerabilități de securitate, deoarece procesele pot fi capabile să acceseze sau să modifice date aparținând altor procese.
  3. Necesită gestionarea atentă a resurselor sistemului, cum ar fi memoria și timpul CPU, pentru a se asigura că operațiunile IPC nu degradează performanța generală a sistemului.
    Poate duce la inconsecvențe de date dacă mai multe procese încearcă să acceseze sau să modifice aceleași date în același timp.
  4. În general, avantajele IPC depășesc dezavantajele, deoarece este un mecanism necesar pentru sistemele de operare moderne și permite proceselor să lucreze împreună și să partajeze resursele într-un mod flexibil și eficient. Cu toate acestea, trebuie avut grijă să proiectați și să implementați cu atenție sistemele IPC, pentru a evita potențialele vulnerabilități de securitate și probleme de performanță.

Mai multe referințe:
http://nptel.ac.in/courses/106108101/pdf/Lecture_Notes/Mod%207_LN.pdf
https://www.youtube.com/watch?v=lcRqHwIn5Dk