Comunicatia Intre Procese (IPC)

11
1. Comunicaţia între procese (IPC) 1.1. Mecanismul IPC Comunicaţia între procese este un mecanism prin care diferite procese interacţionează şi comunică date între ele. Aplicaţiile, care folosesc IPC pentru interacţiune pot fi împărţite în două categorii: aplicatii server şi aplicaţii client. Aplicaţia server asigură aplicaţiei client serviciile care i-au fost cerute. Există mai multe mecanisme IPC care sunt suportate de WIN32 SDK. Pipe-urile sunt unele din aceste mecanisme. Pipe-urile permit transferul de date între procese într-o manieră FIFO. Aplicaţia care creeează pipe-ul se numeste aplicaţie pipe server iar, aplicaţia care se conectează la ea se numeşte aplicaţie pipe client. Figure 1-1 - Cum este organizat un pipe FIFO înseamnă că procesele citesc datele în aceeasi ordine în care a fost scrisă. Sistemul se asigură că datele nu se pierd la mijloc (unul dintre procese iese prematur). Odată citită din pipe data este ştearsă din pipe, eliberând astfel spatiu pentru procesul care scrie în pipe. Există două tipuri de pipe: pipe-uri nenumite (anonime) şi pipe-uri numite.

description

ccc

Transcript of Comunicatia Intre Procese (IPC)

Page 1: Comunicatia Intre Procese (IPC)

1. Comunicaţia între procese (IPC)

1.1. Mecanismul IPC

Comunicaţia între procese este un mecanism prin care diferite procese interacţionează şi comunică date între ele. Aplicaţiile, care folosesc IPC pentru interacţiune pot fi împărţite în două categorii: aplicatii server şi aplicaţii client. Aplicaţia server asigură aplicaţiei client serviciile care i-au fost cerute. Există mai multe mecanisme IPC care sunt suportate de WIN32 SDK. Pipe-urile sunt unele din aceste mecanisme. Pipe-urile permit transferul de date între procese într-o manieră FIFO. Aplicaţia care creeează pipe-ul se numeste aplicaţie pipe server iar, aplicaţia care se conectează la ea se numeşte aplicaţie pipe client.

Figure 1-1 - Cum este organizat un pipe

FIFO înseamnă că procesele citesc datele în aceeasi ordine în care a fost scrisă. Sistemul se asigură că datele nu se pierd la mijloc (unul dintre procese iese prematur). Odată citită din pipe data este ştearsă din pipe, eliberând astfel spatiu pentru procesul care scrie în pipe.

Există două tipuri de pipe: pipe-uri nenumite (anonime) şi pipe-uri numite.

Un pipe nenumit este o conductă de date care transfera datele între procesele înrudite (exemplu: între procesul tată şi fiu). Nu suportă comunicaţia în retea şi sunt întotdeauna orientate byte-stream.

Page 2: Comunicatia Intre Procese (IPC)

Un pipe numit asigură comunicaţia într-un sens sau în ambele între pipe server şi pipe client. Poate fi folosita sa interacţioneze între procese nu neapărat înrudite pe maşini diferite, în retea. [3]

Pipe-ul numit mai este întâlnit şi sub denumirea de pipe FIFO. Numele unui pipe numit este de fapt un fişier în sistemul de fişiere. Pipe-urile numite sunt vizibile cu comanda ls ca orice alt fişier cu câteva diferenţe:

% ls -l fifo1prw-r--r-- 1 mconstan e214 0 May 22 20:15 fifo1|

Litera p din stânga indică faptul că fifo1 este un pipe, deasemenea caracterul | din coloana dreaptă.

Pe sistemele Linux mai vechi pipe-urile numite se creau prin comanda mknod. Pe sistemele moderne acest lucru se face prin comanda mkfifo ce primeşte unul sau mai multe nume de fişiere ca argumente şi creează pipe-uri cu aceste nume. De exemplu pentru a crea un pipe numit cu numele pipe1 se dă comanda:

mkfifo pipe

Pentru a evidenţia cum lucrează un pipe numit se execută în console separate comenzile :

ls -l > pipecat < pipe

Rezultatul primei comenzi se va afişa în cea de-a doua consolă. [4]

1.2. IPC (Modelul Master-Slave)

Acest mod de comunicaţie reprezintă o relaţie tată-fiu. Un proces care creează un alt proces se numeste tată, procesele create se numesc fii. Procesul fiu, odata creat nu mai depinde de procesul tată şi ruleaza independent. Procesul fiu are spatiu său de adresa virtuală care este independent de spatiul tatalui. Un fiu poate moşteni handler-urile deschise de la tată. Un handler moştenit în procesul fiu referă la acelaşi obiect ca handler-ul original al tatălui. Când un proces fiu moşteneste handler-ul, sistemul de operare asigură numai acces la procesul fiu. Un proces fiu are un nou spaţiu de adresă virtuală, în consecintă tatăl comunică valoarea handler-ului fiului. Acestă valoare poate fi trimisă prin mai multe căi:

2

Page 3: Comunicatia Intre Procese (IPC)

Prin argument în linie de comandă

Prin obiecte de mapare fişier

Prin pipe-uri sau prin canale standard de I/O

Moştenirea handler-ului determină procesul fiu să aibă intrările pentru toate handler-urile mostenibile (care sunt deschise) în propria sa tabelă obiect. Odată ce valoarea handler-ului este trimisă fiului printr-un mecanism IPC, acesta poate folosi de asemenea handlerul. [3]

1.3. Comunicare în ambele sensuri folosind pipe-uri

În sisteme mai complexe, se descoperă că comunicarea într-un sens este prea limitată. Se doreşte astfel să se comunice în ambele sensuri: de la tată la fiu şi de la fiu la tată. Acest lucru se face relativ uşor folosind două pipe-uri, câte una în fiecare sens. Insă acest mod poate duce la apariţia unei situaţii numită deadlock (blocare).

Deadlock-ul reprezintă o situaţie în care un grup de două sau mai multe procese aşteaptă pentru un set de resurse care care sunt alocate altor procese din acelasi grup, ori după evenimente care trebuie anuntate de alte procese din grup.

Aceasta situaţie poate fi întâlnită atunci când două procese comunică prin 2 pipe-uri. Sunt prezentate 2 posibile scenarii:

Amândouă pipe-urile sunt libere şi ambele procese încearcă să citească din capătul de citire. Fiecare este blocat pe citire (pentru că pipe-ul este gol), şi vor rămâne blocate nedefinit.

Fiecare pipe are un buffer limitat asociat. Când un proces scrie în pipe, datele sunt plasate în bufferul pipe-ului, până când este citit de procesul care citeste. Daca bufferul este plin, apelul de sistem write(),se blocheaza pana se elibereaza bufferul. Singura cale de eliberare este de a citi din buffer. Astfel daca ambele procese scriu date, fiecare în capătul pipe-ul lui de scriere, ambele se vor bloca pe apelul de sistem write(). Cum nici un alt

3

Page 4: Comunicatia Intre Procese (IPC)

proces nu mai citeste din pipe-uri, cele 2 procese vor intra într-o stare de deadlock. [1]

1.4. Exemple

Apelul pipe aşa cum reiese din manualul Linux:

NAME

pipe - create pipe

SYNOPSIS

#include <unistd.h>

int pipe(int filedes[2]);

DESCRIPTION

pipe creates a pair of file descriptors, pointing to a pipe inode, and places them in the array pointed to by filedes. filedes[0] is for reading, filedes[1] is for writing.

RETURN VALUE

On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

ERRORS

EMFILE Too many file descriptors are in use by the process.

ENFILE The system file table is full.

EFAULT filedes is not valid.

CONFORMING TO

SVr4, SVID, AT&T, POSIX, X/OPEN, BSD 4.3

SEE ALSO

read(2), write(2), fork(2), socketpair(2)

4

Page 5: Comunicatia Intre Procese (IPC)

REFERENCED BY

csh(1), event(3), fifo(4), fstat(2), ksh(1), lstat(2), pdksh(1), popen(3), stat(2), syscall(2), syscalls(2), tcsh(1) [5]

Un apel al funcţiei pipe() returnează o pereche de descriptori. Unul dintre aceşti descriptori este conectat la capătul de scriere al pipe-ului iar, celălalt este conectat la capătul de citire. Orice poate fi scris în pipe şi citit de la celalalt capăt în ordinea în care a intrat. Pe majoritatea sistemelor, pipe-urile se umplu după ce s-a scris în ele aproximativ 10K fară a se citi nimic.

Un exemplu simplu de folosire a unui pipe nenumit (sau pipe anonim) sub Linux este comanda:

ls | grep x

Când interpretorul examinează linia de comandă, găseşte caracterul | ce separă două comenzi. Shell-ul execută ambele comenzi, conectând ieşirea prime la intrarea celei de a două. Exemplul de mai sus foloseşte un pipe nenumit. Pipe-ul există doar în kernel şi nu poate fi accesat de procese ce l-au creat, în acest caz shell-ul care l-a creat.

Un exemplu simplu de folosire a unui pipe numit (sau pipe FIFO ):

mkfifo pipels -l > pipecat < pipe

Un exemplu care creează, scrie şi citeşte dintr-un pipe:

#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <unistd.h> int main() { int pfds[2]; char buf[30]; if (pipe(pfds) == -1) // se testează reuşita apelului pipe { perror("pipe"); exit(1); } printf("writing to file descriptor #%d\n", pfds[1]); write(pfds[1], "test", 5);

5

Page 6: Comunicatia Intre Procese (IPC)

printf("reading from file descriptor #%d\n", pfds[0]); read(pfds[0], buf, 5); printf("read \"%s\"\n", buf); }

Apelul pipe() primeşte un vector de 2 întregi (ints) că parametru. Presupunând că nu apare nici o eroare, conectează 2 descriptori de fişiere şi îi întoarce într-un vector. Primul element este capătul de citire iar, secundul cel de scriere.[ Figure 1-1 ]

Vom folosi alt exemplu. Intâi procesul tata va crea un fiu. Vom apela fork(). Astfel fiul va putea să trimită date spre capătul de scriere al pipe-ului, iar tatal le va primi la capătul de citire astfel:

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h>

int main() { int pfds[2]; char buf[30];

pipe(pfds);

if (!fork()) { printf(" CHILD: writing to the pipe\n"); write(pfds[1], "test", 5); printf(" CHILD: exiting\n"); exit(0); } else { printf("PARENT: reading from pipe\n"); read(pfds[0], buf, 5); printf("PARENT: read \"%s\"\n", buf); wait(NULL); } }

Rezultatul întors va arăta astfel:

PARENT: reading from pipe CHILD: writing to the pipe CHILD: exitingPARENT: read "test"

În acest caz tatal încearcă sa citească din pipe înainte ca fiul să scrie în ea. Când acest lucru se întâmplă, tatal trece în block sau sleep pâna urmeaza ca să sosească date pentru citire. Tatăl încearcă să citească, trece în sleep, fiul scrie şi iese, tatăl se trezeste şi citeste datele.

6

Page 7: Comunicatia Intre Procese (IPC)

Exerciţiul 1:

Implementarea comenzii “ls | wc -l” în C.

Rezolvare:

Se vor folosi apelurile exec() şi dup(). Familia exec de funcţii înlocuieşte procesul curent ce rulează cu orice proces îi este trimis de exec(). Acesta este funcţia care va fi folosită pentru a executa ls şi wc –l. Apelul dup() primeste un descriptor de fisier şi îi face o copie (clonă). Astfel se conectează stdout al comenzii ls cu stdin al comenzii wc. Iesirea stdout a lui ls intră în pipe şi intrarea stdin alui wc intră în pipe.

#include <stdio.h> #include <stdlib.h> #include <unistd.h>

int main() { int pfds[2];

pipe(pfds);

if (!fork()) { close(1); /* close normal stdout */ dup(pfds[1]); /* make stdout same as pfds[1] */ close(pfds[0]); /* we don't need this */ execlp("ls", "ls", NULL); } else { close(0); /* close normal stdin */ dup(pfds[0]); /* make stdin same as pfds[0] */ close(pfds[1]); /* we don't need this */ execlp("wc", "wc", "-l", NULL); } }

Funcţia close(1) eliberează descriptorul de fisier 1 (stdout), dup(pdfs[1]) copiază capătul scriere al pipe-ului în primul descriptor liber, care este “1”, pentru că tocmai ce a fost eliberat. În acest fel tot ce este scris în stdout ( descriptorul 1) de către ls va merge în pdfs[1] (capătul de scriere al pipe-ului). În mod similar funcţionează şi wc numai că în sens invers. [2]

Observaţie:

7

Page 8: Comunicatia Intre Procese (IPC)

Orice proces cu permisiuni adecvate poate mai apoi citi sau scrie întru-un pipe numit.

In apelul open(2), procesul ce deschide pipe-ul se blochează până un alt proces redeschide pipe-ul.

Pentru a deschide un pipe numit fară a-l bloca, apelul open(2) însumează masca O_NDELAY ( din sys/fcntl.h) cu masca modului fişier selectat folosind booleanul sau operaţia pe apelul open(2). Dacă nici un alt proces nu este conectat la pipe când open(2) este apelat, se returnează -1 cu errno setat pe EWOULDBLOCK.

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

To open a named pipe without blocking, the open(2) call joins the O_NDELAY mask (found in sys/fcntl.h) with the selected file mode mask using the Boolean or operation on the call to open(2). If no other process is connected to the pipe when open(2) is called, -1 is returned with errno set to EWOULDBLOCK.[9]

1.5. Exerciţii propuse

1) Să se realizeze un pipe bidirecţional (two-way) între părinte şi copil într-un program C. De exemplu ambii pot transmite îi primi semnale.[6]

2) Să se realizeze un program care foloseşte pipe-uri astfel: un proces citeşte litere de la tastatură iar altul le converteşte în majuscule şi le afişează.[7]

3) Implementaţi comanda ps -xl | grep nume_proces folosind pipe-uri.[8]

4) Sincronizaţi două procese folosind pipe-uri.

1.6. Concluzii

Probabil că modul cel mai bun de folosire al pipe-ului este cel mai banal: trimiterea stdout al unei comenzi la stdin-ul alteia. Pentru alte moduri de folosire, pipe-urile sunt destul de limitate, existând alte tehnici IPC mai avantajoase. [2]

8

Page 9: Comunicatia Intre Procese (IPC)

Bibliografie

[1] Little Unix Programmers Group (LUPG)'s Little Site http://users.actcom.co.il/~choo/lupg/tutorials/multi-process/multi-process.html

[2] Beej's Guide to Unix Interprocess Communication http://www.ecst.csuchico.edu/~beej/guide/ipc/

[3] Unleashing anonymous pipes – Part 1 By Dinesh Ahujahttp://www.codeproject.com/

[4] Introduction to Named PipesBy Andy Vaughthttp://www2.linuxjournal.com/article/2156

[5] Interprocess Communication (IPC), Pipeshttp://www.cs.cf.ac.uk/Dave/C/node23.html

[6] pipe(2) - Linux man page[7] Pipe example

http://users.actcom.co.il/~choo/lupg/tutorials/multi-process/two-way-pipe.c

[8] Pipe examplehttp://www.ee.ic.ac.uk/docs/software/unix/programming/sys/transfer/pipe.html

[9] Interprocess Communication, Named Pipeshttp://www.cs.manchester.ac.uk/solaris/sun_docs/C/solaris_9/SUNWdev/NETPROTO/p18.html

9