This shows you the differences between two versions of the page.
sde:laboratoare:04_ro_python [2020/03/07 14:16] ioana_maria.culic [Exemplu (my_system)] |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laborator 4 - Procese ====== | ||
- | ===== Materiale ajutătoare ===== | ||
- | |||
- | * [[https://www.slideshare.net/alexandruradovici/sde-tp-4-processus-135693677 | Slide-uri]] | ||
- | * [[http://elf.cs.pub.ro/so/res/laboratoare/lab03-refcard.pdf | lab03-refcard.pdf]] | ||
- | * [[http://elf.cs.pub.ro/so/res/tutorial/lab-03-procese/|Video Procese]] | ||
- | |||
- | ==== Nice to read ==== | ||
- | * TLPI - Chapter 6, ''Processes'', Chapter 26 ''Monitoring Child Processes'' | ||
- | * WSP4 - Chapter 6, ''Process Management'' | ||
- | |||
- | ===== Link-uri către secțiuni utile ===== | ||
- | * [[#Crearea unui proces în Linux|Crearea unui proces]] | ||
- | * [[#Înlocuirea imaginii unui proces în Linux|Înlocuirea imaginii unui proces]] | ||
- | * [[#Așteptarea terminării unui proces în Linux|Așteptarea terminării unui proces]] | ||
- | * [[#Terminarea unui proces în Linux|Terminarea unui proces]] | ||
- | * [[#Exemplu (my_system)|Exemplu (my_system)]] | ||
- | * [[#Copierea descriptorilor de fișier|Copierea descriptorilor de fișier]] | ||
- | * [[#Moștenirea descriptorilor de fișier după operații fork/exec|Moștenirea descriptorilor de fișier după operații fork/exec]] | ||
- | * [[#Variabile de mediu în Linux|Variabile de mediu în Linux]] | ||
- | |||
- | ===== Prezentare concepte ===== | ||
- | |||
- | Un **proces** este un program în execuție. Procesele sunt unitatea primitivă prin care sistemul de operare alocă resurse utilizatorilor. Orice proces are un spațiu de adrese și unul sau mai multe fire de execuție. Putem avea mai multe procese ce execută același program, dar oricare două procese sunt complet independente. | ||
- | |||
- | Informațiile despre procese sunt ținute într-o structură numită ''Process Control Block'' ([[http://en.wikipedia.org/wiki/Process_control_block|PCB]]), câte una pentru fiecare proces existent în sistem. Printre cele mai importante informații conținute de PCB regăsim: | ||
- | *PID - identificatorul procesului | ||
- | *spațiu de adresă | ||
- | *registre generale, ''PC'' (contor program), ''SP'' (indicator stivă) | ||
- | *tabela de fișiere deschise | ||
- | *[[so:laboratoare-2013:laborator-06|informații referitoare la semnale]] | ||
- | *lista de semnale blocate, ignorate sau care așteaptă să fie trimise procesului | ||
- | *handler-ele de semnale | ||
- | *informațiile referitoare la sistemele de fișiere (directorul rădăcină, directorul curent) | ||
- | |||
- | |||
- | În momentul lansării în execuție a unui program, în sistemul de operare se va crea un proces pentru alocarea resurselor necesare rulării programului respectiv. Fiecare sistem de operare pune la dispoziție apeluri de sistem pentru lucrul cu procese: creare, terminare, așteptarea terminării. Totodată există apeluri pentru duplicarea descriptorilor de resurse între procese, ori închiderea acestor descriptori. | ||
- | |||
- | Procesele pot avea o organizare: | ||
- | *//ierarhică// - de exemplu pe Linux - există o structură arborescentă în care rădăcina este procesul ''init'' (pid = 1). | ||
- | *//neierarhică// - de exemplu pe Windows. | ||
- | |||
- | În general, un proces rulează într-un mediu specificat printr-un set de **variabile de mediu**. O variabilă de mediu este o pereche ''NUME = valoare''. Un proces poate să verifice sau să seteze valoarea unei variabile de mediu printr-o serie de apeluri de bibliotecă ([[ #variabile de mediu in linux |Linux]], [[ #variabile de mediu in windows | Windows]]). | ||
- | |||
- | **Pipe-urile** (canalele de comunicație) sunt mecanisme primitive de comunicare între procese. Un pipe poate conține o cantitate limitată de date. Accesul la aceste date este de tip FIFO (datele se scriu la un capăt al pipe-ului pentru a fi citite de la celălalt capăt). Sistemul de operare garantează sincronizarea între operațiile de citire și scriere la cele două capete | ||
- | ([[ #pipe-uri in linux |Linux]], [[ #pipe-uri in windows | Windows]]). | ||
- | |||
- | Există două tipuri de pipe-uri: | ||
- | *pipe-uri **anonime**: pot fi folosite doar de procese **înrudite** (un proces părinte și un copil sau doi copii), deoarece sunt accesibile doar prin moștenire. Aceste pipe-uri nu mai există după ce procesele și-au terminat execuția. | ||
- | *pipe-uri **cu nume**: au suport fizic - există ca fișiere cu drepturi de acces. Prin urmare, ele vor exista independent de procesul care le creează și pot fi folosite de procese neînrudite. | ||
- | |||
- | ===== Procese în Linux ===== | ||
- | |||
- | Lansarea în execuție a unui program presupune următorii pași: | ||
- | *Se creează un nou proces cu ''fork'' - procesul copil are o copie a resurselor procesului părinte. | ||
- | *Dacă se dorește înlocuirea imaginii procesului copil aceasta poate fi schimbată prin apelarea unei funcții din familia ''exec*''. | ||
- | ==== Crearea unui proces în Linux ==== | ||
- | |||
- | În UNIX un proces se creează folosind apelul de sistem [[https://docs.python.org/3/library/os.html#os.fork|fork]]: | ||
- | |||
- | <code python> | ||
- | os.fork() | ||
- | </code> | ||
- | |||
- | Efectul este crearea unui nou proces (procesul copil), copie a celui care a apelat ''fork'' (procesul părinte). Procesul copil primește un nou //process id// (''PID'') de la sistemul de operare. | ||
- | |||
- | |||
- | <note important> Această funcție este apelată o dată și se întoarce (în caz de succes) de două ori: | ||
- | *În părinte va întoarce pid-ul procesului nou creat (copil). | ||
- | *În procesul copil apelul va întoarce 0. | ||
- | </note> | ||
- | |||
- | {{ so:laboratoare:fork.png?300|}} | ||
- | |||
- | Pentru aflarea ''PID''-ului procesului curent și al procesului părinte se vor apela funcțiile de mai jos. | ||
- | |||
- | Funcția [[https://docs.python.org/3/library/os.html#os.getpid|getpid]] întoarce ''PID''-ul procesului apelant: | ||
- | |||
- | <code python> | ||
- | os.getpid() | ||
- | </code> | ||
- | |||
- | Funcția [[https://docs.python.org/3/library/os.html#os.getppid|getppid]] întoarce ''PID''-ul procesului părinte al procesului apelant: | ||
- | |||
- | <code python> | ||
- | os.getppid() | ||
- | </code> | ||
- | |||
- | ==== Înlocuirea imaginii unui proces în Linux ==== | ||
- | |||
- | |||
- | Familia de funcții [[https://docs.python.org/3/library/os.html#os.execv|exec]] va executa un nou program, înlocuind imaginea procesului curent, cu cea dintr-un fișier (executabil). Acest lucru înseamnă: | ||
- | * Spațiul de adrese al procesului va fi înlocuit cu unul nou, creat special pentru execuția fișierului. | ||
- | * Registrele ''PC'' (contorul program), ''SP'' (indicatorul stivă) și registrele generale vor fi reinițializate. | ||
- | * Măștile de semnale ignorate și blocate sunt setate la valorile implicite, ca și handler-ele semnalelor. | ||
- | * ''PID''-ul și descriptorii de fișier care nu au setat flag-ul ''CLOSE_ON_EXEC'' rămân neschimbați (implicit, flag-ul ''CLOSE_ON_EXEC'' nu este setat). | ||
- | |||
- | |||
- | <code python> | ||
- | |||
- | os.execl(path, arg1, arg2, ...); | ||
- | os.execv(path, args); | ||
- | os.execlp(file, arg1, arg2, ...); | ||
- | |||
- | </code> | ||
- | |||
- | Exemplu de folosire a funcțiilor de mai sus: | ||
- | |||
- | <code python> | ||
- | os.execl("/bin/ls", "ls", "-la") | ||
- | |||
- | args = ["ls", "-la"] | ||
- | os.execv("/bin/ls", args) | ||
- | |||
- | os.execlp("ls", "ls", "-la") | ||
- | </code> | ||
- | |||
- | ''execl'' și ''execv'' nu caută programul dat ca parametru în ''PATH'', astfel că acesta trebuie însoțit de calea completă. Versiunile ''execlp'' și ''execvp'' caută programul și în ''PATH''. | ||
- | |||
- | Toate funcțiile ''exec*'' sunt implementate prin apelul de sistem [[http://linux.die.net/man/2/execve|execve]]. | ||
- | ==== Așteptarea terminării unui proces în Linux ==== | ||
- | |||
- | Familia de funcții [[https://docs.python.org/3/library/os.html#os.waitpid|wait]] suspendă execuția procesului apelant până când procesul (procesele) specificat în argumente fie s-a terminat, fie a fost oprit (''SIGSTOP''). | ||
- | |||
- | <code python> | ||
- | os.waitpid(pid, options) | ||
- | </code> | ||
- | |||
- | |||
- | Starea procesului interogat se poate afla examinând ''status'' cu macrodefiniții precum [[https://docs.python.org/3/library/os.html#oswexitstatus|WEXITSTATUS]], care întoarce codul de eroare cu care s-a încheiat procesul așteptat, evaluând cei mai nesemnificativi 8 biți. | ||
- | |||
- | Există o variantă simplificată, care așteaptă orice proces copil să se termine. Următoarele secvențe de cod sunt echivalente: | ||
- | |||
- | <code python> | ||
- | (pid, status, info) = wait3(0) | status = waitpid(-1,0) | ||
- | </code> | ||
- | |||
- | ==== Terminarea unui proces în Linux ==== | ||
- | |||
- | |||
- | Pentru terminarea procesului curent, Linux pune la dispoziție apelul de sistem [[https://docs.python.org/3/library/sys.html#sys.exit|sys.exit]]. | ||
- | |||
- | <code python> | ||
- | import sys | ||
- | sys.exit(status) | ||
- | </code> | ||
- | |||
- | <spoiler Apeluri exit> | ||
- | Dintr-un program ''Python'' există doua moduri de invocare a acestui apel de sistem: | ||
- | |||
- | 1. apelul [[https://docs.python.org/3/library/sys.html#sys.exit|sys.exit]] (recomandat sa fie folosit in majoritatea cazurilor: | ||
- | |||
- | <code python> | ||
- | sys.exit(status) | ||
- | </code> | ||
- | |||
- | 2. apelul [[https://docs.python.org/3/library/os.html#os._exit|os._exit]] din biblioteca standard ''os'' (recomandat sa fie folosit in procese copil, dupa un apel os.fork(): | ||
- | |||
- | <code python> | ||
- | os._exit(status) | ||
- | </code> | ||
- | |||
- | In urma apelului os._exit, procesul nu va "curata" nimic in urma lui. | ||
- | |||
- | </spoiler> | ||
- | |||
- | Un proces al cărui părinte s-a terminat poartă numele de **proces orfan**. Acest proces este adoptat automat de către procesul ''init'', dar poartă denumirea de orfan în continuare deoarece procesul care l-a creat inițial nu mai există. | ||
- | |||
- | Un proces finalizat al cărui părinte nu a citit (încă) statusul terminării acestuia poartă numele de **proces zombie**. Procesul intră într-o stare de terminare, iar informația continuă să existe în tabela de procese astfel încât să ofere părintelui posibilitatea de a verifica codul cu care s-a finalizat procesul. În momentul în care părintele apelează funcția ''wait'', informația despre proces dispare. Orice proces copil o să treacă prin starea de proces zombie la terminare. | ||
- | |||
- | Pentru terminarea unui alt proces din sistem, se va trimite un semnal către procesul respectiv prin intermediul apelului de sistem [[https://docs.python.org/3/library/os.html#os.kill|kill]]. Mai multe detalii despre ''kill'' și semnale în[[so:laboratoare-2013:laborator-06| laboratorul de semnale]]. | ||
- | ==== Exemplu (my_system) ==== | ||
- | |||
- | <code python> | ||
- | import os | ||
- | |||
- | def my_system(command): | ||
- | try: | ||
- | pid = os.fork() | ||
- | if pid == -1: | ||
- | # error forking | ||
- | return os.EXIT_FAILURE | ||
- | elif pid == 0: | ||
- | # child process | ||
- | os.execvp(command, args) | ||
- | # only if exec failed */ | ||
- | os._exit(os.EXIT_FAILURE) | ||
- | else: | ||
- | # parent process | ||
- | pass | ||
- | |||
- | #only parent process gets here | ||
- | status = os.waitpid(pid0) | ||
- | if (os.WIFEXITED(status)): | ||
- | print("Child {} terminated normally, with code {}".format(pid, os.WEXITSTATUS(status))) | ||
- | return status | ||
- | except Exception as e: | ||
- | print ("Error: {}".format (e)) | ||
- | |||
- | my_system("ls") | ||
- | </code> | ||
- | |||
- | ==== Copierea descriptorilor de fișier ==== | ||
- | |||
- | [[http://linux.die.net/man/2/dup|dup]] duplică descriptorul de fișier ''oldfd'' și întoarce noul descriptor de fișier, sau ''-1'' în caz de eroare: | ||
- | |||
- | <code c> | ||
- | int dup(int oldfd); | ||
- | </code> | ||
- | |||
- | [[http://linux.die.net/man/2/dup|dup2]] duplică descriptorul de fișier ''oldfd'' în descriptorul de fișier ''newfd''; dacă ''newfd'' există, mai întâi va fi închis. Întoarce noul descriptor de fișier, sau ''-1'' în caz de eroare: | ||
- | |||
- | <code c> | ||
- | int dup2(int oldfd, int newfd); | ||
- | </code> | ||
- | |||
- | Descriptorii de fișier sunt, de fapt, indecși în tabela de fișiere deschise. Tabela este populată cu pointeri către structuri cu informațiile despre fișiere. Duplicarea unui descriptor de fișier înseamnă duplicarea intrării din tabela de fișiere deschise (adică 2 pointeri de la poziții diferite din tabelă vor indica spre aceeași structură din sistem, asociată fișierului). Din acest motiv, toate informațiile asociate unui fișier (lock-uri, cursor, flag-uri) sunt **partajate** de cei doi file descriptori. Aceasta înseamnă că operațiile ce modifică aceste informații pe unul dintre file descriptori (de ex. ''lseek'') sunt vizibile și pentru celălalt file descriptor (duplicat). | ||
- | |||
- | <note important> Flag-ul ''CLOSE_ON_EXEC'' nu este partajat (acest flag nu este ținut în structura menționată mai sus). </note> | ||
- | |||
- | === Moștenirea descriptorilor de fișier după operații fork/exec === | ||
- | |||
- | Descriptorii de fișier ai procesului părinte se **moștenesc** în procesul copil în urma apelului ''fork''. După un apel ''exec'', descriptorii de fișier sunt păstrați, excepție făcând cei care au flag-ul ''CLOSE_ON_EXEC'' setat. | ||
- | |||
- | <spoiler Detalii despre flagul CLOSE_ON_EXEC> | ||
- | **fcntl** | ||
- | |||
- | Pentru a seta flag-ul ''CLOSE_ON_EXEC'' se folosește funcția [[http://linux.die.net/man/3/fcntl|fcntl]], cu un apel de forma: | ||
- | |||
- | <code c> | ||
- | fcntl(file_descriptor, F_SETFD, FD_CLOEXEC); | ||
- | </code> | ||
- | |||
- | **O_CLOEXEC** | ||
- | |||
- | ''fcntl'' poate activa flag-ul ''FD_CLOEXEC'' doar pentru descriptori de fișier deja existenți. În aplicații cu mai multe fire de execuție, între crearea unui descriptor de fișier și un apel ''fcntl'' se poate interpune un apel ''exec'' pe un alt fir de execuție. | ||
- | |||
- | <code C> | ||
- | / * THREAD 1 */ |/ * THREAD 2 */ | ||
- | fd = op_creare_fd() | | ||
- | | exec(...) | ||
- | fcntl(fd, F_SETFD, FD_CLOEXEC); | | ||
- | </code> | ||
- | |||
- | Cum, implicit, descriptorii de fișiere sunt moșteniți după un apel ''exec'', deși programatorul a dorit ca acesta să nu poată fi accesat după ''exec'', nu poate preveni apariția unui apel ''exec'' între creare și ''fcntl''. | ||
- | |||
- | Pentru a rezolva această condiție de cursă, s-au introdus în Linux 2.6.27 (2008) versiuni noi ale unor apeluri de sistem: | ||
- | |||
- | <code C> | ||
- | int dup3(int oldfd, int newfd, int flags); | ||
- | int pipe2(int pipefd[2], int flags); | ||
- | int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); | ||
- | </code> | ||
- | |||
- | Aceste variante ale apelurilor de sistem adaugă câmpul ''flags'', prin care se poate specifica ''O_CLOEXEC'', pentru a crea și activa ''CLOSE_ON_EXEC'' în mod //atomic//. Numărul din numele apelului de sistem, specifică numărul de parametri ai apelului. | ||
- | |||
- | Apelurile de sistem care creează descriptori de fișiere care primeau deja un parametru ''flags'' (e.g. ''open'') au fost doar extinse să accepte și ''O_CLOEXEC''. | ||
- | |||
- | </spoiler> | ||
- | ===== Variabile de mediu în Linux ===== | ||
- | |||
- | În cadrul unui program se pot accesa variabilele de mediu, prin evidențierea celui de-al treilea parametru (opțional) al funcției ''main'', ca în exemplul următor: | ||
- | |||
- | <code c> | ||
- | int main(int argc, char **argv, char **environ) | ||
- | </code> | ||
- | |||
- | Acesta desemnează un vector de pointeri la șiruri de caractere, ce conțin variabilele de mediu și valorile lor. Șirurile de caractere sunt de forma ''VARIABILA=VALOARE''. Vectorul e terminat cu ''NULL''. | ||
- | |||
- | [[http://linux.die.net/man/3/getenv|getenv]] întoarce valoarea variabilei de mediu denumite ''name'', sau ''NULL'' dacă nu există o variabilă de mediu denumită astfel: | ||
- | |||
- | <code c> | ||
- | char* getenv(const char *name); | ||
- | </code> | ||
- | |||
- | [[http://linux.die.net/man/3/setenv|setenv]] adaugă în mediu variabila cu numele ''name'' (dacă nu există deja) și îi setează valoarea la ''value''. Dacă variabila există și ''replace'' e ''0'', acțiunea de setare a valorii variabilei e ignorată; dacă ''replace'' e diferit de ''0'', valoarea variabilei devine ''value'': | ||
- | |||
- | <code c> | ||
- | int setenv(const char *name, const char *value, int replace); | ||
- | </code> | ||
- | |||
- | [[http://linux.die.net/man/3/unsetenv|unsetenv]] șterge din mediu variabila denumită ''name'': | ||
- | |||
- | <code c> | ||
- | int unsetenv(const char *name); | ||
- | </code> | ||
- | |||
- | |||
- | ===== Depanarea unui proces ===== | ||
- | |||
- | Informații suplimentare legate de depanarea unui proces se găsesc [[so:laboratoare:resurse:gdb#depanarea unui proces | aici]] | ||
- | |||
- | |||
- | ===== Exerciții ===== | ||
- | |||
- | Pentru rezolvarea laboratorului, va rugam sa clonati [[https://www.github.com/upb-fils/sde|repository-ul]]. daca il aveti deja, va rugam sa rulati ''git pull''. | ||
- | |||
- | <note tip>Pentru a vă ajuta la implementarea exercițiilor din laborator, în directorul ''utils'' din arhivă există un fișier ''utils.h'' cu funcții utile.</note> | ||
- | |||
- | |||
- | ==== Exercițiul 1 - system (1p) ==== | ||
- | |||
- | Intrați în directorul ''1-system''. | ||
- | Programul ''my_system.c'' execută o comandă transmisă ca parametru, folosind funcția de bibliotecă [[http://linux.die.net/man/3/system|system]]. Modul de funcționare al [[http://linux.die.net/man/3/system|system]] este următorul: | ||
- | * se creează un nou proces cu [[http://linux.die.net/man/2/fork|fork]] | ||
- | * procesul copil execută, folosind [[http://linux.die.net/man/2/execve | execve]], programul **''sh''** cu argumentele -c “comanda”, timp în care procesul părinte așteaptă terminarea procesului copil. | ||
- | |||
- | Compilați (folosind ''make'') și rulați programul dând ca parametru o comandă. | ||
- | * Exemplu: <code bash> ./my_system pwd </code> | ||
- | |||
- | Cum procedați pentru a trimite mai mulți parametri unei comenzi? (ex: ''ls -la'') | ||
- | |||
- | Pentru a vedea câte apeluri de sistem [[http://linux.die.net/man/2/execve | execve]] se realizează, rulați: | ||
- | <code bash> strace -e execve,clone -ff -o output ./my_system ls </code> | ||
- | * **atenție!** nu este spațiu după virgulă în argumentul ''execve,clone'' | ||
- | * argumentul ''-ff'' însoțit de ''-o output'' generează câte un fișier de output pentru fiecare proces. | ||
- | * cițiți pagina de manual [[http://linux.die.net/man/1/strace|strace]] | ||
- | |||
- | Revedeți secțiunea [[#Înlocuirea imaginii unui proces în Linux |Înlocuirea imaginii unui proces în Linux]] și pagina de manual pentru [[http://linux.die.net/man/2/execve | execve ]]. | ||
- | |||
- | Inlocuiti functia ''system'' cu ''execlp''. Functia primeste ca parametru o comanda, lista de argumente (despartite de virgule) si apoi NULL (ultimul parametru, este de fapt marcatorul de sfarsit de lista). | ||
- | |||
- | Urmariti linia cu TODO 1. | ||
- | |||
- | ==== Exercițiul 2 - parameters (2p) ==== | ||
- | |||
- | Intrați în directorul ''2-parameters''. | ||
- | |||
- | Compilati programul //parameters.c// folosind comanda ''make''. Ce face programul //parameters.c//? | ||
- | |||
- | Rezolvati exercitiul in fisierul //program.c//. | ||
- | |||
- | === 2a. system (1p) === | ||
- | |||
- | Folositi functia ''system'' pentru a rula programul //parameters// cu cativa parametrii. | ||
- | |||
- | <note>Pentru a rula un program din directorul curent, trecuie sa prefixati executabilul cu ./ (ex: ./parameters)</note> | ||
- | |||
- | === 2b. execl (1p) === | ||
- | |||
- | Inlocuiti functia //system// cu //execl//. De ce nu se afiseaza textul prin //printf//-ul de dupa //execl//? | ||
- | |||
- | Urmariti liniile cu TODO 2. | ||
- | |||
- | ==== Exercițiul 3 - run (4p) ==== | ||
- | |||
- | Intrați în directorul ''3-run''. | ||
- | |||
- | === 3a - fork, exec (1p) === | ||
- | |||
- | Folsiti functiile //fork// si //execl// pentru a rula comanda ''ls'' din programul //run.c//. | ||
- | |||
- | Urmariti liniile cu TODO 1. | ||
- | |||
- | === 3b - waitpid (1p) === | ||
- | |||
- | Asigurati-va ca textul //"ls was run"// este afisat dupa inchiderea programului programului //ls//. (Hint: waitpid) | ||
- | |||
- | Urmariti liniile cu TODO 2. | ||
- | |||
- | === 3c - exit status (1p) === | ||
- | Mutati si modificati linia cu TODO 3 astfel incat sa se afiseaza codul de iesire (exit code) al programului //ls//. (Hint: WEXITSTATUS) | ||
- | |||
- | |||
- | === 3d - waitpid (1p) === | ||
- | Compilati programul //exitcode.c// folosind comanda ''make''. Rulati programul si afisati codul de iesire. Modificati programul //exitcode.c// astfel incat sa intoarca alt cod de iesire. | ||
- | |||
- | Urmariti liniile cu TODO 4. | ||
- | |||
- | ==== Exercițiul 4 - orphan (0.5p) ==== | ||
- | |||
- | Intrați în directorul ''4-orphan'' și inspectați sursa ''orphan.c''. | ||
- | |||
- | Compilați programul (''make'') și apoi rulați-l folosind comanda: | ||
- | <code bash> ./orphan </code> | ||
- | |||
- | Deschideți alt terminal și rulați comanda: | ||
- | <code bash> watch -d '(ps -al | grep -e orphan -e PID)' </code> | ||
- | |||
- | Observați că pentru procesul indicat de executabilul ''orphan'' (coloana ''CMD''), pid-ul procesului părinte (coloana ''PPID'') devine 1, întrucât procesul este adoptat de ''init'' după terminarea procesului său părinte. De ce sunt doua procese ''orphan''? | ||
- | |||
- | ==== Exercițiul 5 - zombie (0.5p) ==== | ||
- | |||
- | Intrați în directorul ''5-zombie'' și inspectați sursa ''zombie.c''. | ||
- | |||
- | Compilați programul (''make'') și apoi rulați-l folosind comanda: | ||
- | <code bash> ./zombie </code> | ||
- | |||
- | Deschideți alt terminal și rulați comanda: | ||
- | <code bash> watch -d '(ps -al | grep -e orphan -e PID)' </code> | ||
- | |||
- | Observați că pentru procesul indicat de executabilul ''zombie'' coloana ''CMD'' devine ''zombie'' <defunct>. Ce se intampla de fapt? | ||
- | |||
- | Modificati fisierul //zombie.c// astfel incat procesul sa nu mai devina un zombie (Hint: waitpid). | ||
- | |||
- | Urmariti liniile cu TODO 1. | ||
- | |||
- | ==== Exercițiul 6 - Tiny-Shell (3p) ==== | ||
- | |||
- | Intrați în directorul ''6-tiny''. | ||
- | |||
- | Următoarele subpuncte au ca scop implementarea unui shell minimal, care oferă suport pentru execuția unei //singure// comenzi externe cu argumente multiple și redirectări. Shell-ul trebuie să ofere suport pentru folosirea și setarea variabilelor de mediu. | ||
- | |||
- | //Observație:// Pentru a ieși din tiny shell folosiți ''exit'' sau ''CTRL+D''. | ||
- | |||
- | === 6a. Execuția unei comenzi simple (1p) === | ||
- | |||
- | Creați un nou proces care să execute o comandă simplă. | ||
- | |||
- | Funcția ''simple_cmd'' primește ca argument un vector de șiruri ce conține comanda și parametrii acesteia. | ||
- | |||
- | Citiți exemplul [[#exemplu(my_system) | my_system]] și urmăriți în cod comentariile cu ''TODO 1''. | ||
- | Pentru testare puteți folosi comenzile: <code bash> ./tiny | ||
- | > pwd | ||
- | > ls -al | ||
- | > exit </code> | ||
- | |||
- | === 6b. Adăugare suport pentru setarea și expandarea variabilelor de mediu (1p) === | ||
- | |||
- | Trebuie să completați funcțiile ''set_var'' și ''expand''; acestea sunt apelate deja atunci când se face parsarea liniei de comandă. Verificarea erorilor trebuie făcută în aceaste funcții. | ||
- | |||
- | * Urmăriți în cod comentariile cu ''TODO 2''. | ||
- | * Citiți secțiunea [[#variabile de mediu in linux | Variabile de mediu in Linux]]. | ||
- | * Pentru testare puteți folosi comenzile: <code bash> ./tiny | ||
- | > echo $HOME | ||
- | > name=Makefile | ||
- | > echo $name </code> | ||
- | |||
- | === 6c. Redirectarea ieșirii standard (1p) === | ||
- | |||
- | Completați funcția ''do_redirect'' astfel încât tiny-shell trebuie să suporte redirectarea output-ului unei comenzi (stdout) într-un fișier. | ||
- | |||
- | Dacă fișierul indicat de ''filename'' nu există, va fi creat. Dacă există, trebuie trunchiat. | ||
- | |||
- | Citiți secțiunea [[#Copierea descriptorilor de fișier|Copierea descriptorilor de fișier]] și urmăriți în cod comentariile cu ''TODO 3''. | ||
- | Pentru testare puteți folosi comenzile: <code bash> ./tiny | ||
- | > ls -al > out | ||
- | > cat out | ||
- | > pwd > out | ||
- | > cat out | ||
- | </code> | ||
- | |||
- | ===== Soluții ===== | ||
- | |||
- | [[https://github.com/UPB-FILS/sde/tree/master/tp04/solutions | Solutii]] | ||
- | |||
- | |||
- | |||
- | |||
- | ===== Resurse utile ===== | ||
- | |||
- | - [[http://en.wikipedia.org/wiki/Fork_(operating_system)|Fork - Wikipedia]] | ||
- | - [[http://www-h.eng.cam.ac.uk/help/tpl/unix/fork.html|About Fork and Exec]] | ||
- | - [[http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html|Fork, Exec and Process Control - YoLinux Tutorial]] | ||