Odata cu Windows, notiunea de multitasking este un cuvint cheie pentru o9c10cp
SO. Legat de aceasta notiune sunt termenii de preemption sau deadlock.
In general, prin multitasking se intelege executarea simultana a mai multor taskuri. Traducerea corecta spune doar ca se executa mai multe taskuri-nici
fara a se specifica simultaneitatea lor (multiprogramare).
Aceasta ambiguitate isi are originea in faptul ca PC-urile uzuale sunt calculatoare cu un singur procesor, deci pot executa la un moment dat un singur program (task). O prelucrare paralela a mai multor taskuri presupune insa existenta mai multor procesoare (multiprocesare).
Cu toate acestea, prin multitasking la sistemele de operare de pe
PC-uri-se intelege tocmai acest paralelism tehnic imposibil. Deci problema este numai aparenta deoarece pe calculatoare cu un singur procesor, multitaskingul nu este decit o "iluzie optica".
Principiul ce sta la baza acestui efect se poate ilustra cu exemplul unui zidar care lucreaza la un sir de case: de la ora 9 la 10 lucreaza la casa
A, de la ora 10 la 11 la casa B, de la 11 la 12 la casa C, apoi din nou la casa
A si asa mai departe, pentru spectatorul accidental, cele trei case par sa se
ridice simultan.
Tocmai aceasta strategie o aplica si sistemele multitasking cu deosebire ca schimbarea se face de regula cam la 0,05 secunde. Ca si in viata reala, si
calculatorului ii trebuie un timp pentru a comuta de la un task la altul
(schimbare de context). Chiar daca SO reusesc sa faca extrem de rapid trecerea,
totdeauna, in multitasking, un program se deruleaza mai incet decit daca procesorul i-ar apartine in exclusivitate.
De ce este nevoie de aceasta tehnica pretentioasa? Uneori, nu se poate altfel. Exista, de exemplu, sistemele multiuser, in care mai multi utilizatori,
de la terminale diferite, lucreaza pe acelasi calculator.In astfel de cazuri,
multitasking-ul este un singura solutie pentru ca fiecare sa poata lucra.
In sistemele cu un singur utilizator -ele formeaza marea majoritate a
PC-urilor- necesitatea multitasking-ului nu mai este deloc evidenta. Cu toate
acestea, iata urmatoarea situatie : asa-numitul "spooler" de exemplu,
permite o usurare semnificativa a lucrului. El face posibila tiparirea unui document
in "background", in timp ce se lucreaza deja la textul urmator. O
astfel de functie - la fel ca un ceas de exemplu, afisat intr-un colt al ecranului - reprezinta deja o forma simpla de multitasking. Pe baza intreruperii de timer,
programul curent este mereu intrerupt. Controlul procesorului trece atunci la
un subprogram. Acesta trimite atunci citeva caractere de text spre imprimanta
sau actualizeaza afisajul de ceas, redind apoi controlul programului principal.
Astfel de subprograme se gasesc pe toate PC-urile sub forma programelor rezidente (asa-numitele "TSR"-uri). Marele dezavantaj al acestora
consta in faptul ca un program intrerupe un altul intotdeauna ritmic. Sistemele de operare moderne ofera de aceea un mecanism, in centrul caruia sta un comutator. Acesta
permite pastrarea simultana in memorie a mai multor programe, trecerea de la
unul la altul facindu-se numai la solicitari (prin hotkey=tasta fierbinte, combinatie de taste sau click pe mouse).
Astfel de exemple dintr-un program de grafica se poate lucra la o figura, pentru a comuta apoi in prelucrarea de texte si a o insera acolo. O
zona de memorie comuna - numita "clipboard" - face posibil acest transfer
de date. Pentru ca acest lucru sa functioneze, ambele programe trebuie sa fie scrise special pentru acest mediu, deoarece:
* - un transfer de date poate functiona numai daca ambele programe lucreaza
cu acelasi format al datelor in clipboard
* - ambele programe trebuie sa aiba loc simultan in memorie.
Deoarece din motive de spatiu, acest lucru nu este posibil uneori, este treaba programatorului sa pregateasca programele pentru a putea fi rulate in
mediul comutatorului, deci sa le fragmenteze, dindu-le atribute ca temporar
depozitat pe disc sau sa ramina tot timpul in memorie. Daca aceasta premisa
nu este indeplinita, trebuie evaluat, respectiv restaurat, intotdeauna intreg
programul pe harddisk - ceea ce dureaza foarte mult.
Practic deci nu avem de a face cu o prelucrare paralela. Conteaza si momentul cind se face comutarea taskurilor. Cea mai des intilnita situatie este cind, intr-un sistem in care avem de exemplu 3 taskuri, alocarea se face pe
rind o cuanta de timp identica. Pe cit de onest poate sa para acest lucru la
prima vedere, pe atit poate fi deranjant. Un program de prelucrare texte este
ocupat mai ales sa astepte tastari de la operator. Oricit de iute s-ar tasta,
la timpi de comutare taskuri de ordinul ms, intre doua tastari sunt intervale mari, deci se risipeste timp UC.
De aceea in general se are grija ca unui task sa i se aloce timp calculator numai atunci cind nu asteapta un "eveniment"- o apasare
de exemplu sau o miscare a mouse-ului. Premisa necesara pentru acest lucru este desigur
ca SO sa stie cind un task asteapta un eveniment. Sub Windows de exemplu, aceasta problema se rezolva prin faptul ca programele aplicative interogheaza
totdeauna SO asupra producerii unui eveniment. Astfel, in Windows se cunoaste, in mod necesar, o stare a unui program : in asteptare. In afara de aceasta, un task se mai poate gasi in starile gata de executie sau executie. Diagrama
de stari este cea prezentata la curs.
Comutarea efectiva a taskurilor o rezolva o rutina sistem numita dispecer (distribuitor). Aceasta opreste un task si salveaza toate datele sale
(registrii, stive). Apoi incarca toate datele salvate ake urmatorului task si
il lasa sa continue de unde a fost intrerupt.
Cind anume intervine dispecerul, este o decizie a altei functii de nucleu numita scheduler. Planificatorul decide, care task, cind ajunge la rind si cite cuante de timp sa i se aloce. Decizia se ia in felul urmator: generatorul de tact al scheduler-ului este intrerupere de timer, declansata
de 18,2 ori intr-o sec. Aceasta determina o unitate de timp de (cuanta) de
55 ms. O parte din acest timp, scheduler-ul il consuma el insusi, deoarece el trebuie sa verifice daca unitatile de timp alocate unui task s-au consumat deja. Cind se executa mai multe taskuri, scheduler-ul lucreaza de regula cu
liste, care in plus pot fi ordonate dupa prioritati. In cel mai simplu caz,
exista numai o astfel de lista. Scheduler-ul stie, care este taskul care tocmai se executa, si cauta urmatorul. Daca acesta este in asteptare, atunci
trece la urmatorul task si tot asa mai departe, pina ce gaseste unul care este gata de executie.
Strategia de parcurgere a listelor costa timp. Aceeasi pierdere de timp se produce si la aparitia unui eveniment: in acest caz, lista trebuie parcursa pentru a afla care este taskul (sau care sint taskurile) care il astepta. Acesta e trecut atunci din starea in asteptare, in gata de executie
Pentru a evita aceste intirzieri, unele sisteme gestioneaza liste separate pentru taskurile in asteptare, gata de exeutie si active (ultima fiind necesara numai in cazul sistemelor multiprocesor). Procedura descrisa
se refera la o strategie secventiala : toate taskurile ajung pe rind sa lucreze, chiar daca pentru intervale de timp diferite. Cind s-a ajuns la ultimul se continua din nou cu primul - lista de taskuri este o lista circulara.
Procedeul poarta numele de "round robin". Nucleul acestuia este asa-numita
"preemption" - inlocuire. Un task il inlocuieste pe altul, daca s-a
epuizat timpul afectat.
Contraponderea acestei strategii o reprezinta asa-numita strategie de urgenta. In acest caz, lista de taskuri este sortata dupa prioritati, deci un task este lasat sa ruleze, iar urmatorul porneste abia cind predecesorul
lui s-a terminat. In acest caz exista doua feluri de inlocuiri :
* - daca apare un task nou, de prioritate mai mare decit cel in curs, atunci
acesta inlocuieste task-ul care se executa. Exista insa si modele, in care acest lucru nu este permis. In cazul acestora, task-ul nou este asezat in coada listei. Algoritmul se numeste "FCFS"(first come,first served).
* - chiar si intr-un sistem ghidat prin prioritati, un task nu poate fi neintreruptibil : SO intervine regulat, pentru a afla daca au aparut task-uri
noi. Deseori se tine un fel de statistica, care poate duce la o redistribuire
dinamica a prioritatilor. Motivul este simplu: daca SO constata ca un task se
gaseste mereu in starea in asteptare, inseamna ca acesta nu este prioritar,
deci i se reduce prioritatea, si se va lasa inlocuit de alt task.
Pentru SO Windows, in cazul normal se executa un singur task, cel care corespunde ferestrei active. Nimeni si nimic nu il poate inlocui - doar
daca task-ul interogheaza SO daca nu cumva s-a produs vreun eveniment. Acest
lucru se face destul de regulat, caci altfel nu se afla niciodata daca utilizatorul a selectat un meniu sau a apasat o tasta. Apelurile de functie
necesare pentru aceasta redau in mod automat controlul SO, care trece taskul
curent in asteptare si se executa urmatorul task, de exemplu ceasul afisat.
Acesta testeaza daca s-a produs un eveniment tip timer, iar daca da, se citeste ceasul sistemului si se deseneaza noua pozitie a limbilor ceasului pe
ecran. Apoi se interogheaza despre un nou eveniment relevant - moment in care,
din nou, controlul trece la SO.
Acest procedeu se numeste non-preemtiv sau cooperativ. Totusi, pentru sisteme cu un singur utilizator, aceasta strategie este satisfacatoare.
Renuntarea la schimbarile de context periodice din sistemele preemptive are si avantajul in ce priveste viteza de executie, dar si simplifica conlucrarea
diverselor componente ale unui sistem. Acest lucru are loc deoarece intreruperea unui task trebuie facuta cu precautie pentru a nu se afla de exemplu intr-o sectiune critica. Astfel, inceputul unei sectiuni critice poate fi anuntat printr-o functie de genul "forbid()", pentru a se permite apoi intreruperile
cu "permit()".
SO la rindul lui poate si trebuie sa impiedice accesul simultan a doua taskuri la o resursa unica, cum ar fi harddisk-ul sau imprimanta.
Imaginati-va ca doua taskuri tiparesc simultan doua texte diferite, fiecare
punind cite o litera cind e la rind.
Pentru solutionarea acestei probleme exista doua strategii, si anume excluderea reciproca (mutual exclusion) si accesul exclusiv (exclusive access).
In primul caz, SO decide asupra distribuirii drepturilor de acces, iar in cel
de-al doilea, task-ul trebuie sa le solicite si sa le predea.
Astfel de solicitari constituie una dintre componentele de baza ale comunicatiei inter-task-uri bazata pe mesaje (IPC). In acest context, si unele
rutine trebuie vazute ca task-uri.
Derularea corecta a acestei comunicatii este in sarcina SO, altfel putind apare situatia de blocaj (deadlock) : task-ul A asteapta un mesaj de
la B, iar task-ul B asteapta un mesaj de la A. Astfel de situatii duc la blocarea sistemului si de aceea ar trebui pe cit posibil evitate sau macar
corectate de SO.
In Windows de exemplu, intrega responsabilitate ramine programatorului.
El trebuie sa aiba grija ca sa nu apara deadlock-uri. Ceea ca uneori este dificil,
mai ales cind se foloseste functia Yield(), cedeaza trecerea. Aceasta opreste
task-ul curent si porneste un altul, care deja asteapta. Daca insa un task cedeaza trecerea in timp ce prelucreaza un mesaj, pe care un alt task i l-a
trimis, se ajunge la blocajul amintit, deoarece taskul expeditor asteapta pina cind taskul destinatar achita mesajul prin intermediul functiei ReplyMessage.
(ori,destinatarul deja nu mai este activ).
Acest lucru este usor de evitat, daca se tine cont de anumite specificatii : Yield() nu se cheama totdeauna explicit, ci este deseori apelata automat de functii precum DialogBox() sau MessageBox(). Windows ofera
o functie numita InSendMessage(), cu care se poate constata daca mai exista mesaje neachitate. Deci daca programatorul testeaza totdeauna cu InSendMessage() daca apelul uneia din cele opt functii critice (care apeleaza Yield()) este
permis, atunci nu exista blocaje.
Glosar de termeni
-deadlock-doua task-uri se asteapta reciproc, ducind la blocajul intregului sistem
-distribuitor-rutina sistem care asigura oprirea unui task si lansarea altuia
-eveniment-o actiune extrema, de care trebuie sa se tina seama (o tastare, o miscare, o miscare a mouse-ului, etc.)
-FCFS-First Come, First Served (primul venit, primul servit), toate taskurile sint intr-o coada de asteptare. Cel din capul listei va fi primul servit
-non-preemtive-sistem multitasking fara control de timp sau prioritati.
Fiecare task trebuie sa redea de buna voie controlul SO, care poate lansa atunci un alt task.
-PCB-Process Control Block-forma extinsa a TCB
preemptive-sistem multitasking ghidat de timp sau de prioritati. Un task inlocuieste un altul (preia procesul) cind a trecut un anumit interval de timp sau pentru ca are prioritate mai mare
-prelucrare cvasi-paralela-pe calculatoare cu un singur procesor, o prelucrare paralela nu este posibila. In schimb fiecare task este prelucrat cite o cuanta de timp, dupa care se trece urmatorul
-prelucrare paralela- mai multe taskuri sau sarcini partiale se desfasoara simultan pe mai multe procesoare, sub un SO unic.
-round-robin-aranjament circular al listei de taskuri (dupa ultimul, urmeaza din nou primul)
-scheduler-rutina sistem care decide, din mai multe taskuri posibile, care este cel selectat pentru continuare si cit timp i se aloca.
-sistem multiprocesor- calculator realizat cu mai mult de un procesor, in ultimul timp folosind mai ales transputere.
-sistem multiuser-calculator ce poate fi folosit simultan de mai multi utilizatori, de la mai multe terminale (de exemplu UNIX)
-spooler-task ce se deruleaza in background (plan second) care preia foarte repede in zona proprie de memorie un fisier ce urmeaza a fi tiparit,
pentru a-l tipari apoi in intregime, consumind pentru aceasta foarte putin din timpul procesorului, disponibilizat astfel pentru activitati in foreground (prim plan)
-TCB-Task Control Block - zona de date a unui task, in care sint memorate toate datele specifice pentru SO relative la task (nume, prioritate, stare, etc.)
Tema :
- sa se construiasca exemple cu apelurile din clasa spawn... si exec... din
biblioteca process.h si sa se evalueze posibilitatile oferite.
Vezi exemplele parent.c, cop1.c, cop2.c. Sa se imagineze si alte fluxuri de executie. Sa se testeze toti parametrii.
- asigura multitasking unul din apelurile testate ?
- sa se transmita parametri printr-un fisier deschis