Urmăriți precizările din pagina de reguli.
Puteți participa la un singur examen final.
Datele de examen de SO pentru sesiunea iunie 2018 sunt:
Datele de examen de SO pentru sesiunea septembrie 2018 sunt:
Puteți veni o singură dată la examen. Studenții care refac materia pot veni doar în primele două date de examen (31 mai 2018 și 7 iunie 2018). Pentru ei sesiunea este mai scurtă (26 mai 2018 - 8 iunie 2018).
Studenții de anul 3 pot veni în alte zile față de cea aferentă grupei/seriei lor. Studenții în alte situații (de exemplu transferați de la IS) pot veni oricând. Cei care doresc să vină în altă zi față de cele alocate, să trimită un e-mail către Elena cu subiectul [SO][Examen] Transfer: Prenume NUME, grupă
, de exemplu [SO][Examen] Transfer: Ana POPESCU, 332CB
.
Informații despre desfășurătorul și conținutul examenului găsiți în secțiunea aferentă din pagina de notare. În pregătirea examenului recomandăm să parcurgeți subiectele de examen anterioare.
Urmăriți precizările din pagina de reguli.
[SO][Lucrare X] Transfer Prenume Nume, Grupa
unde:X
este indexul lucrării (1, 2, 3 sau 4)Prenume
este prenumele.Nume
este numa.Grupa
este grupa.[SO][Lucrare X] Prenume NUME - grupa
; de exemplu [SO][Lucrare 1] Andreea POPESCU - 332CA
.open()
urmat de un apel dup()
descriptorii vor indica spre aceeași structură.int i = fork(); if (i==0) i = getppid(); else sleep(1); printf(“%d\n”, i);
pid_t pid = fork(); switch(pid) { case 0: fd = open(“/dev/null”, O_WRONLY | O_TRUNC | O_CREAT, 0644); dup2(fd, STDOUT_FINENO); close(fd); execlp(“ls”, “ls”, NULL); default: waitpid(pid, NULL, 0); }
De asemenea, au fost punctate și variantele care conțin variante în pseudocod ale apelurilor precum: open(”/dev/null”)
, exec(“ls”)
etc.
int i = fork(); if (i==0) i++; else sleep(1); printf(“%d\n”, i);
ls | wc -l
.fd_redirect; fd = fork(); if (fd == 0) { //child close(STDOUT); dup2(STDOUT, fd_redirect); exec("ls"); } else if (fd > 0) { //parent close(STDIN); dup2(STDIN, fd_redirect); wait(); //for child exec("wc -l"); } else { //error }
ls >> test.out
(este de interes implementarea redirectării, în locul comenzii ls putea fi orice alta comandă)?int fd = open("test.out"); close (STDOUT_FILENO); dup(fd); close(fd); lseek(STDOUT_FILENO,0, SEEK_END);
ls | grep test
)?fork()
pentru crearea celor două procese, pipe()
pentru deschiderea unor pipe-urilor folosite de cele două procese, close()
pentru închiderea unuia din capetele pipe-urilor create înainte, dup2()
pentru redirectarea capetelor pipe-ului și execve()
pentru execuție.open()/fopen()
care duce la ocuparea unei intrări din tabela de descriptori de fișier a unui proces.dup()
(sau dup2()
) pentru duplicarea unei intrări existente sau cu apeluri precum socket()
sau pipe()
pentru crearea unui socket sau a unui pipe care ocupă, respectiv, o intrare și două intrări.getppid()
în două situații diferite. În prima situație apelul întoarce valoarea 2832, iar în a doua situație apelul întoarce valoarea 1. Cum explicați?getppid()
întoarce pid-ul procesului părinte. În prima situație valoarea returnată este diferită de 1 deoarece procesul are un părinte (creator) care încă rulează. În cea de-a doua situație procesul a fost adoptat de init deoarece părintele creator al procesului și-a terminat execuția și nu l-a așteptat.printf()
în locul apelului write()
pentru afișarea unui mesaj la ieșirea standard a unui proces?printf()
sunt: formatare, portabilitate, mai puține apeluri de sistem (când mesajul este buffered). Dezavantaje: buffer-ul ocupă memorie, nu se afișează instant mesajul (este buffered).for (i=0; i<100000; i++) if (fork() == 0) exit(1);
Care este consumul total de memorie fizică folosită de toate procesele copil în plus față de procesul părinte, exceptând structurile de date din kernel?
fork()
, deși pagina respectivă este scrisă atât de copil cât și de părinte.mmap (…, MAP_SHARED,…)
;memset(a, 0, 8); /* fill a with 8 bytes of 0 */
_sync_bool_compare_and_swap (int* t, int r, int n)
.do { int s = t+1; } while (!_sync_bool_compare_and_swap(&t,s-1,s));
fork()
pentru fiecare client și are un stack overflow exploatabil de client prin modificarea inputului. De câte încercări are nevoie atacatorul, în medie, pentru a ghici valoarea canary?_sync_bool_compare_and_swap (int* t, int r, int n)
.while (!_sync_bool_compare_and_swap(&lock,0,1));
.data
și .bss
sunt separat puse în fișierul executabil?.bss
refera variabila globale care au valoarea 0. Gruparea acestora intr-o sectiune speciala, diferita de .data
, optimizeaza consumul de spatiu intrucat nu mai este necesar sa stocam 0-uri.a += 5
este atomică? (la executarea secvenței de 2 thread-uri, rezultatul final va fi același tot timpul) Explicați..text
?add [%rax], 5
cand avem 2 core-uri în sistem?a++
din C poate fi atomică pe un sistem x86 single-core dar niciodată pe un sistem x86 multi-core?mprotect()
. De ce și cum folosește atacatorul apelul mprotect()
?lock()
pentru un spinlock?spinlock = 1; /* initial value is 1 - open */ lockfn: lock cmpxchg spinlock, 1, 0 cmp old_value, 0 ; if old_value (stored in a register by cmpxchg) is 0, busy wait je lockfn
recv
efectuat la celălalt capăt al conexiunii la momentul t1 > t0 să întoarcă valoarea -1?recv(fd, buf, 10000, 0)
va nevoie pentru a primi toate datele?/usr/bin/time -v cp in.dat out.dat
. Aceasta durează 1 secundă. Mai executăm o dată comanda /usr/bin/time -v cp in.dat out.dat
. A doua oară aceasta va dura doar 0.1 secunde. Care ar putea fi cauza?int n = recv(s,buf,1000,0);
. Dați exemplu de un caz în care n va fi 1 și de un caz în care n va fi 1000.int n = send(s,buf,2000,0);
. Dați exemplu de un caz în care n va fi 1 și de un caz în care n va fi 2000.send()
pe un socket?recv()
pe un socket?O_NONBLOCK
si ASYNC IO
.