Recapitulare

Laboratorul constă în subiecte în stilul celor de la examen, însoțite de mici bucăți de cod complete sau aproape complete pe care le puteți rula pentru a vă convinge. Discuția pe marginea subiectelor cu asistentul sau cu colegii este încurajată. Subiectele sunt suficient de multe încât nu pot fi acoperite într-un singur laborator.

Exerciții

  1. Demand-paging, Copy-on-Write. Intrați în directorul 01-cow.
    • Rulați comanda make și rulați executabilul creat: cow.
    • Explicați când au loc evenimentele de Demand-paging și de Copy-on-Write.
    • De ce scrierea efectuată de procesul copil în paginile alocate de părinte dureaza mult mai mult decât citirile?
  2. Subsistemul IO. Intraţi în directorul 02-io şi inspectaţi fişierul splice.c.
    • Închipuiţi-vă scenariul în care programul rulează repede şi un alt scenariu în care rulează greu (din punctul de vedere al timpului).
    • Hints:
      • Ce operaţii se fac la trunchierea fişierului?
      • Care este rolul fişierului /proc/sys/vm/drop_caches?
      • Ce fel de mecanism implementează apelul de sistem splice?
      • Atenţie: Asigurați-vă că sistemul dispune de suficientă memorie (RAM) liberă pentru a putea aduce tot fișierul în RAM (i.e. dimensiunea memorie liberă > dimensiunea fișierului).
      • Utilizaţi scriptul run.sh:
        sudo ./run.sh
      • Ignoraţi mesajul:
        Command terminated by signal 13
  3. Threads counter (LD): Un proces crează N thread-uri și un thread apelează fork(); câte threaduri, atât în procesul părinte cât și în procesul copil, există imediat după acest moment? Studiați conținutul directorului 03-threads-counter. În ce director din /proc/$PID/ se află informațiile despre thread-urile procesului? De ce acestea au asignat un pid?
  4. Function hijacking (LD): De ce în cazul folosirii LD_PRELOAD se pot intercepta apeluri de funcții? Intrați în directorul 04-functions-hijacking, compilați (make) și rulați (./test și LD_PRELOAD=./libnative.so ./test. Cum explicați output-ul diferit?
  5. Inițializări (VD): Intrați în directorul 05-init. Compilați programul initializari. De ce durează mai mult prima inițializare? De asemenea, încercați să explicați dimensiunea mare a executabilului.
  6. Spațiu de adresă (AF): Intrați în directorul 06-addrspc și inspectați fișierul addrspc.c. Ce se întâmplă cu spațiul de adresă al procesului în momentul schimbării de context?
    • Hints:
      • Threadurile rulează alternativ, scriind la stdout atunci când încep rularea
      • Monitorizați /proc/$PID/maps
  7. Reentranță / thread-safety (AF): Catalogați funcțiile din 07-reentr/reentr.c ca fiind reentrante / thread-safe. Puteți modifica programul încât să puneți în evidență aceste aspecte.
  8. Stack growth (VD): Scrieți o secvență de cod care să decidă dacă stiva crește în sus sau în jos. Un asemenea exemplu îl aveți în directorul 08-stack-growth.
  9. Apeluri de sistem (DB): Câte apeluri de sistem se efectuează în urma rulării secvenţei de mai jos:
       for (i = 0; i < 10; i++) {
                    getpid();
                    write(1, "A", 1);
       }
    • Intrați în directorul 09-syscall.
    • Analizaţi conţinutul fişierului pid_test.c.
    • Urmăriţi apelurile de system efectuate rulând comanda strace.
      strace ./pid_test 
    • Suplimentar
      • Analizaţi conţinutul fişierului fork_test.c.
      • Rulaţi programul având pentru SYS_TYPE pe rând valorile SYS_NATIVE şi SYS_GLIBC. *Explicaţi rezultatul obţinut. Citiţi secţiunea NOTES din getpid(2).
  10. Creare procese (DB): Desenaţi arborele de procese rezultat în urma execuţiei programului fork.c din directorul 10-fork.
  11. Descriptori de fisier (OB): Un proces dispune de o tabelă de descriptori de fișiere cu 1024 de intrări. În codul său, procesul deschide un număr mare de fișiere folosind open. Totuși, al 964-lea apel open se întoarce cu eroare, iar errno are valoarea EMFILE (maximum number of files open). Care este o posibilă explicație? Vă puteți gândi și la alte explicații decât cea din exemplu?
  12. File descriptor (BD): Completați zona punctată de mai jos cu cod Linux(posix) astfel incat sa conduca la afișarea mesajului “alfa” la ieșirea standard (standard output) și mesajul “beta” la ieșirea de eroare standard (standard error), fara sa alterați simbolurile standard fputs (functie), stderr și stdout (FILE *):
    • /* de completat */
      ...
      fputs("alfa", stderr);
      fputs("beta", stdout);
    • Intrati in directorul 12-file, si rulati comanda
      ./file >out 2>err 
  13. Calloc/Malloc (IS): Intrați in directorul 13-access. Rulați cele 2 programe atât cu macro-ul TEST_ACCESS definit cât și fără acesta. Măsurați timpii de rulare folosind time și încercați să explicați diferența.
  14. Apeluri de sistem (RD): În urma rulării secvenței de mai jos:
    	int page_size = getpagesize();
    
    	for (i = 0; i < 1000; i++)
    		ptr[i] = malloc(page_size);

    se obține un număr redus de apeluri de sistem, de ordinul zecilor (apelul de sistem folosit de malloc este brk). Cum explicați?

    • Intrați în directorul 14-malloc-syscalls.
    • Urmăriți conținutul fișierului malloc-syscalls.c.
    • Compilați fișierul (folosiți make).
    • Rulați comanda strace -e brk ./malloc-syscall 2>&1 > /dev/null | wc -l.
    • Rulați comanda ltrace -e malloc ./malloc-syscall 2>&1 > /dev/null | wc -l.
    • Suplimentar
      • Actualizați macro-ul DO_FREE la valoarea 1.
      • Câte apeluri brk au loc? Cum explicați? Ce rol are apelul de sistem brk?
  15. Page fault-uri (RD): Câte page fault-uri se obțin în urma rulării secvenței de mai jos?
    	char *p;
    	int status;
    	size_t i;
    	int page_size = getpagesize();
    	char value;
    
    	p = mmap(NULL, NUM_PAGES * page_size, PROT_READ | PROT_WRITE,
    			MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    	if (p == MAP_FAILED) {
    		perror("mmap");
    		exit(EXIT_FAILURE);
    	}
    
    	for (i = 0; i < NUM_PAGES; i++)
    		p[i*page_size] = i;
    
    	switch (fork()) {
    		case -1:    /* handle error */
    			perror("fork");
    			exit(EXIT_FAILURE);
    
    		case 0:     /* child process */
    
    			for (i = 0; i < NUM_PAGES / 2; i++)
    				value = p[i*page_size];
    
    			for (i = NUM_PAGES; i < NUM_PAGES; i++)
    				p[i*page_size] = page_size-i;
    
    			exit(EXIT_SUCCESS);
    			break;
    
    		default:
    			break;
    	}
    
    	wait(&status);
    	for (i = 0; i < NUM_PAGES; i++)
    		p[i*page_size] = i;
    • Intrați în directorul 15-faults.
    • Urmăriți conținutul fișierului fork-faults.c.
    • Compilați fișierul (folosiți make).
    • Instalați pachetul sysstat. Pachetul conține utilitarul pidstat care permite monitorizarea page fault-urilor unui proces (prin intermediul argumentului -r).
    • Rulați programul fork-faults. Folosiți ENTER pentru a continua programul.
    • Folosiți comanda pidstat -r -T ALL -p $PID pentru a urmări page fault-urile. Rulați comanda pentru fiecare secvență de program.
      • $PID reprezintă pid-ul unui proces. Puteți afla atât pid-ul procesului părinte cât și pe cel al procesului copil cu o comandă de forma ps -ef | grep fork-faults.
    • Urmăriți evoluția numărului de page fault-uri pentru cele două procese: părinte și copil. Page fault-urile care apar în cazul unui copy-on-write în procesul copil vor fi vizibile ulterior și în procesul părinte.
so/laboratoare/resurse/recapitulare.txt · Last modified: 2022/03/14 18:02 by teodor_stefan.dutu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0