pro prg unix windows pipe

25
Sunt prezentate cinci exemple de utilizare procese Unix. Exemplele sunt scrise in limbajul C. La sfarsitul fiecarui exemplu este data si sursa Python echivalenta. Primul exemplu ilustreaza legaturile dintre un proces parinte si un fiu al lui creat prin fork. Al doilea exemplu prezinta diferite aspecte legate de utilizarea apelurilor exec. Ultimele trei exemple rezolva trei probleme specifice in care se folosesc combinatii de apeluri sistem fork, exec, wait, exit, system Exemplul 1, utilizari fork. --------------------------- 1. Sursa fork1.c: #include <stdio.h> #include <stdlib.h> main() { int p; p=fork(); if (p == -1) {perror("fork imposibil!"); exit(1);} if (p == 0) { printf("Fiu: pid=%d, ppid=%d\n", getpid(), getppid()); } else { printf("Parinte: pid=%d ppid=%d\n", getpid(), getppid()); //wait(0); printf("Terminat fiul\n"); } } Rulare fork1 fara wait (este comentat): Parinte: pid=2704 ppid=2197 Terminat fiul Fiu: pid=2705, ppid=2704 Rulare fork1 cu wait: Parinte: pid=2715 ppid=2197 Fiu: pid=2716, ppid=2715 Terminat fiul Cauza: mesajul de terminare a fiului este dat de catre procesul parinte. In absenta lui wait, este posibil ca parintele sa primeasca controlul inaintea fiului si sa afiseze mesajul inainte ca fiul sa isi afiseze mesajul lui. Daca wait apare, atunci parintele asteapta efectiv terminarea fiului inainte de a afisa mesajul de terminare. Sursa Python echivalenta:

Upload: corneliu-daniel-luca

Post on 01-Oct-2015

62 views

Category:

Documents


0 download

DESCRIPTION

-

TRANSCRIPT

Sunt prezentate cinci exemple de utilizare procese Unix.Exemplele sunt scrise in limbajul C. La sfarsitul fiecaruiexemplu este data si sursa Python echivalenta.

Primul exemplu ilustreaza legaturile dintre un procesparinte si un fiu al lui creat prin fork.

Al doilea exemplu prezinta diferite aspecte legate de utilizarea apelurilor exec.

Ultimele trei exemple rezolva trei probleme specifice in care sefolosesc combinatii de apeluri sistem fork, exec, wait, exit, system

Exemplul 1, utilizari fork.---------------------------

1. Sursa fork1.c:#include #include main() { int p; p=fork(); if (p == -1) {perror("fork imposibil!"); exit(1);} if (p == 0) { printf("Fiu: pid=%d, ppid=%d\n", getpid(), getppid()); } else { printf("Parinte: pid=%d ppid=%d\n", getpid(), getppid()); //wait(0); printf("Terminat fiul\n"); }}

Rulare fork1 fara wait (este comentat):

Parinte: pid=2704 ppid=2197Terminat fiulFiu: pid=2705, ppid=2704

Rulare fork1 cu wait:

Parinte: pid=2715 ppid=2197Fiu: pid=2716, ppid=2715Terminat fiul

Cauza: mesajul de terminare a fiului este dat de catre procesulparinte. In absenta lui wait, este posibil ca parintele saprimeasca controlul inaintea fiului si sa afiseze mesajul inainteca fiul sa isi afiseze mesajul lui. Daca wait apare, atunciparintele asteapta efectiv terminarea fiului inainte de aafisa mesajul de terminare.

Sursa Python echivalenta:fork1.py--------

import osp=os.fork()if p == -1: print "fork imposibil!" exit(1) if p == 0: print "Fiu: pid="+`os.getpid()`+", ppid="+`os.getppid()`else: print "Parinte: pid="+`os.getpid()`+" ppid="+`os.getppid()` #os.wait() print "Terminat fiul"

2. Sursa fork2.c :#include #include main() { int p; printf("Parinte: pid=%d, ppid=%d\n", getpid(), getppid()); p=fork(); if(p==-1){perror("fork imposibil!"); exit(1);} if(p==0){ printf("Fiu: pid=%d, ppid=%d\n", getpid(), getppid()); //exit(2); } printf("pid=%d, ppid=%d\n", getpid(), getppid());}

Rulare fork2 fara exit(2) (este comentat):

Parinte: pid=2743, ppid=2197pid=2743, ppid=2197Fiu: pid=2744, ppid=2743pid=2744, ppid=2743

Rulare fork2 cu exit(2):

Parinte: pid=2755, ppid=2197pid=2755, ppid=2197Fiu: pid=2756, ppid=2755

Cauza: ultimul print din sursa apartine atat parintelui catsi fiului. Din cauza lui exit(2), in fiu nu se mai executaacest ultim print.

Sursa Python echivalenta: fork2.py --------

import osprint "Parinte: pid="+`os.getpid()`+", ppid="+`os.getppid()`p = os.fork()if p == -1: print "fork imposibil!" exit(1)if p == 0: print "Fiu: pid="+`os.getpid()`+", ppid="+`os.getppid()` exit(2)print "pid="+`os.getpid()`+", ppid="+`os.getppid()`

3. Sursa fork3.c:#include #include main() { int p, i; p=fork(); if (p == -1) {perror("fork imposibil!"); exit(1);} if (p == 0) { for (i=0; i MAXS) break; i++; strcpy(p, n); resp.lung += strlen(n)+1; p += strlen(n)+1; } closedir (dirp); resp.lung += PLUS; return &resp;}

Mesaj *dirPopen(int l, char *s) { static Mesaj resp; FILE *fin; char n[MAXL], *p, *q, comanda[MAXL]; int i; strcpy(comanda, "ls -1 *"); strcat(comanda, s); fin = popen(comanda, "r"); p = resp.s; resp.lung = 0; for (i=0; i MAXS) break; i++; strcpy(p, n); resp.lung += strlen(n)+1; p += strlen(n)+1; } pclose (fin); resp.lung += PLUS; return &resp;}

Ambele functii primesc intregul l si stringul s precizatiin enuntul problemei "lista a maximum l nume de fisiere din directorul curent al caror nume se termina cu s".Ambele functi intorc un pointer la un Mesaj care contine lista numelor. Mesajul contine in s succesiunea de stringuri (conventie C) cu numele fisierelor raspuns,iar lung suma lungimilor acestor stringuri (plus zerourile terminale), plus PLUS.

Functia dir obtine raspunsul folosind apelurile sistemopendir, readdir, closedir si structurile DIR*, struct dirent.

Functia dirPopen obtine raspunsul folosind apelul sistempopen. Cu acesta se apeleaza popen("ls -1 *.c"), daca stringuls este .c. Functia dirPopen capteaza iesirea standarda acestui apel popen, de unde extrage primele l linii.(Din executii se poate observa ca cele doua nu intorc neaparatnumele acelorasi fisiere!).

Pentru comunicarea prin pipe si prin FIFO structura mesajuluieste descrisa in sursa mesaj.h: -------#define MAXS 10000#define MAXL 1000

typedef struct { int lung; int i; char s[MAXS];} Mesaj;#define PLUS (sizeof(int))

Citirea / scrierea unui Mesaj la comunicarea prin pipe sauFIFO se face in doua etape: 1. Se scrie / citeste lung. 2. Se fac scrieri / citiri repetate, pana cand sunt schimbati toti octetii continutului mesajului. De ce a fost necesara o astfel de abordare?Din cauza functionarii apelurilor sistem read si write.Aceste apeluri sunt atomice, insa nu asigura schimbulnumarului total de octeti solicitati (al treilea argumental apelurilor read si write). Aceste apeluri intorc, lasfarsit, numarul de octeti efectiv schimbati.Din aceasta cauza este dificila comunicarea prin pipe sauFIFO intre mai mult de doua procese: procesele care citescnu stiu de la ce procese cititoare isi preia octetii!

Pentru implementarea schimbului de mesaje sunt folositefunctiile Read, Write, ReadMes, WriteMes. ---- ----- ------- --------Primele doua aplica repetat apeluri read, writepana la schimbarea a exact n octeti, n parametru de intrare.ReadMes si WriteMes schimba mai intai lungimea continutului, dupa care cere schimbul complet al acestuia.

Cele patru functii sunt prezentate in sursa ReadWrite.c -----------void Read(int f, char *t, int n) { char *p; int i, c; for (p=t, c=n; ; ) { i = read(f, p, c); if (i == c) return; c -= i; p += i; }}

void Write(int f, char *t, int n) { char *p; int i, c; for (p=t, c=n; c; ) { i = write(f, p, c); if (i == c) return; c -= i; p += i; }}

Mesaj *ReadMes(int canal) { static Mesaj mesaj; read(canal, (char*)&mesaj.lung, sizeof(int)); Read(canal, (char*)&mesaj+sizeof(int), mesaj.lung); return &mesaj;}

void WriteMes(int canal, Mesaj *pm) { write(canal, (char*)pm, sizeof(int)); Write(canal, (char*)pm+sizeof(int), pm->lung);}

Actiunea principala a serverului este dirijata de functia

void parinte(int in, in out) -------

Parametrii sunt handle ale canalelor prin care citeste mesajede la clienti, respectiv scrie raspunsurile spre clienti.Dupa caz, aceste handle pot fi descriptori pipe, descriptoriFIFO, sau, cum vom vedea in laboratoarele viitoare, descriptori de memorie partajata sau de cozi de mesaje. Actiunea acestei functii este simpla: citeste in mod repetatcate un mesaj de la un client, apeleaza dir (sau dirPopen),dupa care scrie in mesaj raspunsul pentru client.

Sursa parinte.c este: ---------void parinte(int in, int out) { Mesaj *pm; for ( ; ; ) { pm = ReadMes(in); //pm = dirPopen(pm->i, pm->s); pm = dir(pm->i, pm->s); WriteMes(out, pm); }}

Actiunea principala a clientului este dirijata de functia

void fiu(int in, in out) ---

La fel ca la server, parametrii sunt handle ale canalelor prin care citeste mesaje de la server, respectiv scrie cereri catre server.Dupa caz, aceste handle pot fi descriptori pipe, descriptoriFIFO, sau, cum vom vedea in laboratoarele viitoare, descriptori de memorie partajata sau de cozi de mesaje. Clientul citeste in mod repetat de la intrarea standardcate un numar l si un string s.Dupa fiecare citire prepara un mesaj, pe care il trimitela server.Apoi citeste mesajul de raspuns primit de la server si illisteaza pe iesirea standard.

Sursa fiu.c descrie actiunea clientului: -----void fiu(int in, int out) { Mesaj *pm, mesaj; char *pc,linie[MAXL]; int i; for ( ; ; ) { printf("Dati: numar|sufix: "); pc = (char*)fgets(linie, MAXL, stdin); if (pc == NULL) break; linie[strlen(linie)-1] = '\0'; pc = strstr(linie, "|"); if (pc == NULL) continue; mesaj.i = atoi(linie); strcpy(mesaj.s, pc+1); mesaj.lung = PLUS + strlen(mesaj.s) + 1; WriteMes(out, &mesaj); pm = ReadMes(in);

pc = pm->s; printf("%d\n",pm->lung); for (i = PLUS; i < pm->lung; ) { printf("%d %s\n", i, pc); i += strlen(pc) + 1; pc += strlen(pc) + 1; } }}

In sfarsit, vom prezenta programele principale.

Prima varianta implementeaza comunicarea prin pipe si estedescrisa in sursa pipe.c: -------#include #include #include #include #include "mesaj.h"#include "ReadWrite.c"#include "dir.c"#include "parinte.c"#include "fiu.c"main() { int fp[2], pf[2], pid; // Doua pipe, fp: fiu->parinte, pf: parinte->fiu if (pipe(fp) < 0 || pipe(pf) < 0) exit(1); pid = fork(); if (pid < 0) exit(2); if (pid > 0) { // Codul serverului (parintelui) fclose(stdin); fclose(stdout); close(fp[1]); close(pf[0]); parinte(fp[0], pf[1]); } else { // Codul clientului (fiului) close(fp[0]); close(pf[1]); fiu(pf[0], fp[1]); close(pf[0]); close(fp[1]); }}

De remarcat faptul ca atat la server cat si la client suntinchise toate fisierele care nu sunt necesare. Prin aceastase reduce consumul global de resurse (fisiere deschise),precum si securizeaza respectarea directiei de transmitere.

Varianta a doua implementeaza comunicarea prin FIFO.

Mai intai sunt creeate in directorul curent doua FIFO-uri:

$ mkfifo fifo1$ mkfifo fifo2

Inainte de aceasta, daca aceste FIFO-uri deja exista, ele sesterg cu:

$ rm fifo1$ rm fifo2

Deoarece si clientul si serverul sunt ai aceluiasi user, ampreferat ca cele doua FIFO-uri sa se afle in directorul curent. Daca cele doua procese apartin la useri diferiti, atunci este convenabil ca FIFO-urile sa fie /tmp/fifo1 si /tmp/fifo2.

Sursa serverului este fifos.c: --------#include #include #include #include #include #include "mesaj.h"#include "ReadWrite.c"#include "dir.c"#include "parinte.c"main() { int f1, f2; fclose(stdin); fclose(stdout); f1 = open("fifo1", O_WRONLY); f2 = open("fifo2", O_RDONLY); parinte(f2, f1);}

Sursa clientului este fifoc.c: -------#include #include #include #include "mesaj.h"#include "ReadWrite.c"#include "fiu.c"main() { int f1, f2; f1 = open("fifo1", O_RDONLY); f2 = open("fifo2", O_WRONLY); fiu(f1, f2); close(f1); close(f2);}

Iata cateva executii cu popen sau fara, cu pipe sau FIFO ---------------

florin@florin-laptop:~/probleme/UPipe-H$ #dirflorin@florin-laptop:~/probleme/UPipe-H$ gcc pipe.cflorin@florin-laptop:~/probleme/UPipe-H$ ./a.outDati: numar|sufix: 5|.c474 fiu.c10 ReadWrite.c22 parinte.c32 fifos.c40 pipe.cDati: numar|sufix: ^Cflorin@florin-laptop:~/probleme/UPipe-H$ #dirPopenflorin@florin-laptop:~/probleme/UPipe-H$ gcc pipe.cflorin@florin-laptop:~/probleme/UPipe-H$ ./a.outDati: numar|sufix: 5|.c434 fifoc.c12 fifos.c20 fiu.c26 parinte.c36 pipe.cDati: numar|sufix: ^Cflorin@florin-laptop:~/probleme/UPipe-H$ mkfifo fifo1florin@florin-laptop:~/probleme/UPipe-H$ mkfifo fifo2florin@florin-laptop:~/probleme/UPipe-H$ #dirPopenflorin@florin-laptop:~/probleme/UPipe-H$ gcc -o s fifos.cflorin@florin-laptop:~/probleme/UPipe-H$ gcc -o c fifoc.cflorin@florin-laptop:~/probleme/UPipe-H$ ./s&[1] 2066florin@florin-laptop:~/probleme/UPipe-H$ ./cDati: numar|sufix: 5|.c434 fifoc.c12 fifos.c20 fiu.c26 parinte.c36 pipe.cDati: numar|sufix: ^C florin@florin-laptop:~/probleme/UPipe-H$ #dirflorin@florin-laptop:~/probleme/UPipe-H$ kill 2066florin@florin-laptop:~/probleme/UPipe-H$ rm fifo1[1]+ Terminated ./sflorin@florin-laptop:~/probleme/UPipe-H$ rm fifo2florin@florin-laptop:~/probleme/UPipe-H$ mkfifo fifo1florin@florin-laptop:~/probleme/UPipe-H$ mkfifo fifo2florin@florin-laptop:~/probleme/UPipe-H$ gcc -o s fifos.cflorin@florin-laptop:~/probleme/UPipe-H$ gcc -o c fifoc.cflorin@florin-laptop:~/probleme/UPipe-H$ ./s&[1] 2142florin@florin-laptop:~/probleme/UPipe-H$ ./cDati: numar|sufix: 5|.c474 fiu.c10 ReadWrite.c22 parinte.c32 fifos.c40 pipe.cDati: numar|sufix:

Sursele Python echivalente:Mesaj.py--------

class Mesaj: MAXS = 10000 SIZEOFINT = 10 # privit ca text PLUS = SIZEOFINT lung = 0 i = 0 s = [""] def __init__(self, ser): if ser == None: return self.lung = int(ser[:self.SIZEOFINT]) self.i = int(ser[self.SIZEOFINT:2*self.SIZEOFINT]) self.s = ser[2*self.SIZEOFINT:self.SIZEOFINT+self.lung].split("|")

def __str__(self): ser = "" for l in self.s: ser += l+"|" ser = ser[:-1] ser = self.i2s(self.lung)+self.i2s(self.i)+ser return ser def i2s(self, i): sir = "000000000000000000"+`i` if sir.endswith("L"): sir = sir[:-1] return sir[-self.SIZEOFINT:]

dir.py------

import osimport Mesajdef dir(l, s): mesaj = Mesaj.Mesaj(None) mesaj.s = [] lung = 0 i = 1 for linie in os.listdir("."): if i > l: break if lung + len(linie) + len(mesaj.s) > mesaj.MAXS: break if len(linie) < len(s): continue if linie[len(linie)- len(s):] != s: continue mesaj.s += [linie] i += 1 lung += len(linie) mesaj.lung = mesaj.PLUS + lung + len(mesaj.s) - 1 if len(mesaj.s) == 0: mesaj.lung += 1 return mesaj

def dirPopen(l, s): mesaj = Mesaj.Mesaj(None) mesaj.s = [] lung = 0 i = 1 for linie in os.popen("ls -1", "r").readlines(): linie = linie[:-1] if i > l: break if lung + len(linie) + len(mesaj.s) > mesaj.MAXS: break if len(linie) < len(s): continue if linie[len(linie)- len(s):] != s: continue mesaj.s += [linie] i += 1 lung += len(linie) mesaj.lung = mesaj.PLUS + lung + len(mesaj.s) - 1 if len(mesaj.s) == 0: mesaj.lung += 1 return mesaj

ReadWrite.py------------

import Mesajimport osdef Read(f, n): c = n sir = "" while c > 0: s = os.read(f, c); sir += s c -= len(s) return sir

def Write(f, sir): c = len(sir) p = 0 while c > 0: i = os.write(f, sir[p:]) c -= i p += i

def ReadMes(canal): lung = os.read(canal, Mesaj.Mesaj.SIZEOFINT) ser = Read(canal, int(lung)) return Mesaj.Mesaj(lung+ser)

def WriteMes(canal, mesaj): lung = mesaj.SIZEOFINT+mesaj.lung Write(canal, str(mesaj)[:lung])

parinte.py----------

import ReadWriteimport dirdef parinte(iN, out): while True: mesaj = ReadWrite.ReadMes(iN) mesaj = dir.dir(mesaj.i, mesaj.s[0]) #mesaj = dir.dirPopen(mesaj.i, mesaj.s[0]) ReadWrite.WriteMes(out, mesaj)

fiu.py------

import sysimport Mesajimport ReadWritedef fiu(iN, out): while True: print "Dati: numar|sufix: ", linie = sys.stdin.readline() if not linie: break linie = linie[:-1] pc = linie.find("|") if pc < 0: continue mesaj = Mesaj.Mesaj(None) mesaj.s = [] mesaj.i = int(linie[:pc]) mesaj.s += [linie[pc+1:]] mesaj.lung = mesaj.PLUS + len(mesaj.s[0])

ReadWrite.WriteMes(out, mesaj)

mesaj = ReadWrite.ReadMes(iN)

for l in mesaj.s: print l

pipe.py-------

import sysimport osimport parinteimport fiudef main(): fp = os.pipe() pf = os.pipe() pid = os.fork() if pid < 0: exit(2) if pid > 0: # Codul serverului (parintelui) sys.stdin.close() sys.stdout.close() os.close(fp[1]) os.close(pf[0]) parinte.parinte(fp[0], pf[1]) else: # Codul clientului (fiului) os.close(fp[0]) os.close(pf[1]) fiu.fiu(pf[0], fp[1]) os.close(pf[0]) os.close(fp[1])main()

fifos.py--------

import sysimport osimport parintedef main(): sys.stdin.close() sys.stdout.close( f1 = os.open("fifo1", os.O_WRONLY, 0666) f2 = os.open("fifo2", os.O_RDONLY, 0666) parinte.parinte(f2, f1)main()

fifoc.py--------

import osimport fiudef main(): f1 = os.open("fifo1", os.O_RDONLY, 0666) f2 = os.open("fifo2", os.O_WRONLY, 0666) fiu.fiu(f1, f2) os.close(f1) os.close(f2)main()

Pentru un n dat (5