Sunteți pe pagina 1din 10

1.

Comunicaia ntre procese (IPC)

1.1. Mecanismul IPC


Comunicaia ntre procese este un mecanism prin care diferite
procese interacioneaz i comunic date ntre ele. Aplicaiile, care
folosesc IPC pentru interaciune pot fi mprite n dou categorii:
aplicatii server i aplicaii client. Aplicaia server asigur aplicaiei
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. Aplicaia care creeeaz pipe-ul se numeste aplicaie
pipe server iar, aplicaia care se conecteaz la ea se numete aplicaie
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, elibernd astfel spatiu pentru procesul care scrie n
pipe.

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

Un pipe nenumit este o conduct de date care transfera datele


ntre procesele nrudite (exemplu: ntre procesul tat i fiu). Nu
suport comunicaia n retea i sunt ntotdeauna orientate bytestream.

Un pipe numit asigur comunicaia ntr-un sens sau n ambele


ntre pipe server i pipe client. Poate fi folosita sa interacioneze ntre
procese nu neaprat nrudite pe maini diferite, n retea. [3]
Pipe-ul numit mai este ntlnit i sub denumirea de pipe FIFO.
Numele unui pipe numit este de fapt un fiier n sistemul de fiiere.
Pipe-urile numite sunt vizibile cu comanda ls ca orice alt fiier cu
cteva diferene:
% ls -l fifo1
prw-r--r-1 mconstan

e214

0 May 22 20:15 fifo1|

Litera p din stnga 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 primete unul sau mai multe nume de fiiere 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 evidenia cum lucreaz un pipe numit se execut n console


separate comenzile :
ls -l > pipe
cat < pipe

Rezultatul primei comenzi se va afia n cea de-a doua consol. [4]

1.2. IPC (Modelul Master-Slave)

Acest mod de comunicaie reprezint o relaie 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 su de adresa
virtual care este independent de spatiul tatalui. Un fiu poate moteni
handler-urile deschise de la tat. Un handler motenit n procesul fiu
refer la acelai obiect ca handler-ul original al tatlui. Cnd un
proces fiu moteneste handler-ul, sistemul de operare asigur numai
acces la procesul fiu. Un proces fiu are un nou spaiu de adres
virtual, n consecint tatl comunic valoarea handler-ului fiului.
Acest valoare poate fi trimis prin mai multe ci:
2

Prin argument n linie de comand

Prin obiecte de mapare fiier

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

Motenirea handler-ului determin procesul fiu s aib intrrile


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 pipeuri


n sisteme mai complexe, se descoper c comunicarea ntr-un sens
este prea limitat. Se dorete astfel s se comunice n ambele sensuri:
de la tat la fiu i de la fiu la tat. Acest lucru se face relativ uor
folosind dou pipe-uri, cte una n fiecare sens. Ins acest mod poate
duce la apariia unei situaii numit deadlock (blocare).

Deadlock-ul reprezint o situaie n care un grup de dou sau mai


multe procese ateapt 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 situaie poate fi ntlnit atunci cnd dou procese comunic


prin 2 pipe-uri. Sunt prezentate 2 posibile scenarii:

Amndou pipe-urile sunt libere i ambele procese ncearc s


citeasc din captul de citire. Fiecare este blocat pe citire
(pentru c pipe-ul este gol), i vor rmne blocate nedefinit.

Fiecare pipe are un buffer limitat asociat. Cnd un proces scrie


n pipe, datele sunt plasate n bufferul pipe-ului, pn cnd 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 captul pipe-ul lui de scriere,

ambele se vor bloca pe apelul de sistem write(). Cum nici un alt


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

1.4. Exemple

Apelul pipe aa 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)


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 funciei pipe() returneaz o pereche de descriptori. Unul


dintre aceti descriptori este conectat la captul de scriere al pipeului iar, cellalt este conectat la captul de citire. Orice poate fi scris
n pipe i citit de la celalalt capt 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

Cnd interpretorul examineaz linia de comand, gsete caracterul |


ce separ dou comenzi. Shell-ul execut ambele comenzi, conectnd
ieirea prime la intrarea celei de a dou. Exemplul de mai sus
folosete 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 pipe
ls -l > pipe
cat < pipe

Un exemplu care creeaz, scrie i citete dintr-un pipe:


#include
#include
#include
#include

<stdio.h>
<stdlib.h>
<errno.h>
<unistd.h>

int main()
{
int pfds[2];
char buf[30];
if (pipe(pfds) == -1) // se testeaz reuita apelului pipe
{
}

perror("pipe");
exit(1);

printf("writing to file descriptor #%d\n", pfds[1]);


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

Apelul pipe() primete un vector de 2 ntregi (ints) c parametru.


Presupunnd c nu apare nici o eroare, conecteaz 2 descriptori de
fiiere i i ntoarce ntr-un vector. Primul element este captul de
citire iar, secundul cel de scriere.[ Figure 1 -1 ]
Vom folosi alt exemplu. Inti procesul tata va crea un fiu. Vom apela
fork(). Astfel fiul va putea s trimit date spre captul de scriere al
pipe-ului, iar tatal le va primi la captul 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 arta astfel:


PARENT:
CHILD:
CHILD:
PARENT:

reading from pipe


writing to the pipe
exiting
read "test"

n acest caz tatal ncearc sa citeasc din pipe nainte ca fiul s scrie
n ea. Cnd acest lucru se ntmpl, tatal trece n block sau sleep pna

urmeaza ca s soseasc date pentru citire. Tatl ncearc s citeasc,


trece n sleep, fiul scrie i iese, tatl se trezeste i citeste datele.

Exerciiul 1:
Implementarea comenzii ls | wc -l n C.
Rezolvare:
Se vor folosi apelurile exec() i dup(). Familia exec de funcii
nlocuiete procesul curent ce ruleaz cu orice proces i este trimis de
exec(). Acesta este funcia 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);
/*
dup(pfds[1]);
/*
close(pfds[0]); /*
execlp("ls", "ls",
} else {
close(0);
/*
dup(pfds[0]);
/*
close(pfds[1]); /*
execlp("wc", "wc",
}

close normal stdout */


make stdout same as pfds[1] */
we don't need this */
NULL);
close normal stdin */
make stdin same as pfds[0] */
we don't need this */
"-l", NULL);

Funcia close(1) elibereaz descriptorul de fisier 1 (stdout),


dup(pdfs[1]) copiaz captul 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 ctre ls va merge n pdfs[1]
(captul de scriere al pipe-ului). n mod similar funcioneaz i wc
numai c n sens invers. [2]

Observaie:
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 pn 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
fiier selectat folosind booleanul sau operaia pe apelul open(2). Dac
nici un alt proces nu este conectat la pipe cnd 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. Exerciii propuse


1) S se realizeze un pipe bidirecional (two-way) ntre printe i copil
ntr-un program C. De exemplu ambii pot transmite i primi semnale.
[6]
2) S se realizeze un program care folosete pipe-uri astfel: un proces
citete litere de la tastatur iar altul le convertete n majuscule i le
afieaz.[7]
3) Implementai comanda ps -xl | grep nume_proces folosind pipeuri.[8]
4) Sincronizai 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, existnd alte


tehnici IPC mai avantajoase. [2]

Bibliografie

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]

Little Unix Programmers Group (LUPG)'s Little Site


http://users.actcom.co.il/~choo/lupg/tutorials/multiprocess/multi-process.html
Beej's Guide to Unix Interprocess Communication
http://www.ecst.csuchico.edu/~beej/guide/ipc/
Unleashing anonymous pipes Part 1 By Dinesh Ahuja
http://www.codeproject.com/
Introduction to Named PipesBy Andy Vaught
http://www2.linuxjournal.com/article/2156
Interprocess Communication (IPC), Pipes
http://www.cs.cf.ac.uk/Dave/C/node23.html
pipe(2) - Linux man page
Pipe example
http://users.actcom.co.il/~choo/lupg/tutorials/multiprocess/two-way-pipe.c
Pipe example
http://www.ee.ic.ac.uk/docs/software/unix/programming/sys/tra
nsfer/pipe.html
Interprocess Communication, Named Pipes
http://www.cs.manchester.ac.uk/solaris/sun_docs/C/solaris_9/S
UNWdev/NETPROTO/p18.html

10