Cursul 14 Comunicarea interprocese Pipe Apelul sistem pipe

  • Slides: 45
Download presentation
Cursul 14 Comunicarea interprocese (Pipe)

Cursul 14 Comunicarea interprocese (Pipe)

 • Apelul sistem pipe. • Această funcţie oferă o modalitate de transmitere a

• Apelul sistem pipe. • Această funcţie oferă o modalitate de transmitere a datelor între două programe, fără a invoca un shell pentru a interpreta comanda solicitata. De asemenea, ne dă mai mult control asupra citirii şi scrierii de date. • Pipe-ul este un mecanism de comunicare unidirecţională între procese şi este similar în unele privinţe fişierelor. In cadrul unui proces , imaginea unui pipe este cea din fig. urm. Fd(0) Proces Fd(1) Pipe Nucleu • Procesele care comunică prin pipe trebuie să fie înrudite; de obicei un proces care creează un pipe va apela după aceea fork, iar pipe-ul se va folosi pentru comunicare între părinte şi fiu.

 • Apelul sistem pentru crearea unui pipe are prototipul: int pipe(int filedes[2]); •

• Apelul sistem pentru crearea unui pipe are prototipul: int pipe(int filedes[2]); • Apelul returnează 0 în caz de succes şi -1 în caz de eroare. Prin filedes se returnează 2 descriptori de fişier, primul deschis pentru citire, al doilea pentru scriere. Se consideră că ieşirea lui filedes[1] este intrare pentru filedes[0]. Proces parinte Fd(0) Proces fiu Fd(1) Nucleu Fd(0) Fd(1) Pipe • Practic pipe-ul se foloseşte numai pentru comunicarea între procese, fiecare proces închizând capătul neutilizat.

 • Atunci când unul din capetele pipe-ului este închis, se aplică următoarele reguli:

• Atunci când unul din capetele pipe-ului este închis, se aplică următoarele reguli: – Dacă se citeşte dintr-un pipe al cărui capăt de scriere a fost închis, după citirea tuturor datelor read va returna 0 – Dacă se scrie într-un pipe al cărui capăt de citire nu mai este deschis de nici un proces se generează semnalul SIGPIPE. Dacă acest semnal este ignorat sau este interceptat şi se revine din funcţia de tratare, revine şi apelul write, care returnează -1 iar errno va avea valoarea EPIPE • Frecvent descriptorii pentru un pipe se duplică în descriptori pentru intrarea standard sau ieşirea standard. Astfel un proces fiu va putea lansa în execuţie (apel exec) un alt program, care va avea intrarea standard sau ieşirea standard în pipe.

 • Exemplu. #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string.

• Exemplu. #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> int main() { int data_processed; int file_pipes[2]; const char some_data[] = “ 123”; char buffer[BUFSIZ + 1]; memset(buffer, ‘’, sizeof(buffer)); if (pipe(file_pipes) == 0) { data_processed = write(file_pipes[1], some_data, strlen(some_data)); printf(“Wrote %d bytesn”, data_processed); data_processed = read(file_pipes[0], buffer, BUFSIZ); printf(“Read %d bytes: %sn”, data_processed, buffer); exit(EXIT_SUCCESS); } exit(EXIT_FAILURE); }

 • Observam tabloul de pointeri file_pipes, este transmis functiei pipe ca un parametru.

• Observam tabloul de pointeri file_pipes, este transmis functiei pipe ca un parametru. • Cind se executa acest program, iesirea este: $. /pipe 1 Wrote 3 bytes Read 3 bytes: 123 • Cum lucreaza programul. • Programul creează un pipe utilizând cei doi descriptori de fisiere file_pipes[]. Acesta scrie datele în conducta folosind descriptorul de fişier file_pipes[1] şi le citeşte-o înapoi de la file_pipes[0]. • Observaţi că conducta are un tampon intern care stochează datele între apelurile de scriere/citire. • Observatii. 1. Se utilizează apeluri de nivel inferior de citire şi scriere pentru a accesa datele, si nu fread şi fwrite, deoarece descriptorii returnati sunt de fisier si nu de flux. 2. Acest exemplu de conductă, nu ofera ceea ce nu am fi putut face cu un simplu fişier. Avantajul real al pipe-urilor vine atunci când se transmit date între două procese. Atunci când un program creează un nou proces, utilizând apelul fork, descriptorii de fişiere care au fost deschisi anterior rămân deschise. Prin crearea unei conducte în procesul original şi apoi bifurcare pentru a crea un nou proces, putem trece datele de la un proces la altul în jos prin pipe.

 • Exemplu. Utilizare de pipe-uri peste un fork (pipe 2. c. ). •

• Exemplu. Utilizare de pipe-uri peste un fork (pipe 2. c. ). • 1. Se începe la fel ca şi în primul exemplu, până facem apelul fork. #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> int main() { int data_processed; int file_pipes[2]; const char some_data[] = “ 123”; char buffer[BUFSIZ + 1]; pid_t fork_result; memset(buffer, ‘’, sizeof(buffer)); if (pipe(file_pipes) == 0) { fork_result=fork(); if (fork_result==-1) { fprintf(stderr, “Fork failure”); exit(EXIT_FAILURE); }

/* Dacă fork_result este egal cu zero, suntem în procesul de copil */ if

/* Dacă fork_result este egal cu zero, suntem în procesul de copil */ if (fork_result == 0) { data_processed = read(file_pipes[0], buffer, BUFSIZ); printf(“Read %d bytes: %sn”, data_processed, buffer); exit(EXIT_SUCCESS); } /* Altfel, trebuie sa fim in procesul parinte */ else { data_processed = write(file_pipes[1], some_data, strlen(some_data)); printf(“Wrote %d bytesn”, data_processed); } } exit(EXIT_SUCCESS); } • Când vom rula acest program, se obtine aceeasi ieşire, ca şi mai înainte. • Cum funcţionează. În primul rând, programul creează o conducta cu apelul pipe. Apoi, foloseste apelul fork pentru a crea un nou proces. În cazul în care apelul a fost cu succes, părintele scrie date în conducta, în timp ce fiul citeşte date din conducta. Observam ca avem procese separate pentru citirea şi scrierea datelor.

 • Exemplu de utilizare a unui pipe intern pentru comunicatia intre 2 procese,

• Exemplu de utilizare a unui pipe intern pentru comunicatia intre 2 procese, folosind functii I/O de nivel scazut (pipe-ex 1. c ). #include <stdio. h> #include <errno. h> extern int errno; #define NMAX 1000 void main() { int pid, p[2]; char ch; /* creare pipe intern */ if(pipe(p) == -1) { fprintf(stderr, "Error: can't open a channel, errno=%dn", errno); exit(1); }

/* creare proces fiu */ if( (pid=fork()) == -1) { fprintf(stderr, "Error: can't create

/* creare proces fiu */ if( (pid=fork()) == -1) { fprintf(stderr, "Error: can't create a child!n"); exit(2); } if(pid) { /* in tata */ /* tatal isi inchide capatul Read */ close(p[0]); /* citeste caractere de la tastatura, */ /* pentru terminare: CTRL+D (i. e. EOF) */ /*si le transm. doar pe acelea care sunt lit. */ /* mici */

while( (ch=getchar()) != EOF) if((ch>='a') && (ch<='z')) write(p[1], &ch, 1); /* tatal isi inchide

while( (ch=getchar()) != EOF) if((ch>='a') && (ch<='z')) write(p[1], &ch, 1); /* tatal isi inchide capatul Write, */ /*ptr. ca fiul sa poata citi EOF din pipe */ close(p[1]); /* asteapta terminarea fiului */ wait(0); } else { /* in fiu */ char buffer[NMAX]; int n. Index = 0; /* fiul isi inchide capatul Write */ close(p[1]);

/* fiul citeste caracterele din pipe si salveaza in /* /* buffer, pina depisteaza

/* fiul citeste caracterele din pipe si salveaza in /* /* buffer, pina depisteaza EOF, apoi afiseaza /* /* continutul bufferului. */ while(read(p[0], &ch, 1) != 0) if(n. Index < NMAX) buffer[n. Index++] = ch; buffer[ (n. Index==NMAX) ? NMAX-1 : n. Index ] = ''; printf("Fiu: am citit buffer=%sn", buffer); /* fiul isi inchide capatul Read */ close(p[0]); /* Obs: nu mai era nevoie de acest close explicit, /* /* deoarece oricum toti descriptorii sunt inchisi la /* /* terminarea programului. */ } } • Efectul acestui program: procesul tata citeste un sir de caractere de la tastatura, sir terminat cu combinatia CTRL+D (i. e. , caracterul EOF ), si le transmite procesului fiu, prin intermediul canalului, doar pe acelea care sunt litere mici. Iar procesul fiu citeste din canal caracterele trasmise de procesul parinte si le afiseaza pe ecran.

 • Urmatorul pas logic legat de apelul pipe, este de a permite procesului

• Urmatorul pas logic legat de apelul pipe, este de a permite procesului fiu sa fie un program diferit de tatal lui; vom face acest lucru cu apelul exec. • O dificultate este ca noul proces are nevoie sa cunoasca ce descriptor de fisier sa acceseze. • In exemplele anterioare, aceasta nu a fost o problema, deoarece procesul fiu are acces la descriptorii file_pipes. • Dupa un apel exec, acest lucru nu va mai fi valabil, pentru ca vechiul proces este inlocuit de noul proces, corespunzator noului program. • Procesului nou ii este transmis descriptorul de fisier ca un parametru. • Pentru a arăta modul în care acest lucru funcţionează, avem nevoie de două programe. Primul este producător de date. Aceasta creează conducta şi apoi invocă noul program, consumatorul de date.

 • Exemplu. Pipe si exec (pipe 3. c). • 1. pipe 3. c

• Exemplu. Pipe si exec (pipe 3. c). • 1. pipe 3. c se obtine prin modificarea lui pipe 2. c. Liniile pe care le-am schimbat sunt afişate cu caractere italice: #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> int main() { int data_processed; int file_pipes[2]; const char some_data[] = “ 123”; char buffer[BUFSIZ + 1]; pid_t fork_result; memset(buffer, ‘’, sizeof(buffer));

if (pipe(file_pipes) == 0) { fork_result = fork(); if (fork_result == (pid_t)-1) { fprintf(stderr,

if (pipe(file_pipes) == 0) { fork_result = fork(); if (fork_result == (pid_t)-1) { fprintf(stderr, “Fork failure”); exit(EXIT_FAILURE); } if (fork_result == 0) { sprintf(buffer, “%d”, file_pipes[0]); (void)execl(“pipe 4”, buffer, (char *)0); exit(EXIT_FAILURE); } else { data_processed = write(file_pipes[1], some_data, strlen(some_data)); printf(“%d - wrote %d bytesn”, getpid(), data_processed); } } exit(EXIT_SUCCESS); }

 • 2. Programul consumator care citeste date (pipe 4. c): #include <unistd. h>

• 2. Programul consumator care citeste date (pipe 4. c): #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> int main(int argc, char *argv[]) { int data_processed; char buffer[BUFSIZ + 1]; int file_descriptor; memset(buffer, ‘’, sizeof(buffer)); sscanf(argv[1], “%d”, &file_descriptor); data_processed = read(file_descriptor, buffer, BUFSIZ); printf(“%d - read %d bytes: %sn”, getpid(), data_processed, buffer); exit(EXIT_SUCCESS); } • Lansarea in executie a lui pipe 3: $. /pipe 3 980 - wrote 3 bytes 981 - read 3 bytes: 123

 • Cum lucreaza: Programul pipe 3 incepe la fel ca cel din exemplul

• Cum lucreaza: Programul pipe 3 incepe la fel ca cel din exemplul anterior, folosind apelul pipe pt. a crea un pipe si apoi, folosind apelul fork pt. a crea un nou proces. • Apoi foloseste sprintf pentru a stoca descriptorul de fisier conductă într-un tampon care va fi un argument al pipe 4. • Un apel execl este folosit pentru a invoca programul pipe 4. Argumente pentru a execl sunt: - programul invocat; - argv[0], care coresp. numelui programului - argv[1], care conţine descriptorul de fisier din care dorim ca programul să citească - (char *)0, care termina parametrii. • Programul pipe 4 extrage numărul descriptor de fisier din string-ul argument şi apoi citeşte din fisier pentru a obţine date.

 • • • FIFO (Pipe-uri cu nume) Una din limitările pipe-urilor, este necesitatea

• • • FIFO (Pipe-uri cu nume) Una din limitările pipe-urilor, este necesitatea ca procesele care comunică să fie înrudite; se elimină prin utilizarea unui pipe cu nume, numit pe scurt FIFO, care este un tip special de fişier. Fişierele FIFO combina caracteristicile fişierelor obişnuite cu cele ale fişierelor PIPE. Fişierele FIFO sunt caracterizate de faptul că fiecărui astfel de fişier îi corespunde o intrare în unul din cataloagele sistemului de fişiere, lucru care face posibilă referirea unui astfel de fişier prin intermediul numelui asociat acestuia. De asemenea, un fişier FIFO este deschis la fel ca fişierele obişnuite. După deschidere, fişierul FIFO capătă caracteristicile unui fişier PIPE. Datele se citesc strict în ordinea în care au fost depuse, iar apelurile de scriere şi citire în fişier se garantează a fi atomice. Nu este posibilă poziţionarea în fişier utilizând apelul de sistem lseek. Putem crea un FIFO in shell folosind comanda mkfifo, care sintaxa: $mkfifo [ -m mode ] pathname este numele FIFO-ului ce va fi creat; –m option este folosit pt. a specifica drepturile, in acelasi mod utiliz. de comanda chmod.

 • Exemplu. Crearea unui FIFO si afisarea informatiilor despre el: $mkfifo /tmp/my_fifo $ls

• Exemplu. Crearea unui FIFO si afisarea informatiilor despre el: $mkfifo /tmp/my_fifo $ls -l /tmp/my_fifo prw-rw-rw- 1 ion users 0 Jan 16 14: 04 /tmp/my_fifo • Obs. Primul caracter al iesirii lui ls este p, indicind tipul fisierului (p-pipe). • Scrierea intr-un FIFO se poate realiza cu o comanda cat, la fel ca intr-un fisier obisnuit, sau prin redirectarea iesirii unei comenzi. • Exemplu. $echo “sdsdfasdf” >/tmp/my_fifo • Citirea dintr-un FIFO se poate realiza cu o comanda cat, prin redirectarea iesirii. • Le putem lansa simultan, punind prima comanda in background. • Exemplu. $cat < /tmp/my_fifo & [1] 1316 $echo “sdsdfasdf” > /tmp/my_fifo sdsdfasdf [1]+ Done cat </tmp/my_fifo $ • Obs. 1. La inceput procesul cat este blocat in background. Cind echo produce date disponibile, cat le citeste si le afiseaza la iesirea standard. • 2. Dupa citire, cat se termina, fara a mai astepta alte date.

 • Apelul sistem pentru crearea unui FIFO este: int mkfifo(const char *pathname, mode_t

• Apelul sistem pentru crearea unui FIFO este: int mkfifo(const char *pathname, mode_t mode); • Primul argument este numele FIFO, iar al doilea defineste drepturile de proprietate, care sunt aceleaşi ca şi la fişiere. • Exemplu: Crearea unui FIFO (fifo 1. c). #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <sys/types. h> #include <sys/stat. h> int main() { int res = mkfifo(“/tmp/my_fifo”, 0777); if (res == 0) printf(“FIFO createdn”); exit(EXIT_SUCCESS); }

 • Daca vrem sa vedem inf. despre FIFO-ul creat, tastam: $ls -l. F

• Daca vrem sa vedem inf. despre FIFO-ul creat, tastam: $ls -l. F /tmp/my_fifo prwxr-xr-x 1 ion users 0 July 10 14: 55 /tmp/my_fifo| • Obs. ca primul caracter al liniei este p, indicind un pipe; simbol |, de la sfirsit este adaugat de catre optiunea –F a comenzii ls si indica un pipe. • Modul de Lucru. Programul utilizează functia mkfifo pt. a crea fisierul special. Drepturile asupra fisierului sunt determinate scazind din constanta octala 0777, valoarea stabilita prin umask; implicit aceasta este 022 si in acest caz drepturile vor fi codificate de 0755. • Obs. • 1. Spre deosebire de o conductă creata cu un apel pipe, un FIFO există ca un fişier cu nume, nu ca un descriptor de fisier şi trebuie să fie deschis înainte de a putea fi citite/scrise date din/in el. • 2. Pt. deschiderea si inchiderea unui FIFO, se utilizeaza aceleaşi funcţii folosite pt. fişiere, cu unele funcţionalităţi suplimentare; in apelul de deschidere este trecuta calea catre FIFO si nu numai numele de fisier. • După ce FIFO a fost creat, el poate fi deschis prin open, apoi toate operaţiile cu fişiere (close, read, write, unlink, etc. ) i se pot aplica, la fel ca altor fişiere.

 • Deschiderea fisierelor cu apelul open. • Deschiderea unui FIFO are o semantică

• Deschiderea fisierelor cu apelul open. • Deschiderea unui FIFO are o semantică oarecum neobişnuită. • În general, utilizarea unui FIFO este de a avea un proces de citire şi unul de scriere la fiecare capăt. Prin urmare, în mod implicit, deschiderea unui FIFO pentru citire (open() cu fanionul O_RDONLY) blocheaza procesul până cind un alt proces deschide FIFO pentru scriere (open() cu fanionul O_WRONLY). • Restricţia principala privind deschiderea unui FIFO, este faptul că un program nu poate deschide un FIFO pentru citire şi scriere cu O_RDWR. Dacă un program face acest lucru, rezultatul este nedefinit. Acest lucru este o restricţie nesemnificativa, deoarece, în mod normal, vom folosi un FIFO doar pentru a trimite date într-o singură direcţie, aşa că nu avem nevoie de O_RDWR. • Dacă vrem să transmitem date în ambele direcţii între programe, este mult mai bine să utilizam o pereche de FIFO-uri sau de conducte, una pentru fiecare direcţie sau sa schimbam în mod explicit direcţia fluxului de date prin închiderea şi redeschiderea FIFO.

 • Modul de comportare al unui FIFO după deschidere este afectat de fanionul

• Modul de comportare al unui FIFO după deschidere este afectat de fanionul O_NONBLOCK astfel: 1. Dacă O_NONBLOCK nu este specificat (cazul normal), atunci un open pentru citire se va bloca până când un alt proces deschide acelasi FIFO pentru scriere. Dacă deschiderea este pentru scriere, se poate produce blocare până când un alt proces il deschide pentru citire. Cu alte cuvinte, două procese care comunică printr-un fişier FIFO se aşteaptă unul pe celalalt la deschidere, pentru sincronizare, înainte de începerea comunicaţiei propriu-zise. Aceasta aşteptare nu mai are loc daca la deschidere se specifica semaforul O_NDELAY, caz în care daca la celalalt capăt deja exista un proces, atunci se produce sincronizarea, altfel apelul de deschidere este terminat cu eroare. 2. Dacă se specifică O_NONBLOCK, din deschiderea pentru citire se revine imediat, dar o deschidere pentru scriere poate returna eroare cu errno la valoarea ENXIO, dacă nu există un alt proces care a deschis acelaşi FIFO pentru citire.

 • Exista 4 combinatii valide ale fanioanelor O_RDONLY, O_WRONLY si O_NONBLOCK. 1. open(const

• Exista 4 combinatii valide ale fanioanelor O_RDONLY, O_WRONLY si O_NONBLOCK. 1. open(const char *path, O_RDONLY); • In acest caz, apelul open se va bloca; debl. se va face, când un alt proces deschide acelaşi FIFO pentru scriere. • Acesta este la fel cu exemplul legat de cat. 2. open(const char *path, O_RDONLY | O_NONBLOCK); • Apelul open se exec. cu succes, chiar dacă FIFO-ul nu a fost deschis pentru scriere de catre alt proces. 3. open(const char *path, O_WRONLY); • In acest caz, apelul open se va bloca; debl. se va face, când un alt proces deschide acelaşi FIFO pentru citire. 4. open(const char *path, O_WRONLY | O_NONBLOCK); • In acest caz , revenirea se va realiza imediat, dar în cazul în care nici un proces nu are FIFO deschis pentru citire, se va reveni cu o eroare ( -1), iar FIFO nu va fi deschis. Dacă un proces are deschis FIFO pentru citire, fişierul descriptor returnat poate fi folosit pentru a scrie date in FIFO.

 • Exemplu. Deschiderea de fisiere FIFO( fifo 2. c ). Cum putem folosi

• Exemplu. Deschiderea de fisiere FIFO( fifo 2. c ). Cum putem folosi comportarea lui open cu fanionul O_NONBLOCK, pt. a sincroniza două procese. Programul prezintă comportarea unui FIFO prin transmiterea a diferiti parametri. #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> #include <fcntl. h> #include <sys/types. h> #include <sys/stat. h> #define FIFO_NAME “/tmp/my_fifo” int main(int argc, char *argv[]) { int res; int open_mode = 0; if (argc < 2) { fprintf(stderr, “Usage: %s <some combination of O_RDONLY O_WRONLY O_NONBLOCK>n”, *argv); exit(EXIT_FAILURE);

 • /* Presupunând că programul a trecut testul, vom seta valoarea lui*/ /*open_mode

• /* Presupunând că programul a trecut testul, vom seta valoarea lui*/ /*open_mode , pentru acele argumente*/ argv++; if (strncmp(*argv, “O_RDONLY”, 8) == 0) open_mode |= O_RDONLY; if (strncmp(*argv, “O_WRONLY”, 8) == 0) open_mode |= O_WRONLY; if (strncmp(*argv, “O_NONBLOCK”, 10) == 0) open_mode |=O_NONBLOCK; argv++; if (*argv) { if (strncmp(*argv, “O_RDONLY”, 8) == 0) open_mode |= O_RDONLY; if (strncmp(*argv, “O_WRONLY”, 8) == 0) open_mode |= O_WRONLY; if (strncmp(*argv, “O_NONBLOCK”, 10) == 0) open_mode |= O_NONBLOCK; }

/* verificam daca FIFO-ul exista si il vom crea daca este necesar */ if

/* verificam daca FIFO-ul exista si il vom crea daca este necesar */ if (access(FIFO_NAME, F_OK) == -1) { res = mkfifo(FIFO_NAME, 0777); if (res != 0) { fprintf(stderr, “Could not create fifo %sn”, FIFO_NAME); exit(EXIT_FAILURE); } } printf(“Process %d opening FIFOn”, getpid()); res = open(FIFO_NAME, open_mode); printf(“Process %d result %dn”, getpid(), res); sleep(5); if (res != -1) (void)close(res); printf(“Process %d finishedn”, getpid()); exit(EXIT_SUCCESS); }

 • Cum lucrează. Acest program ne permite sa specificam in linia de comanda

• Cum lucrează. Acest program ne permite sa specificam in linia de comanda combinatii de O_RDONLY, O_WRONLY şi O_NONBLOCK pe care dorim sa le folosim. • Face acest lucru, comparind sirurile cunoscute cu parametri din linia de comandă şi prin setare (cu |=) a fanionului potrivit daca sirul se potriveşte. • Program foloseşte functia access ptr. a verifica daca fisierul FIFO deja exista si îl va crea dacă este necesar. • Nu se distruge FIFO-ul, deoarece nu se poate sti daca un alt program nu foloseste FIFO-ul.

 • Exemple de apeluri. Observ. lansarea în background a programului. 1. O_RDONLY şi

• Exemple de apeluri. Observ. lansarea în background a programului. 1. O_RDONLY şi O_WRONLY fără O_NONBLOCK $. /fifo 2 O_RDONLY & [1] 152 Process 152 opening FIFO $. /fifo 2 O_WRONLY Process 153 opening FIFO Process 152 result 3 Process 153 result 3 Process 152 finished Process 153 finished • Acesta este cel mai întâlnit mod de utilizare a pipe-urilor. El permite cititorului să înceapă şi să aştepte în apelul open şi apoi ambelor programe să continue când scriitorul deschide FIFO-ul. Obs. sincronizarea ambelor programe la apelul open. • Cînd un proces este blocat, el nu consumă resurse CPU, deci metoda este eficientă d. p. d. v. al CPU.

 • 2. O_RDONLY cu O_NONBLOCK şi O_WRONLY • În acest moment, procesul cititor

• 2. O_RDONLY cu O_NONBLOCK şi O_WRONLY • În acest moment, procesul cititor execută apelul open şi continuă imediat exec. , deşi nu este prezent nici-un proces scriitor. De as. scriitorul imediat efect. apelul open, deoarece FIFO-ul este deschis pentru citire. $. /fifo 2 O_RDONLY O_NONBLOCK & [1] 160 Process 160 opening FIFO $. /fifo 2 O_WRONLY Process 161 opening FIFO Process 160 result 3 Process 161 result 3 Process 160 finished Process 161 finished [1]+ Done fifo 2 O_RDONLY O_NONBLOCK • Obs. Aceste doua exemple sunt cele mai frecvente combinatii a utiliz. open.

 • Citirea şi scrierea din/in FIFO-uri • O_NONBLOCK afecteaza comportamentul apelurilor de citire/scriere

• Citirea şi scrierea din/in FIFO-uri • O_NONBLOCK afecteaza comportamentul apelurilor de citire/scriere pe FIFO-uri. • O citire pe un FIFO vid blocat ( adica unul care nu a fost deschis cu O_NONBLOCK ) va astepta pina cind vor fi date care pot fi citite. Invers, o citire dintr-un FIFO neblocat care nu contine date va returna 0 octeti. • O scriere pe un FIFO plin si blocat va astepta pina cind datele pot fi scrise. O scriere pe un FIFO ce nu poate accepta toti octetii ce urmeaza a fi scrisi, va: - esua, daca cererea este de cel mult PIPE_BUF octeti; - scrie o parte din date, daca cererea este mai mare de PIPE_BUF octeti, returnind numarul octetilor scrisi. • Dimensiunea unui mesaj FIFO este limitata de PIPE_BUF, definita in limits. h. In mod normal, aceasta este de 4096 octeti. • Daca se cere scrierea a PIPE_BUF octeti (sau mai putini) pe un FIFO deschis cu O_WRONLY (adica blocat), ori se vor scrie toti octetii ori niciunul. • Aceasta limitare este importanta, daca exista mai multi scriitori si un singur cititor.

 • Comunicarea inte procese cu FIFO (fifo 3. c, fifo 4. c). •

• Comunicarea inte procese cu FIFO (fifo 3. c, fifo 4. c). • 1. Primul program este producatorul; el creaza conducta (daca se cere) si apoi scrie date cit mai repede posibil. • Liniile scrise cu italic arata schimbarile, fata de fifo 2. c #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> #include <fcntl. h> #include <limits. h> #include <sys/types. h> #include <sys/stat. h> #define FIFO_NAME “/tmp/my_fifo” #define BUFFER_SIZE PIPE_BUF #define TEN_MEG (1024 * 10)

int main() { int pipe_fd; int res; int open_mode = O_WRONLY; int bytes_sent =

int main() { int pipe_fd; int res; int open_mode = O_WRONLY; int bytes_sent = 0; char buffer[BUFFER_SIZE + 1]; if (access(FIFO_NAME, F_OK) == -1) { res = mkfifo(FIFO_NAME, 0777); if (res != 0) { fprintf(stderr, “Could not create fifo %sn”, FIFO_NAME); exit(EXIT_FAILURE); } } printf(“Process %d opening FIFO O_WRONLYn”, getpid()); pipe_fd = open(FIFO_NAME, open_mode); printf(“Process %d result %dn”, getpid(), pipe_fd); if (pipe_fd != -1) {

while(bytes_sent < TEN_MEG) { res = write(pipe_fd, buffer, BUFFER_SIZE); if (res == -1) {

while(bytes_sent < TEN_MEG) { res = write(pipe_fd, buffer, BUFFER_SIZE); if (res == -1) { fprintf(stderr, “Write error on pipen”); exit(EXIT_FAILURE); } bytes_sent += res; } (void)close(pipe_fd); } else {exit(EXIT_FAILURE); } printf(“Process %d finishedn”, getpid()); exit(EXIT_SUCCESS); }

 • 2. Al doilea program, consumator, citeste datele din FIFO. #include <unistd. h>

• 2. Al doilea program, consumator, citeste datele din FIFO. #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> #include <fcntl. h> #include <limits. h> #include <sys/types. h> #include <sys/stat. h> #define FIFO_NAME “/tmp/my_fifo” #define BUFFER_SIZE PIPE_BUF int main() { int pipe_fd; int res; int open_mode = O_RDONLY; char buffer[BUFFER_SIZE + 1]; int bytes_read = 0;

memset(buffer, ‘�’, sizeof(buffer)); printf(“Process %d opening FIFO O_RDONLYn”, getpid()); pipe_fd = open(FIFO_NAME, open_mode); printf(“Process

memset(buffer, ‘’, sizeof(buffer)); printf(“Process %d opening FIFO O_RDONLYn”, getpid()); pipe_fd = open(FIFO_NAME, open_mode); printf(“Process %d result %dn”, getpid(), pipe_fd); if (pipe_fd != -1) { do { res = read(pipe_fd, buffer, BUFFER_SIZE); bytes_read += res; } while (res > 0); (void)close(pipe_fd); } else { exit(EXIT_FAILURE); } printf(“Process %d finished, %d bytes readn”, getpid(), bytes_read); exit(EXIT_SUCCESS); }

 • Ptr. lansarea in executie a programelor in acelasi timp, se utilizeaza a

• Ptr. lansarea in executie a programelor in acelasi timp, se utilizeaza a comanda time $. /fifo 3 & [1] 375 Process 375 opening FIFO O_WRONLY $time. /fifo 4 Process 377 opening FIFO O_RDONLY Process 375 result 3 Process 377 result 3 Process 375 finished Process 377 finished, 10485760 bytes read real 0 m 0. 053 s user 0 m 0. 020 s sys 0 m 0. 040 s [1]+ Done fifo 3

 • Cum lucreaza. Ambele programe folosesc FIFO-ul in modul blocare. Se lanseaza primul

• Cum lucreaza. Ambele programe folosesc FIFO-ul in modul blocare. Se lanseaza primul producatorul (fifo 3) , care se blocheaza asteptind ca cititorul sa deschida FIFO-ul. • Cind fifo 4 (consumatorul) este lansat, scriitorul este deblocat si incepe scrierea datelor in pipe. In acelasi timp, cititorul incepe citirea datelor din pipe. • Planificatorul realizeaza executia celor doua procese atunci cind este posibil; de as. scriitorul este blocat cind pipe-ul este plin si scriitorul este blocat cind pipe-ul este vid. • Iesirea comenzii time afiseaza timpii in executie si cantitatea de date citita.

 • Aplicatie Client/Server folosind FIFO • Vom considera un singur proces server ce

• Aplicatie Client/Server folosind FIFO • Vom considera un singur proces server ce accepta cereri, le proceseaza si returneaza datele rezultate din prelucrare procesului client. Presupunem ca exista mai multe procese client. • Datele transmise sunt impartite in blocuri, de dimensiune cel mult PIPE_BUF octeti. • Deoarece server-ul va prelucra, la un moment dat numai un bloc de informatii, va exista un singur FIFO din care citeste server-ul si in care scriu mai multi clienti. • Prin deschiderea FIFO-ului in modul blocare, server-ul si clientii se vor bloca automat atunci cind este necesar. • Pentru returnarea datelor catre clienti, va exista cite un pipe pentru fiecare client. • Prin transmiterea PID-ului clientului in cadrul datelor trimise catre server, ambele parti il pot folosi pt. a genera un nume unic ptr. pipe-ul de retur.

 • 1. Mai intii, avem nevoie de un fisier antet ( client. h

• 1. Mai intii, avem nevoie de un fisier antet ( client. h ), ce defineste datele comune atit pt. progr. client cit si server. El include antetele de sistem necesare. #include <unistd. h> #include <stdlib. h> #include <stdio. h> #include <string. h> #include <fcntl. h> #include <limits. h> #include <sys/types. h> #include <sys/stat. h> #define SERVER_FIFO_NAME “/tmp/serv_fifo” #define CLIENT_FIFO_NAME “/tmp/cli_%d_fifo” #define BUFFER_SIZE 20 struct data_to_pass_st { pid_t client_pid; char some_data[BUFFER_SIZE - 1]; };

 • 2. In programul server (server. c) se creaza si se deschide pipe-ul

• 2. In programul server (server. c) se creaza si se deschide pipe-ul server, setat read-only, cu blocare. Dupa “adormire”, server-ul citeste date de la client, de tipul data_to_pass_st. #include “client. h” #include <ctype. h> int main() { int server_fifo_fd, client_fifo_fd; struct data_to_pass_st my_data; int read_res; char client_fifo[256]; char *tmp_char_ptr; mkfifo(SERVER_FIFO_NAME, 0777); server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY); if (server_fifo_fd == -1) { fprintf(stderr, “Server fifo failuren”); exit(EXIT_FAILURE); } sleep(10); /* lets clients queue for demo purposes */ do { read_res = read(server_fifo_fd, &my_data, sizeof(my_data)); if (read_res > 0) {

 • 3. In aceasta urm. etapa, se prel. datele care au fos citite

• 3. In aceasta urm. etapa, se prel. datele care au fos citite de la client: Se convertesc toate caract. in some_data si combina CLIENT_FIFO_NAME cu client_pid primit. tmp_char_ptr = my_data. some_data; while (*tmp_char_ptr) { *tmp_char_ptr = toupper(*tmp_char_ptr); tmp_char_ptr++; } sprintf(client_fifo, CLIENT_FIFO_NAME, my_data. client_pid); • 4. Se transmit datele procesate inapoi, deschizind pipe-ul client in modul write-only, cu blocare. In final, se opreste FIFO-ul server, inchizind fisierul si anulind legatura la FIFO. client_fifo_fd = open(client_fifo, O_WRONLY); if (client_fifo_fd != -1) { write(client_fifo_fd, &my_data, sizeof(my_data)); close(client_fifo_fd); } } } while (read_res > 0); close(server_fifo_fd); unlink(SERVER_FIFO_NAME); exit(EXIT_SUCCESS); }

 • 5. Aici este prez. programul client ( client. c ). In prima

• 5. Aici este prez. programul client ( client. c ). In prima parte a acestui program se deschide server-ul FIFO, daca acesta exista, ca un fisier. Apoi isi obtine PID-ul sau, care face parte din datele care se transmit catre server. Este creat FIFO-ul client, ptr. a fi utiliz. in urm sectiune. #include “client. h” #include <ctype. h> int main() {int server_fifo_fd, client_fifo_fd; struct data_to_pass_st my_data; int times_to_send; char client_fifo[256]; server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY); if (server_fifo_fd == -1) { fprintf(stderr, “Sorry, no servern”); exit(EXIT_FAILURE); } my_data. client_pid = getpid(); sprintf(client_fifo, CLIENT_FIFO_NAME, my_data. client_pid); if (mkfifo(client_fifo, 0777) == -1) { fprintf(stderr, “Sorry, can’t make %sn”, client_fifo); exit(EXIT_FAILURE); }

 • 6. Pentru fiecare din urm. 5 ciclari, datele client sunt transmise server-ului.

• 6. Pentru fiecare din urm. 5 ciclari, datele client sunt transmise server-ului. Apoi FIFO-ul client este deschis ( read-only, modul blocare ) si datele sunt transmise inapoi. In final, FIFO-ul server este inchis si FIFO-ul client este sters. for (times_to_send = 0; times_to_send < 5; times_to_send++) { sprintf(my_data. some_data, “Hello from %d”, my_data. client_pid); printf(“%d sent %s, “, my_data. client_pid, my_data. some_data); write(server_fifo_fd, &my_data, sizeof(my_data)); client_fifo_fd = open(client_fifo, O_RDONLY); if (client_fifo_fd != -1) { if (read(client_fifo_fd, &my_data, sizeof(my_data)) > 0) { printf(“received: %sn”, my_data. some_data); } close(client_fifo_fd); } } close(server_fifo_fd); unlink(client_fifo); exit(EXIT_SUCCESS); }

 • Pt. a testa acesta aplicatie, avem nevoie sa executam o singura copie

• Pt. a testa acesta aplicatie, avem nevoie sa executam o singura copie a server si clientii severului. Lansarea in executie a unui server si cinci clienti, se face prin: $server & $for i in 1 2 3 4 5 do client & done $ • Iesirile clientilor: 531 sent Hello from 531, received: HELLO FROM 531 532 sent Hello from 532, received: HELLO FROM 532 529 sent Hello from 529, received: HELLO FROM 529 530 sent Hello from 530, received: HELLO FROM 530 531 sent Hello from 531, received: HELLO FROM 531 532 sent Hello from 532, received: HELLO FROM 532 • Obs. Iesirile sunt afisate intr-o ordine aleatoare.