Examen CA/CC 2015-2016
Examen final
Puteți participa la un singur examen final.
Datele de examen de SO pentru sesiunea iunie 2016 sunt:
sâmbătă, 4 iunie 2016, ora 08:00, sala EC101, refacere materie anul 4 seria CB
marți, 7 iunie 2016, ora 11:00, sala EC101, 331CB, 333CB
miercuri, 8 iunie 2016, ora 08:00, sala EC002, 332CB
miercuri, 8 iunie 2016, ora 14:00, sala EC004, refacere materie anul 4 seriile CA+CC
vineri, 10 iunie 2016, ora 09:00, sala EC002, refacere materie anul 4 seriile CA+CC
joi, 16 iunie 2016, ora 08:00, sala A02, 3CC
vineri, 17 iunie 2016, ora 08:00, sala PR001, 3CA
vineri, 17 iunie 2016, ora 08:00, sala EC004, 334CB, 335CB
Datele de examen de SO pentru sesiunea septembrie 2016 sunt:
marți, 30 august 2016
luni, 5 septembrie 2016
Detalii despre examen găsiți în secțiunea aferentă din pagina de reguli.
Foi de examen
Lucrări
Lucrare 1
La începutul cursului 4:
marți, 15 martie 2016, 08:00-08:15, EC004, seria CA
miercuri, 16 martie 2016, 17:00-17:15, EC004, seria CC
3CA, varianta 1
De ce este importantă separația user mode - kernel mode?
Răspuns: Separația kernel mode - user mode este importantă pentru că asigură un mod privilegiat de execuție (kernel mode) pentru operații critice. Un mod privilegiat în care rulează sistemul de operare înseamnă că operațiile critice (IPC, lucrul cu I/O, lucrul cu memoria) vor fi validate de sistemul de operare și un proces (aplicație user space) nu poate face pagube sistemului. Pentru operații privilegiate va fi necesară trecerea în kernel mode prin intermediul unui apel de sistem, și astfel invocarea sistemului de operare care acționează ca un gardian al operațiilor, garantând securitatea și integritatea sistemului.
fd1
este un descriptor de fișier, iar fd2 = dup(fd1)
. Precizați ce se întamplă cu cursorul de fișier al fd1
atunci când scriem în fd2
.
Răspuns: Apelul dup()
duplică un descriptor de fișier în alt descriptor de fișier, astfel încât ambii descriptori referă aceeași structură de fișier deschis. Referind aceeași structură de fișier deschis, descriptorii partajează cursorul de fișier (aflat în structura de fișier deschis). Atunci când scriem folosind descriptorul de fișier fd2
, modificăm (creștem) cursorul de fișier lucru vizibil și în cadrul descriptorului fd1
. Este, practic, vorba de același cursor de fișier.
În Bash, executăm comanda sleep 10
. Precizați pașii pe care îi va realiza procesul Bash pentru a rula această comandă.
Răspuns: Procesul Bash folosește fork()
pentru a crea un proces copil identic sieși (clonă a sa) și apoi exec("sleep")
în procesul copil pentru rularea codului din executabilul sleep
. În procesul părinte se apelează wait()
pentru a aștepta încheierea execuției procesului copil (sleep
), procesul Bash apărând blocat utilizatorului până la încheierea comenzii (nu se pot introduce comenzi noi în Bash).
3CA, varianta 2
Apelurile printf(...)
și write(1,...)
pot fi folosite pentru a afișa text la consolă. Care este diferența principală dintre ele?
Răspuns: Diferența principală este că cele două apeluri este că printf()
este un apel de tipul buffered, adică scrie într-un buffer de memorie în user space. Doar când buffer-ul este plin sau când se apelează fflush()
sau când se întâlnește newline (\n
) se face apel de sistem. write()
realizează mereu apel de sistem. printf()
oferă potențial avantaj de overhead (nefăcând apel de sistem de fiecare dată) cu nevoia alocării unui buffer de memorie în user space.
Un proces scrie în fiecare secundă PID-ul și timpul curent în fișierul f.txt
cu ajutorul descriptorului fd
. Procesul execută fork()
. După fork()
ambele procese scriu PID-ul și timpul curent în fd
. Care dintre procese va reuși să scrie în f.txt
?
În Bash, executăm comanda sleep 10 &
. Precizați pașii pe care îi va realiza procesul Bash pentru a rula această comandă.
Răspuns: Procesul Bash folosește fork()
pentru a crea un proces copil identic sieși (clonă a sa) și apoi exec("sleep")
în procesul copil pentru rularea codului din executabilul sleep
. În procesul părinte nu se apelează wait()
, procesul Bash nu așteaptă încheierea procesului sleep astfel încât utilizatorul poate introduce comenzi noi în Bash.
3CC, varianta 1
Indicați un avantaj și un dezavantaj al unui kernel monolitic față de un microkernel.
Răspuns: Avantaj pentru un nucleu monolitic sunt viteza de execuție (overhead-ul redus). Dezavantaje pentru un nucleu monolitic sunt securitatea potențal mai slabă (întrucât kernel-ul este mai mare și are suprafață de atac mai mare, spunem că are TCB (Trusted Computing Base) mai mare); este mai puțin modular decât un microkernel însemnând o toleranță la defecte mai redusă și o proiectare mai greu extensibilă.
Indicați două funcții de lucru cu fișiere care alterează cursorul de fișier.
Câte procese se pot găsi la un moment dat în fiecare dintre cele trei stări ale unui proces: READY
, RUNNING
și WAITING
?
Răspuns: În starea RUNNING
se pot găsi între 0
și N
procese unde N
este numărul de procesoare. Un proces are nevoie de un procesor pentru a putea rula. În stările READY
și WAITING
se pot găsi oricâte procese, limitarea fiind dată de resursele sistemului (memorie). Nu există constrângeri puternice pentru stările READY
și WAITING
.
3CC, varianta 2
Care este un avantaj și un dezavantaj al folosirii apelurilor de sistem?
Indicați două informații din metadatele unui fișier (excluzând numele fișierului).
Răspuns: Metadatele fișierului conțin informații despre utilizarea fișierului. Metadate sunt: dimensiunea fișierului, timpii de acces, user, group, permisiuni de acces, indecși către blocurile de date, tipul fișierului.
Ce se întâmplă cu un proces zombie rămas orfan?
Răspuns: Un proces rămas orfan este adoptat de init
. Și un proces zombie rămas orfan este adoptat de init
; doar că, spre deosebire de un proces non-zombie, procesul zombie este apoi "terminat" de init
(zombie reaping) și informațiile rezidente legate de modul în care și-a încheiat execuția sunt șterse.
Greșeli frecvente
-
&
înseamnă rulare în background, dar când vorbim de internele lucrului cu procese semnificația sa este că procesul părinte nu așteaptă (nu apelează wait()
sau WaitForSingleProcess()
) după procesul copil. Rularea în background este o denumire de comportament în shell; internele shell-ului (pașii urmați de shell) se referă la faptul că nu se apelează wait()
de procesul părinte.
Rularea în background-ul shell-ului nu înseamnă proces daemon. Procesul daemon este detașat de terminal; un proces care rulează în background poate fi adus în foreground-ul shell-ului. Spunem că un proces daemon rulează în background-ul sistemului, complet detașat de un terminal sau de un shell. Background-ul shell-ului diferă de noțiunea de background al sistemului pentru procese deamon (decuplate de terminal).
Afirmația printf()
scrie mereu la stdin, pe când write()
poate scrie la un descriptor de fișier ce poate fi redirectat. este falsă. Redirectarea se face la nivelul descriptorului de fișier, nu ține de apelul folosit. Și printf()
folosește un descriptor de fișier (descriptorul 1
) care poate redirectat, caz în care ce scriem folosind printf()
va ajunge în fișierul în care am realizat redirectarea.
Afirmația fd1
pointează către fișier nu este complet corectă. Corect este pointează către o structură de fișier deschis; este vorba de o structură în memorie creată după deschiderea fișierului. Fișierul stă pe disc indiferent dacă este deschis sau nu de un proces, structura de fișier deschis stă în memorie și este creată în momentul deschiderii fișierului de pe disc de un proces.
Când vorbim despre pașii pe care îi realizează procesul Bash (sau orice alt proces) ne referim la internele operațiilor (
API-ului) realizate de Bash. Stările prin care trece procesul Bash (sau orice alt proces) (
RUNNING
,
READY
,
WAITING
) țin de modul în care acest proces este planificat de planificatorul/scheduler-ul sistemului de operare. Nu sunt pași pe care procesul îi realizează.
Există o confuzie între modurile de operare și utilizatori; de la exprimări de genul pentru că un utilizator să nu strice kernel-ul, până la afirmația utilizatorul root operează în kernel mode. Cel mai bine este să privim user mode/user space ca fiind application mode; nu are legătură cu utilizatorul/permisiunile utilizatorului ci cu procesele/aplicațiile; se referă la nivelul de privilegiu al proceselor/aplicațiilor. Procesele/Aplicațiile pornite de utilizatorul root
rulează, de asemenea, în user space. Diferența este că la trecerea în kernel mode (prin apel de sistem) acestea au privilegii superiorare; un apel kill()
de trimitere a unui semnal către un proces va fi tratat de kernel în mod diferit dacă procesul/aplicația care l-a inițiat a fost rulată de root sau de un utilizator obișnuit.
Afirmația În starea WAITING
există un singur proces. este nevalidă. Starea WAITING
este starea în care sunt procese blocate în așteptarea unui eveniment care să le deblocheze. Pot fi oricât de multe procese în starea WAITING
în limita resurselor sistemului.
Ca avantaj pentru apeluri de sistem a fost precizat Apelurile de sistem sunt mai rapide. Apelurile de sistem au overhead, nu sunt neapărat rapide. Prin comparație cu apeluri de bibliotecă, apelurile de sistem sunt mai lente.
Afirmația Cursorul de fișier e metadată a fișierului. este falsă. Fișierul rezidă pe disc, structura de fișier deschis rezidă în memorie și este creată când este deschis un fișier de proces; structura de fișier deschis conține cursorul de fișier. Metadatele fișierului se referă la fișierul de pe disc, iar aceste metadate nu conțin cursorul de fișier.
Afimația Cursorul de fișier e afectat de apelul close()
. nu este validă. Apelul close()
invalidează un cursor de fișier și eventual dezalocă structura de fișier deschis ce conține cursorul de fișier; dar nu schimbă valoarea cursorului de fișier.
Afirmația Un proces zombie nu poate fi orfan. este falsă. Un proces devine zombie când își încheie execuția dar nu este încă așteptat de procesul său părinte. Un proces zombie devine orfan când procesul său părinte își încheie execuția, fără să îl aștepte.
Lucrări foarte bune
DUMITRU Mihai-Valentin, 335CB
MURARU George-Cristian, 333CB
IONESCU Bianca-Raluca, 331CC
MIHAI Darius, 331CC
BĂLAN Alexandru, 332CC
DIMOS Alexandros, 332CC
POȘTOACĂ Andrei Vlad, 332CC
OPREA George Bogdan, 333CC
MARINUȘ Alexandru, 334CC
RACU Roxana, 335CC
ROTARU Alexandru Andrei, 335CC
ONOSE Cristian, 342C3
PAVEL Nicolae Teodor, 333CA
TUFĂ Adriana 333CA
COSTEA Dragos Florin 334CA
Lucrare 2
La începutul cursului 7:
marți, 5 aprilie 2016, 08:00-08:15, EC004, seria CA
miercuri, 6 aprilie 2016, 17:00-17:15, EC004, seria CC
3CA, varianta 1
Precizați un avantaj și un dezavantaj al multitasking-ului preemptiv pe sisteme cu un singur procesor.
Răspuns: Multitasking-ul preemptiv pe un singur procesor mărește interactivitatea sistemului, planificând rând pe rând procesele pe procesor, dând șansa fiecăruia să ruleze. Este avantajos pentru că limitează process starvation. Are dezavantajul lipsei de throughput întrucât un proces va fi întrerupt când îi expiră cuanta de timp alocată. De asemenea, e dezavantajos pentru că nu mai avem determinism la nivelul rulării și nu putem estima cu precizie când un proces care știm cât ar trebui să ruleze își va încheia execuția.
Explicați cum folosește sistemul de operare Translation Lookaside Buffer (TLB).
Răspuns: În momentul în care este accesată o adresă virtuală, trebuie realizată translatarea acesteia în adresă fizică, informație care se găsește în tabela de pagini, dar cached în TLB. Se caută prima oară in TLB dacă există intrarea pentru pagina virtuală corespunzătoare adresei și se extrage de acolo pagina fizică. Dacă există (TLB hit), se extrage adresa paginii fizice și se calculează adresa fizică și se accesează. În caz contrar (TLB miss) se parcurge tabela de pagini a procesului și, în caz de intrare validă, se populează o intrare în TLB cu valoarea în cauză.
Procesul A folosește 3MB
de memorie fizică. A execută fork()
și creează procesul B. Înainte ca A sau B să își continue execuția, care va fi consumul total de memorie fizică al celor două procese?
Răspuns: Întrucât fork()
folosește copy-on-write, imediat după fork()
procesul părinte și procesul copil, deși cu spațiii virtuale diferite, vor partaja spațiul fizic de memorie al procesului părinte. Consumul total de memorie va rămâne de 3MB
.
3CA, varianta 2
Precizați două dezavantaje ale algoritmului de planificare Shortest Job First (SJF).
Răspuns: Un dezavantaj este faptul că poate duce la process starvation. Dacă în sistem apar în continuu procese noi și care durează puțin, procesele care durează mult nu vor fi planificate. Acest dezavantaj poate fi exprimat și prin timp mare de așteptare pentru procesele cu durată mare. Sau poate fi exprimat și prin interactivitate redusă. Un alt dezavantaj este că nu putem estima cu precizie durata unui proces ca să putem face o planificare de tip Shortest Job First. Nu există dezavantaje legate de priorități sau de cuante de timp pentru că este un sistem de planificare nepreemptiv.
Detaliați pașii pe care îi execută Memory Management Unit (MMU) pentru a rezolva o adresă virtuală într-o adresă fizică.
Răspuns: MMU primește o adresă virtuală și are un pointer (de exemplu registrul CR3
pe x86) la tabela de pagini a procesului care a făcut acces la acea adresă. Calculează adresa pagini virtuale aferente adresei și, în primă fază, caută existența intrării în TLB. Dacă există, extrage adresa paginii fizice aferente. Dacă nu există, accesează tabela de pagini a procesului în memoria RAM și caută acolo corespondența. Dacă există în TLB sau în tabela de pagini calculează adresa fizică corespunzătoare adresei virtuale inițiale și accesează memoria RAM. Dacă nu există în TLB și nici în tabela de pagini generează un page fault.
Explicați de ce nu este nevoie de TLB flush în cadrul unui apel de sistem.
Răspuns: În sistemele de operare moderne, spațiul virtual de adrese al tuturor proceselor are rezervată zonă pentru nucleul sistemului de operare. În acest fel, în momentul unui apel de sistem și al schimbării din user mode în kernel mode, spațiul virtual de adrese nu se schmbă iar intrările din TLB sunt valide. La apelarea și la revenirea din apel de sistem nu are loc schimbare de context și nu este nevoie de TLB flush.
3CC, varianta 1
De ce este problematică folosirea unui planificator care folosește doar priorități statice?
Răspuns: Dacă un planificator folosește doar priorități statice atunci procesele care au priorități superioare vor fi preferate în fața celor cu priorități inferioare. Acest lucru rezultă într-un sistem mai puțin interactiv, cu timp mediu de așteptare mare și riscul de process starvation pentru procesele cu priorități inferioare. Singurul moment în care procesele cu priorități inferioare vor putea rula este atunci când procesele cu priorități superioare sunt toate blocate sau când nu mai există astfel de procese în sistem.
Cum se realizează partajarea memoriei între două procese pe un sistem care folosește paginare și memorie virtuală?
Răspuns: Pe un sistem cu paginare și memorie virtuală, fiecare proces dispune de o tabelă de pagini care face asocierea între pagini virtuale și pagini fizice. Două procese diferite pot avea astfel corespondențe de corespondențe la aceeași pagină fizică; paginile virtuale sunt diferite dar vor corespunde aceleiași pagini fizice.
De ce dereferențierea unei adrese virtuale peste 3GB
cauzează terminarea/omorârea procesului pe un sistem Linux pe 32 de biți?
Răspuns: Un sistem Linux pe 32 de biți rezervă pentru spațiul virtual de adrese al fiecărui proces zona [3GB,4GB]
pentru sistemul de operare. Această zonă este protejată și accesibilă doar din kernel mode. Dacă un proces accesează o astfel de zonă (prin dereferențierea unei adrese virtuale mai mari de 3GB
), va primi un page fault și va fi omorât, nefiind validă accesarea acestei adrese din user mode.
3CC, varianta 2
De ce preferăm să oferim o cuantă de timp de planificare (scheduling time slice) mai mare proceselor I/O intensive (I/O bound)?
Răspuns: Preferăm o cuantă de timp mai mare proceselor I/O intensive pentru că acestea au probabilitate mare de a se bloca și de a elibera astfel procesorul, lăsând locul altor procese. Un proces I/O intensiv va părăsi astfel mai rar procesorul din cauză exprirării cuantei, mărind productivitatea (throughput-ul) sistemului (o schimbare de context suplimentară cauzează overhead).
Un sistem x86 poate folosi pagini de 4KB
sau de 4MB
(numite și huge pages). Care este un avantaj al folosirii paginilor de 4KB
și un avantaj al folosirii paginilor de 4MB
?
Răspuns: Avantajul folosirii unei pagini mai mici, de 4KB
, este că vom avea fragmentară internă redusă (la nivelul unei pagini). Alocarea a 10
octeți de date într-o pagină nouă lasă nefolosiți 4KB-10 octeți
, relativ puțin față de același lucru întâmplându-se pentru o pagină de 4MB
. Un alt avantaj este acela al granularității alocării, în cazul în care acest lucru ne interesează (corelează cu reducerea fragmentării interne). Avantajul folosirii unei pagini mai mare, de 4MB
, este dimensiunea redusă a tabelei de pagini. Spațiul ocupat de tabela de pagini va fi de 1024
de ori mai mic decât în cazul folosirii de pagini de 4KB
. Viteza de căutare în tabela de pagini va fi, de asemenea, redusă.
Dați exemplu de două situații în care apariția unui page fault nu cauzează terminarea/omorârea procesului care a generat page fault-ul.
Greșeli frecvente
Prioritățile statice au problema că unele procese pot fi etern prioritare și pot să producă starvation pentru alte procese. Orice alte explicații sau precizări riscă să
Răspunsuri presupuse sau încercări de a nimeri un răspuns la o întrebare vor conduce de multe ori la un răspuns greșit. Dacă nu știți răspunsul la o întrebare, cel mai bine este să nu răspundeți la acea întrebare. Colecționăm perle și probabil le vom publica (anonime) pentru viitorii studenți.
Lucrări foarte bune
Lucrare 3
3CA, varianta 1
Compilatorul folosește canary values pentru a proteja împotriva vulnerabilităților de tip stack buffer overflow, iar toate celelalte mecanisme de protecție sunt dezactivate. Explicați cum poate fi exploatat acest sistem.
Răspuns: Cu ajutorul canary values putem proteja spațiul dintre un buffer și adresa de retur. Dacă însă în urma unui stack buffer overflow ajungem să suprascriem un pointer de funcție și sărim astfel la acea funcție, sau dacă suprascriem o variabilă care este apoi folosită într-o comparație și alterăm astfel fluxul de execuție, atunci vom reuși să exploatăm programul. Alternativ, printr-o anumită formă de atac de tip information leak putem extrage valoarea canary value și apoi să suprascriem zona de canary value cu valoarea extrasă (adică valoarea inițială) și să suprascriem și adresa de retur a funcției și să executăm cod din altă zonă din spațiul de adresă. Întrucât canary value nu apare modificat, nu se va detecta o problemă.
Precizați un avantaj și un dezavantaj al folosirii unei implementări de thread-uri user level față de o implementare de thread-uri kernel level.
Răspuns: O implementare de thread-uri user level are avantajul unor timpi de creare, schimbare de context și încheiere mult mai rapide. De asemenea, o implementare de thread-uri user level are avantajul că nu necesită suport la nivelul sistemului de operare. Totodată, implementatorul decide funcționarea planificatorului de thread-uri. Pe partea de dezavantaje o bibliotecă cu implementare de thread-uri user level blochează întreg procesul atunci când un thread se blochează, nu poate rula în format multicore (un thread pe un cor) dacă sistemul are așa ceva (sistemul de operare nu este conștient de existența mai multor thread-uri), iar implementaraea planificatorului este în general una colaborativă (nepreemptivă).
Care este diferența dintre un deadlock și o condiție de cursă (race condition)?
Răspuns: Un deadlock blochează instanțele de execuție care sunt blocate în deadlock (thread-uri sau procese). O condiție de cursă înseamnă o ordonare necorespunzătoare a instanțelor de execuție astfel încât o instanță folosește date incoerente/corupte, ducând la un comportament arbitrar al programului.
3CA, varianta 2
Un sistem are activat DEP (Data Execution Prevention) și ASLR (Address Space Layout Randomization). Într-un program care rulează pe acest sistem un hacker a descoperit o vulnerabilitate de tip stack buffer overflow. Ce poate face atacatorul ca să altereze execuția normală a programului?
Răspuns: Un atacator poate ajunge să suprascrie o variabilă care este apoi folosită într-o comparație pentru a altera astfel fluxul de execuție. Dacă un atacator suprascrie o adresă de funcție (pointer de funcție sau adresa de retur) atunci acesta poate sări undeva în codul programului (codul existent al programului, nu o bibliotecă al cărei cod este randomizat - ASLR); dacă în acest cod se găsesc elemente care pot fi folosite de atacator (probabil da), acesta va altera execuția programului pentru a îl exploata.
Pe un sistem pe 32 de biți, un programator testează care este limita de procese pe care le poate crea (folosind fork()
într-o buclă for
într-un program) și care este limita de thread-uri pe care le poate crea (folosind pthread_create()
într-o buclă for
într-un program). De ce va putea crea mai multe procese decât thread-uri?
Răspuns: Când creăm thread-uri fiecare thread ocupă în spațiul virtual de adrese al procesului spațiu pentru stivă (în mod tipic 8MB
pe Linux). Limita de thread-uri care poate fi creată este dată de limita de 4GB
(pe 32 de biți, de fapt mai puțin pentru că există spațiu ocupat de kernel) pentru spațiul virtual de adrese al procesului. În cazul proceselor, se partajează memoria procesului, doar se creează structurile pentru noul proces. Limita este dată de spațiul fizic de memorie pe care îl are sistemul, în mod tipic suficient pentru a crea mai multe procese în sistem decât thread-uri într-un proces.
Precizați două diferențe între spinlock-uri și mutex-uri.
Răspuns: Spinlock-urile folosesc busy waiting în timp ce mutex-urile folosesc blocare. Un spinlock nu reține informații despre thread-urile/procesele care îl folosesc, pe când un mutex menține coada de thread-uri/procese blocate. Un spinlock este util de folosit în regiuni critice de mici dimensiuni în vreme ce un mutex este folosit în regiuni critice de dimensiuni mai mari, eventual și în care thread-urile/procesele fac acțiuni blocante.
3CC, varianta 1
De ce este ASLR (Address Space Layout Randomization) un mecanism de protecție mai puternic pe un sistem x86_64 (64 de biți) decât pe un sistem i386 (32 de biți)?
Răspuns: ASLR este un sistem care plasează la adrese aleatoare zone din spațiul de adresă al procesului (precum heap, stivă, biblioteci partajate). Întrucât este mai mult spațiu disponibili pentru de randomizare pe 64 de biți (de fapt 48 de biți de adrese virtuale) atunci va fi mult mai dificil de “șuntat” ASLR printr-un atac de tipul brute force (încercări repetate). Adică ASLR este mai puternic pe 64 de biți decât pe 32 de biți.
Precizați două avantaje ale folosirii unei implementări de thread-uri kernel level față de o implementare de thread-uri user level.
Răspuns: Thread-urile cu implementare kernel level au avantajul că pot folosi suportul de multicore al sistemului fizic, putând fi planificate câte un thread per core și mărind nivelul de paralelism; thread-urile cu implementare user level nu pot folosi suportul de multicore. De asemenea, un într-o implementare kernel level, un thread care se blochează nu blochează întreg procesul, spre deosebire de thread-urile user level.
Pentru sincronizarea accesului la o variabilă, un programator folosește într-un program multithreaded secvența de cod de mai jos. De ce este problematică această secvență în contextul sincronizării accesului?
/* a is initially 0 */
atomic_inc(&a); /* increment a atomically */
atomic_cmp_xchg(&a, 16, 0); /* atomically do: if (a == 16) a = 0; */
Răspuns: În secvența de mai sus este posibilă preemptarea unui thread (sau rularea simultană a unui alt thread pe un alt procesor) între cele două instrucțiuni. În acel caz, dacă valoarea variabilei este 15
, va ajunge la 17
(două incrementări) iar comparația va eșua, contorul nu va fi resetat la 0
. Operațiile în sine sunt atomice, dar ansamblul nu este atomic.
3CC, varianta 2
Pentru exploatarea unei vulnerabilități de tip stack buffer overflow urmărim suprascrierea adresei de retur a unei funcții. Cu ce vom suprascrie adresa de retur în cazul unui atac de tipul return-to-libc?
Un programator implementează o aplicație de tip server astfel încât la fiecare conexiune către server se creează un thread nou care se ocupă de gestiunea conexiunii. Care este dezavantajul acestei abordări și care este o alternativă?
Răspuns: Dezavantajul acestei abordări este că se creează un thread nou la fiecare conexiune (și care apoi este închis la sfârșitul tratării conexiunii). Se consumă foarte multe resurse și timp de creare/închidere a thread-ului. Alternativa este să folosim un pool de thread-uri care să servească la nevoie cereri sosite, fără nevoia de creare a unui thread la fiecare pas. Pornind de la ideea că thread-urile au dezavantajul că pot omorî întreg serverul la o eroare, putem considera și o alternativă de creare a unui proces, dar este mult prea “heavy” această abordare pentru o situație practică.
Pentru comunicarea și pentru sincronizarea accesului într-un mediu multithreaded, un programator are de ales între folosirea unui vector sau a unei liste. Precizați un avantaj al folosirii vectorului în locul listei din punct de vedere al sincronizării.
Răspuns: Un vector este o structură de date ușor partiționabilă; dacă un thread dorește să modifice anumite elemente din vector va folosi câte un lock pentru o anumită secțiune/partiție a vectorului. În cazul unei liste este problematică partiționarea întrucât listele își pot modifica pointerii între elemente și duce la date incoerente; soluția la liste este un singur lock pe întreaga listă, rezultând în cod puternic serial.
Greșeli frecvente
Răspunsuri cu definiții despre buffer overflow, DEP, ASLR și nu răspuns la întrebare. Învârtit în jurul întrebării, scris ca să fie scris, dar fără răspuns la întrebare. Un soi de învârtit în jurul cozii.
Nu s-a înțeles că dacă ASLR este activat nu se pot face atacuri pe stivă sau în codul din biblioteci. Pentru că stiva și codul din alte biblioteci sunt plasate la adrese aleatoare.
Suprascrierea unei adrese nu este un atac. Trebuie să fie suprascrisă adresa cu o adresă validă care pointează către o zonă de cod executabil; suprascrierea cu o valoare neadecvată va duce la Segmentation fault dar nu exploatarea programului.
Într-un sistem cu DEP și ASLR, nu se poate sări la un cod injectat de atacator. Codul nu poate fi injectat pe un sistem cu DEP, pentru că nu se poate executa ceea ce a scris cineva.
Exprimarea “Limita de thread-uri este dată de numărul de core-uri” (sau alte exprimări echivalente) este nevalidă. Thread-urile care rulează sunt limitate de numărul de core-uri, dar nu cele care există la nivelul sistemului.
“Avantaj kernel level threads față de user level threads: omorârea unui thread nu omoară întreg procesul”. Și variante pe această temă: “Dacă se blochează un thread nu moare întreg procesul.” În ambele cazuri omorârea unui thread duce la încheierea procesului.
Legat de kernel level threads: “Dacă moare un thread în kernel, celelalte încă rulează. În user space dacă moare un thread, acesta generează o excepție care duce la oprirea întregului proces.” În ambele cazuri omorârea unui thread duce la încheierea procesului.
Lucrări foarte bune
CIOCÎRLAN Ștefan-Dan, 331CA
ȘTEFU Teodor, 332CC
CRUCERU Călin, 335CB
COMAN Tudor Emil, 333CC
RIȚĂ Cristian, 335CC
DINU Andrei Mario, 335CC
MURARU George, 333CB
POPA Maria Cătălina, 331CC
MATEȘICĂ Iulian-Răzvan, 331CC
Lucrare 4
3CA, varianta 1
Descrieți cum ajung datele din buffer-ul buf
la dispozitivul de I/O în cazul apelului write(fd, buf, 100)
.
Răspuns: Datele din buffer sunt pentru început copiate în memorie în kernel space în urma unui apel de sistem. Apoi datele ajung în registrele dispozitivului cu ajutorul driverului de dispozitiv din kernel folosind, în general, întreruperi.
Un transmițător efectuează un apel send(s,buf,10000,0)
pe un socket TCP conectat. La receptor se apelează recv(s,buf,10000,0)
. Câți octeți se vor primi la receptor?
Răspuns: Datele pot ajunge în partiții (chunk-uri de date) diferite între transmitățor și receptor din cauza buffer-ului transmițătorului, modului de funcționare a rețelei și buffer-ul receptorului. Receptorul poate primi de la 1
octet până la toți cei 10000
de octeți și aceasta este valoarea întoarsă de apelul recv()
: câți octeți sunt disponibili atunci în buffer-ul receptorului.
Comanda rm f.txt
rulează cu succes. În ce situație rularea acestei comenzi va duce la ștergerea fișierului f.txt
pe un sistem de fișiere cu inode-uri?
Răspuns: Comanda rm
desface un link (un hard link, un nume) de la un inode. Dacă numele/link-ul f.txt
este singurul link la inode-ul aferent fișierului, atunci fișierul va fi șters de pe disc. Dacă există mai multe nume/link-uri la fișier, atunci desfacerea link-ului f.txt
nu va conduce la ștergerea fișierului de pe disc.
3CA, varianta 2
Dați un exemplu de situație în care folosirea polling este de preferat folosirii întreruperilor pentru comunicarea cu un dispozitiv I/O.
Răspuns: Polling este preferat întreruperilor dacă ar veni foarte multe întreruperi, adică dacă procesorul ar sta ocupat în întreruperi în loc să facă acțiuni de procesare. Astfel în cazul dispozitivelor care efectuează transferuri rapide de date și care, astfel, ar genera foarte multe întreruperi, este de preferat să folosim polling. Este cazul plăcii de rețea, care atinge viteze foarte mari (de exemplu plăci de rețea de 10Gbit
).
Când se întoarce cu succes apelul accept()
în cadrul 3-way handshake-ul TCP? Justificați.
după primirea primului pachet (conținând flag-ul SYN)
după primirea celui de-al treilea pachet (conținând flag-ul ACK)
Răspuns: Apelul accept()
se întoarce în momentul în care conexiunea a fost realizată și socket-ul TCP întors poate fi folosit pentru transmitere și recepție de date. Pentru aceasta, trebuie ca întreg handshake-ul să aibă loc pentru a confirma că receptorul și transmitățorul sunt conectați și că numerele de secvență inițiale (ISN: Initial Sequence Number) au fost negociate.
Precizați un avantaj și un dezavantaj al folosirii FAT (file allocation table) pentru gestiunea spațiului într-un sistem de fișiere.
Răspuns: FAT are ca avantaje simplitatea (implementare ușoară) și eficiența stocării, având doar pointeri către indecși. Dezavantajul este că folosim un spațiu proporțional cu dimensiunea partiției care poate ajunge destul de mare pentru partiții mari. De asemenea, nu există suport implicit pentru metadate legate de securitate.
3CC, varianta 1
Care este un avantaj și un dezavantaj al folosirii operațiilor I/O asincrone?
În ce situație apelul send(s,buf,100,0)
pe un socket se blochează?
Cu ce diferă un director de un fișier obișnuit (regular file) din punctul de vedere al implementării sistemului de fișiere?
3CC, varianta 2
De ce, în lucrul cu discul, apelul write()
în general NU se blochează?
Răspuns: În urma unui apel write()
datele sunt, în cazul discului, copiate din user space în buffer cache de unde vor fi la un moment dat scrise pe disc. Apelul de sistem write()
nu interacționează efectiv cu discul, ci doar copiază datele în buffer cache, operație care nu este blocantă (nu interacționează cu un dispozitiv de I/O).
Ce apel de funcție pe socket declanșează handshake-ul TCP de inițiere de conexiune?
De ce numele unui fișier NU este parte a inode-ului?
Greșeli frecvente
Lucrări foarte bune
Examene anterioare