Universitatea Politehnica Bucureşti Facultatea de Automatică şi Calculatoare Departamentul de Automatică şi Ingineria Sistemelor LUCRARE DE LICENŢĂ Algoritmi de planificare a resurselor in sisteme de operare de timp real Coordonator: Absolvent: Prof. dr. Ing. Daniela Saru Levent Menadil Bucureşti, 2013
78
Embed
LUCRARE DE LICENŢĂ - ACSE Departmentacse.pub.ro/wp-content/uploads/2013/07/Licenta_Menadil_Levent_341… · Diagrama de stări a task-urilor ... Simularea algoritmului FCFS
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Universitatea Politehnica Bucureşti
Facultatea de Automatică şi Calculatoare
Departamentul de Automatică şi Ingineria Sistemelor
LUCRARE DE LICENŢĂ
Algoritmi de planificare a resurselor in sisteme de
Figură 2.1. Regimul de funcţionare al procesorului ............................................................................................ 9 Figură 2.2. Diagrama de stări a task-urilor .......................................................................................................10 Figură 2.3.Diagrama simplificată de stări .........................................................................................................12 Figură 2.4. Ordinea de execuţie cu algoritmul FCFS ...........................................................................................13 Figură 2.5. Simularea algoritmului FCFS ...........................................................................................................14 Figură 2.6. Ordinea de execuţie cu algoritmul SJF .............................................................................................15 Figură 2.7. Simularea algoritmului SJF ..............................................................................................................15 Figură 2.8. Ordinea de execuţie cu algoritmul bazat pe priorităţi ......................................................................16 Figură 2.9. Simularea algoritmului bazat pe priorităţi .......................................................................................17 Figură 2.10. Simularea algoritmului Round-robin .............................................................................................18 Figură 2.11. Simularea algoritmului Lottery Round-robin ..................................................................................19 Figură 2.12. Algoritmul de tip coadă multinivel ................................................................................................20 Figură 2.13. Algoritmul de tip coadă multinivel cu feedback .............................................................................20 Figură 2.14. Funcţia de probabilitate a 𝑡𝑡 pentru cei trei algoritmi ....................................................................23 Figură 2.15. Funcţia de probabilitate a 𝑡𝑟 pentru cei trei algoritmi....................................................................24 Figură 2.16. Funcţia de probabilitate a 𝑡𝑡 pentru RR, respectiv Lottery RR .........................................................25 Figură 2.17. Funcţia de probabilitate a 𝑡𝑟 pentru RR si pentru Lottery RR ..........................................................25 Figură 2.18. Constrângerile de timp real...........................................................................................................26 Figură 3.1. Sistem de operare monolitic ...........................................................................................................29 Figură 3.2. Sistem de operare de tip microkernel ..............................................................................................29 Figură 3.3. Sistem de operare de tip hibrid .......................................................................................................30 Figură 3.4. Plăcuţa de dezvoltare STM32-H107.................................................................................................31 Figură 3.5. Programatorul/Depanatorul ST/Link v2. ..........................................................................................32 Figură 3.6. Stagiile pipeline pentru instrucţiunile ARMv7. .................................................................................34 Figură 3.7. Regiştrii procesorului Cortex-M3 .....................................................................................................35 Figură 3.8. Schimbarea contextului in procesorul Cortex-M3 .............................................................................37 Figură 5.1. Schema de reglare automată pentru un regulator discret ................................................................52 Figură 5.2. Execuţia task-urilor in sistemul proiectat .........................................................................................54
Tabel 2.1. Timpurile medii de terminare pentru algoritmii de planificare ...........................................................21 Tabel 2.2. Timpurile medii de răspuns pentru algoritmii de planificare ..............................................................21 Tabel 2.3. Media si abaterea medie pătratică a măsurătorilor ..........................................................................22 Tabel 2.4. Măsurătorile algoritmilor FCFS, SJF si RR ..........................................................................................22 Tabel 3.1. Modurile de pornire pentru microcontrolerul STM32-F107 ................................................................32 Tabel 4.1. Evaluarea criteriilor de performanţă a soluţiei propuse .....................................................................47 Tabel 4.2. Evaluarea criteriilor de performanţă pentru microkernelul ThreadX ..................................................47 Tabel 4.3. Evaluarea criteriilor de performanţă pentru microkernelul FreeRTOS ................................................48 Tabel 4.4. Evaluarea criteriilor de performanţă pentru microkernelul Neutrino .................................................48
4
1 Introducere
În ultimele decade am fost martori ai unei explozii a sistemelor informatice. Progresul
lor s-a datorat în mare măsură utilităţii în viaţa de zi cu zi, începând de la ajutorul cotidian de
la locul de muncă, ajungând la sistemul medical şi terminând cu vastele sisteme informatice
care sunt folosite pentru asigurarea securităţii. Un al exemplu îl reprezintă sistemele integrate
ce sunt prezente acum în aproape toate aparatele electronice.
Modelul clasic al unui sistem informatic este clădit pe interacţiunea dintre componenta
hardware; partea fizică (procesor, memorie şi restul circuitelor auxiliare) şi componenta
software; definită ca partea logică.
Pentru a permite utilizatorului să interacţioneze eficient cu resursele hardware,
sistemele de calcul implementează un nivel separat de software, numit nivelul sistemului de
operare. Acesta este, din punct de vedere structural, un program care gestionează resursele
hardware, iar din punct de vedere funcţional, duce la o abstractizare a resurselor hardware
pentru utilizatori. Sistemele de operare prezintă utilizatorului o interfaţă mai plăcută
comparativ cu interfaţa directă cu hardware-ul, ducând la performanţe sporite. Realizând
această cerinţă, dificultatea utilizării elementelor de bază ale sistemului de calcul (sau simpla
cunoaştere a acestora) se transferă proiectantului sistemului de operare.
Printre cerinţele sistemelor de operare putem aminti: asigurarea accesului mai multor
utilizatori la un sistem de calcul, rularea mai multor programe în paralel, asigurarea protecţiei
programelor din memorie, precum şi nemodificarea majoră a performanţelor sistemului de
calcul prin implementarea sistemului de operare, iar sistemele specializate pot avea cerinţe
mai variate. Spre exemplu, în aplicaţii ce implică stocarea de date pe un sistem distribuit, un
accent mai mare este pus pe asigurarea siguranţei sistemului iar în aplicaţii industriale sau
ştiinţifice apar constrângeri de timp real. De aici apare nevoia dezvoltării unor sisteme de
operare ce pot satisface aceste cerinţe.
Una din cele mai frecvente cerinţe este execuţia în paralel a mai multor aplicaţii. Fie
că este vorba de execuţia a 20-30 de aplicaţii pe un PC sau de achiziţia, prelucrarea şi
5
trimiterea prin o reţea a unor măsurători într-o aplicaţie industrială, multitasking-ul este
esenţial. Întrucât sistemele de calcul au un număr redus de nuclee de execuţie (cel mai adesea
unul singur), şi prin urmare, un număr redus de fire de execuţie fizice, apar probleme în
realizarea, sau mai degrabă, simularea paralelismului.
Obţinerea paralelismului prin maparea fiecărui proces logic pe un proces fizic este
realizat de sistemul de operare, mai precis de o componentă a sa, numită planificator de
thread-uri sau, mai simplu, planificator (engl: scheduler). Aceasta componentă trebuie să ţină
cont de numeroşi parametrii şi constrângeri ducând la dificultăţi majore în proiectarea sa. Din
cauza dificultăţii proiectării unui algoritm de planificare care să satisfacă toate cerinţele date
şi să minimizeze toate criteriile de performanţă se recomandă utilizarea algoritmilor în funcţie
de cerinţele sistemului.
În sisteme interactive, o importanţă sporită o are timpul de răspuns, adică timpul de la
crearea task-ului până la lansarea sa în execuţie iar în sistemele de timp real, timpul de
terminare, definit ca timpul de la crearea task-ului până la execuţia sa în întregime, este vital.
Lucrarea de faţă îşi propune studierea algoritmilor de planificare şi a criteriilor
acestora de performanţă în Capitolul 2, ţinându-se cont de constrângerile de timp real. În
Capitolul 3 am proiectat şi am implementat nucleul unui Sistem de Operare în Timp Real
(SOTR) cu scopul de a testa şi a evalua aceşti algoritmi. Nucleul, de tip microkernel, este
implementat pe un microcontroler STM32-F107, dar va putea fi portat cu uşurinţă către alte
microcontrolere cu acelaşi procesor, Cortex-M3, sau cu versiuni mai noi de procesoare
Cortex-M. Acesta va avea un grad ridicat de scalabilitate, având implementate semafoare
binare şi mutexi că mecanisme de sincronizare şi putând să i se adauge la revizii ulterioare un
sistem de fişiere, drivere pentru periferice, şi un sistem de protecţie a memoriei, toate utile în
aplicaţiile uzuale. În Capitolul 4, am relizat o comparaţie a microkernelului cu alte soluţii
existente de pe piaţă, iar în Capitolul 5, am implementat pe acesta un algoritm de reglare, de
tip PID, sub forma unui task pentru a putea studia criteriile de performanţă definite în
Capitolul 2 şi a evidenţia funcţionalitatea acestui task în timp real.
Scopul urmărit a fost aprofundarea domeniului sistemelor de operare şi a proiectării
lor precum şi a domeniului programării microcontrolerelor. Conexiunea dintre aceste
domenii, aparent neintuitivă, este vitală în sectoarele bazate pe sisteme integrate. Însăşi
proiectarea sistemelor de operare este deseori considerată de mulţi ca fiind un domeniu
6
periculos, destinat unui număr restrâns de oameni, preferându-se utilizarea acestora din
perspectiva de utilizatori, iar lucrarea de faţă încearcă, oferind un exemplu relativ simplu şi
intuitiv, să arate contrariul.
7
2 Aspecte teoretice
Pentru a înţelege conceptul de execuţie în paralel a firelor de execuţie trebuie să fie
definim clar termenul de fir de execuţie (engl. thread). Acesta este cea mai mică unitate de
procesare ce poate fi programată spre execuţie de către sistemul de operare. Nu trebuie făcută
confuzia între program şi fir de execuţie, cel din urmă fiind o instanţiere a unui program în
execuţie şi prezentând parametrii de stare.
În cadrul sistemelor de operare, mai există conceptul de proces pentru a defini o zonă
clară de memorie care poate conţine unul sau mai multe fire de execuţie. Acest concept ne
permite evitarea efectelor nefaste ale utilizării aceluiaşi spaţiu de către mai multe fire de
execuţie şi a alterării codului sau a datelor unui fir de execuţie de către alt fir de execuţie.
Diferenţa dintre proces şi fir de execuţie constă, aşadar în utilizarea unei zone comune de
memorie de către mai multe thread-uri şi utilizarea unor zone separate în cazul proceselor.
Din perspectiva sistemului de operare, schimbarea de la un proces la altul implică saltul la o
zonă de memorie îndepărtată, trecerea la altă stivă, la alt "heap", precum şi schimbarea
completă a parametrilor de stare. Însă trecerea de la un fir de execuţie la altul implica cel mai
adesea încărcarea altui set de regiştrii ai procesorului şi trecerea la altă stivă. Din această
cauză, schimbarea de la un proces la altul se realizează mai lent, iar schimbarea între fire de
execuţie mai rapid, ducând la denumirea de "light-weight process" pentru fire de execuţie.
În mod ideal, firele de execuţie sunt folosite pentru programe ce au nevoie de un
paralelism perfect controlabil. De exemplu, dacă o problemă poate fi împărţită în mai multe
probleme, cu sarcini aproape identice, firele de execuţie ar putea fi o alegere bună, iar dacă nu
este nevoie de un paralelism atât de controlabil, ar trebui utilizate procese [1]. Pentru a obţine
performanţe maxime în sisteme în care se doreşte o paralelizare semnificativă (ex. Serverele
WEB sau sistemele de tip PC) se folosesc procesele iar în cadrul acestora, firele de execuţie.
Pentru a realiza o abstractizare a ambilor termeni când ne referim la îndeplinirea unui
scop, se foloseşte termenul de task. Acest termen, împreună cu cel de fir de execuţie şi de
proces va fi utilizat în continuare în această lucrare, deşi există o distincţie clară între ultimele
două.
8
Sistemele de operare cu suport pentru multitasking au de îndeplinit mai multe cerinţe.
Cele mai elementare sunt alocarea unui spaţiu în memorie pentru variabilele de stare şi pentru
codul programului precum şi asigurarea schimbării între task-uri fără pierderi de informaţii
(doar în cod reentrant) şi relativ rapid [1] iar printre cerinţele opţionale putem enumera
comunicarea şi sincronizarea între task-uri.
Tot sistemului de operare îi revine sarcina asigurării că programele nu se suprascriu şi
că nu se ajunge la pierderi de date. Ideal, într-un astfel de sistem, fiecare task creat de
utilizator ar trebui privit ca având monopol asupra procesorului, a regiştrilor săi de uz general
şi a contorului de program, precum şi a unei zone de memorie.
Alegerea programului ce se va executa în continuare este realizată de o componentă a
sistemului de operare, din cadrul kernelului, numit planificator de task-uri (engl: task
scheduler) iar schimbarea efectivă a regiştrilor procesorului şi sărirea la altă linie de program
este datoria dispacherului, tot din cadrul kernelului [2]. Acesta poate rula ca task separat
pentru a decide task-ul care va urma, sau poate fi apelat de către o întrerupere pentru a aplica
pe moment algoritmul de planificare. Ultima variantă, prezintă avantaje clare de flexibilitate,
kernelul fiind apelat mai des şi ducând la o eficienţă mai bună a algoritmului folosit, însă are
ca dezavantaj schimbarea mai greoaie între task-uri în cazul algoritmilor complecşi şi cu o
durată nedeterministă.
2.1. Algoritmi de planificare
Descrierea algoritmilor de planificare se va face, în continuare cu următoarele
premize: procesorul are un singur nucleu de execuţie, aşadar în orice moment de timp se poate
executa fizic un singur task, şi nu se face distincţia între fire de execuţie şi procese, sistemul
considerându-se că are un singur tip de task implementat.
Pentru a înţelege condiţiile în care se poate schimba contextul şi, implicit task-urile,
trebuie să fie făcute clare condiţiile în care funcţionează majoritatea sistemelor de calcul. În
prezent, date fiind creşterea frecvenţei procesorului şi îmbunătăţirea microarhitecturii
acestora, puterea de calcul este rareori o problemă. În schimb, acestea sunt cel mai adesea
9
limitate de puterea utilizatorului (prin care o să ne referim la orice instanţă situată în afara
calculatorului) de a introduce date. Spre exemplu, motoraşul dintr-un hard disk rulează de
câteva ordine de mărime mai încet decât viteza procesorului. În cazuri concrete, şi luând în
considerare un singur task, acesta din urmă va alterna deseori între perioade lungi în care va
rula pe procesor şi perioade lungi în care va aştepta să primească date din exterior.
Acest lucru reprezintă un avantaj pentru proiectantul sistemului de operare
permiţându-i acestuia să folosească timpul în care task-ul aşteaptă să primească date, pentru a
rula alt task. În figura de mai jos, este reprezentată grafic alternanţa perioadelor în care
procesorul rulează cu programul task-ului (numite aici CPU burst-uri) şi a perioadelor în care
acesta aşteaptă date din exterior (numite I/O burst-uri).
Figură 2.1. Regimul de funcţionare al procesorului
Sursa imaginii este [15]
De aici rezultă o primă categorie de algoritmi de planificare care duc la schimbarea
task-ului curent, şi anume algoritmii non-preemptivi sau cooperativi, (Silberschantz, et al.,
2005) aceştia invocând kernelul doar în situaţia în care task-ul curent trece în starea de
aşteptare pentru a întâmpina un semnal din exterior şi, evident, în situaţia în care task-ul
10
curent se termină de executat. Termenul de cooperativi mai este folosit deoarece, în lipsa unei
întreruperi din exterior pentru a schimba task-ului curent, ei îşi pot ceda între ei, voluntar,
accesul la procesor.
A doua categorie a algoritmilor de planificare o reprezintă algoritmii preemptivi care
pot fi schimbaţi din execuţie de către o întrerupere sau la terminarea unui I/O burst.
Deşi algoritmii non-preemptivi pot fi folosiţi şi pe sisteme ce nu au anumite
componente hardware (de exemplu, un timer) se pot vedea clar beneficiile algoritmilor de tip
preemptiv, aceştia permiţând o flexibilitate sporită, nebazându-se pe evenimente semi-
stocastice cum ar fi cererea unor date de I/O. În sistemele de operare actuale, sunt în folosinţă
algoritmi preemptivi încăpând cu Windows 95 şi cu Mac OS X.
În figura 2.2 este prezentată diagrama corespunzătoare task-urilor organizate de un
algoritm de planificare. În cazul algoritmilor non-preemptivi, dispatcherul nu poate trimite
spre rulare un alt task decât după blocarea task-ului actual.
Figură 2.2. Diagrama de stări a task-urilor
2.1.1. Criterii de performanţă
Alegerea unui algoritm de planificare în defavoarea altuia se face raportat la nişte
criterii de performanţă. Aceştia, la rândul lor, pot fi sau nu semnificativi, în funcţie de context
şi sunt [2]:
11
1. Utilizarea CPU-ului. Reprezintă procentual raportul dintre timpul folosit pentru a
executa cod util şi timpul total care trece. Ideal se doreşte utilizarea procesorului într-o
proporţie de 100%, dar în sistemele reale, utilizarea variază între 40% şi 90%.
2. Rata de terminare a task-urilor. Pentru a măsura eficienţa algoritmului raportat la
cantitatea de muncă efectuată se mai poate lua drept criteriu de performanţa rata în care se
termină task-urile. Pentru task-uri lungi, poate fi de un task / oră iar în cazul task-urilor scurte,
poate fi de 10 task-uri / secundă.
3. Timpul de terminare. Din punctul de vedere al unui task, acesta este caracterizat
prin timpul total scurs de la creare până la terminare. Acest timp este alcătuit din suma
timpilor în care task-ul aşteaptă în lista de aşteptare, execută pe CPU şi realizează transferuri
I/O.
4. Timpul de aşteptare. Acesta reprezintă timpul total pe care task-ul îl petrece în
lista de aşteptare şi este direct afectat de acţiunile luate de planificator
5. Timpul de răspuns. În sistemele interactive se pune accent pe timpul mediu de la
crearea task-ului până când acesta începe să ofere rezultate. Acest timp este în general limitat
de viteza echipamentului periferic de I/O, dar pentru simplificare putem considera acest timp
ca fiind timpul de aşteptare de la crearea task-ului până la lansarea sa în execuţie pentru prima
dată.
Pentru optimizarea algoritmilor de planificare ar trebui să se urmărească mărirea
utilizării CPU-ului şi a ratei de terminare a task-urilor şi să se micşoreze timpul de terminare,
timpul de aşteptare şi timpul de răspuns. Cel mai adesea se doreşte optimizarea valorii medii,
dar în multe situaţii se doreşte obţinerea unui minim sau unui maxim garantat pentru cel puţin
un criteriu de performanţă. În unele situaţii se poate dori obţinerea unei variante mici între
valoarea medie şi valorile de minim sau de maxim.
În continuare vor fi prezentaţi şi exemplificaţi mai mulţi algoritmi de planificare.
Aceştia au fost simulaţi în C++ sub mediul de dezvoltare Devcpp 5.3 cu compilatorul TDM-
GCC pe 64 de biţi. Pentru realizarea diagramelor am folosit programul Gnuplot 4.6. şi un
program realizat în Python 3.3, pentru a translata datele dintr-un format .txt într-un format
.gpl, util programului de plotare.
12
Ca ipoteza se consideră 5 task-uri cu momentul de pornire un timp aleator cuprins
între 0 şi 399 unităţi de timp, cu o durată (care se consideră cunoscută) cuprinsă între 150 şi
500 u.t. şi cu prioritatea între 0 şi 16, din 4 în 4, 16 considerându-se prioritatea minimă, iar 0
prioritatea maximă. De asemenea, cuanta de timp se consideră de 50 u.t. şi se neglijează,
pentru moment, timpul utilizat de planificator şi de dispatcher pentru a schimba între task-uri.
În alegerea acestor mărimi s-a ţinut cont, în defavoarea unor studii şi referinţe pentru
sisteme de operare de pe piaţă (acestea fiind deseori valabile doar în situaţii specifice), doar
de respectarea condiţiilor tipice ce apar în execuţia task-urilor. Ca exemple în acest sens sunt
numărul relativ mic de priorităţi diferite, durata task-urilor comparabilă cu momentul creării
acestora şi mărimea cuantei de timp considerabil mai mică decât durata task-ului.
O altă notă importantă este faptul că nu se în calcul mărimea CPU burst-ului, ci mai
degrabă mărimea task-ului. Aşadar, sistemului simplificat îi lipseşte starea de „blocat pentru
I/O” rezultând diagrama de stări prezentată în figura 2.3.
Figură 2.3.Diagrama simplificată de stări
Criteriile de performanţă luate în considerare au fost timpul mediu de terminare şi
timpul mediu de răspuns. Procesorul se consideră ocupat în proporţie de 100%, neputându-se
lua nivelul de ocupare a CPU-ului drept criteriu iar, deoarece unitatea de timp aleasa este pur
conceptuală, nu se poate lua în calcul nici criteriul ratei de terminare a task-urilor.
2.1.2. Algoritmul FCFS
Unul dintre cei mai simplii algoritmi de planificare este algoritmul Primul Venit –
Primul Servit, (engl: First Come, First Served - FCFS). Acesta este de tip non-preemptiv şi
implică executarea task-urilor în ordinea în care acestea sunt create. Kernelul ţine o coadă de
13
tip FIFO în care sunt trecute task-urile pe măsură ce sunt create, pe o parte, şi din care sunt
şterse task-uri, pe măsură ce intră în execuţie, pe cealaltă parte.
Luăm exemplul a patru task-uri: A, B, C, D, de lungime 5, 1, 3, 2 ms care sunt create
în această ordine la momentul 0. În acest caz, procesele se vor executa precum se vede în
figura 2.4:
Figură 2.4. Ordinea de execuţie cu algoritmul FCFS
În acest caz, timpul de terminare pentru procesul A este de 5 ms, pentru procesul B, 6
ms, pentru procesul C, 9 ms iar pentru procesul D, 11 ms. Aşadar, timpul mediu de terminare
este (5 + 6 + 9 + 11) / 4 = 7.75 ms, iar timpul mediu de răspuns este (0 + 5 + 6 + 9) / 4 = 5 ms.
Simularea acestui algoritm a rezultat, conform aşteptărilor, într-un timp mediu de
terminare şi un timp mediu de răspuns relativ mari, 1047 u.t. respectiv, 650 u.t. Rezultatele
acestui algoritm depind foarte mult de ordinea în care sunt create task-urile şi durata lor, iar
optimizarea lui este practic imposibilă, considerându-i caracterul non-preemptiv. Simularea
acestui algoritm apare în figura 2.5.
14
Figură 2.5. Simularea algoritmului FCFS
2.1.3. Algoritmul SJF
Pentru a încerca scăderea timpului mediu de terminare şi a timpului mediu de răspuns,
s-a încercat prioritizarea task-urilor scurte, punându-le pe acestea înaintea task-urilor create
anterior, dar mai lungi. Acesta este principiul din spatele algoritmului Shortest Job First (SJF)
de tip preemptiv. Algoritmul verifică la fiecare creare de task, dacă acesta are cea mai scurtă
durată, comparându-i durata cu cea a task-ului curent, iar dacă are durata mai mică, se trece la
task-ului nou creat (de unde rezultă caracterul lui preemptiv). Deoarece, deseori, nu
cunoaştem durata task-ului, problema la acest algoritm e dată de estimarea acestui timp.
Aşadar se poate ajunge la pierderea unei părţi semnificative din puterea de procesare al
algoritmului de planificare, care este în sine greu de proiectat. De asemenea, acest algoritm nu
se recomandă aplicaţiilor de timp real, care impun constrângeri dure din cauza neglijării task-
urilor cu durată mare.
Pentru a funcţiona corespunzător, algoritmul mai necesită din partea kernelului un
vector în care sunt ţinute task-urile cu un câmp adiţional în care sunt trecute durata lor
estimată (sau dată).
15
Considerând acelaşi exemplu teoretic de mai devreme cu 4 task-uri, luate în ordinea
sosirii, A, B, C, D de durata 5, 1, 3, 2 şi utilizând algoritmul SJF, ele se vor executa în ordinea
B, D, C, A, cum se vede şi în figura 2.6.
Figură 2.6. Ordinea de execuţie cu algoritmul SJF
Timpul de terminare devine de 1 ms pentru procesul B, de 3 ms pentru D, respectiv 6
şi 11 ms pentru procesele C şi A. Prin urmare, timpul mediu de terminare devine (1 + 3 + 6 +
11) / 4 = 5.25 ms iar timpul mediu de aşteptare este (0 + 1 + 3 + 6) / 4 = 2.5 ms.
Implementarea acestui algoritm a rezultat într-un timp mediu de terminare de 940 u.t.
şi într-un timp mediu de aşteptare de 515 u.t., ambele fiind mai mici decât echivalentele lor în
algoritmul FCFS. Optimizarea acestui algoritm se realizează aproape exclusiv părţii
responsabile de predicţia duratei task-ului sau a duratei CPU burst-ului în cazul proceselor
dependente de I/O. Ordinea de execuţie se poate vedea în simularea din figura 2.7.
Figură 2.7. Simularea algoritmului SJF
16
Cum se vede şi din diagramă, sunt executate prioritar task-urile cu o durată mică. Ce
nu se poate observa, însă, este apelarea planificatorului la fiecare iniţializare de task, acesta
fiind chemat şi la momentul 334 odată cu iniţializarea task-ului 4 precum şi la momentul 369,
cu crearea task-ului 5. Asta duce la un număr dublu de apeluri ale planificatorului faţă de
algoritmul FCFS.
2.1.4. Algoritmi bazaţi pe priorităţi
Problema planificării se poate complica dacă este să luăm în calcul prioritatea
explicită a task-urilor. Algoritmii trataţi anterior aveau prioritatea egală, în cazul algoritmului
FCFS şi o prioritate implicită, dată de durată task-ului, în cazul algoritmului SJF.
Planificarea bazată pe priorităţi poate fi atât de tip non-preemptivă cât şi de tip
preemptivă, în prima situaţie, schimbarea cu task-ul cel mai prioritar din coadă de aşteptare
având loc doar la încheierea task-ului curent. În cazul preemptiv, schimbarea poate avea loc la
o întrerupere dată de expirarea unui timer sau, mai simplu, la crearea noului task (dacă acesta
este mai prioritar decât task-ul curent). La algoritmii bazaţi pe priorităţi poate apărea
problema "înfometării" task-urilor cu o prioritate scăzută, aceştia ajungând să nu se execute
decât după toate task-urile cu o prioritate crescută. Această problemă poate fi rezolvată
modificând dinamic prioritatea taskului, crescându-i prioritatea pe parcurs, când acesta nu se
execută (procedeu cunoscut drept „îmbătrânire”) sau modificând algoritmul prin introducerea
conceptul de cuanta de timp.
Considerând aceleaşi task-uri, cu durata de 5, 1, 3 şi 2 ms, le vom asigna priorităţile 2,
3, 1, 4 şi vom verifica priorităţiile doar la momentul creării task-urilor. Aşadar, ele se vor
executa în ordinea C, A, B, D potrivit priorităţii, cum reiese şi din figura 2.8.
Figură 2.8. Ordinea de execuţie cu algoritmul bazat pe priorităţi
17
Simulând algoritmul cu priorităţi, obţinem timpul mediu de aşteptare de 1167 u.t. şi
timpul mediu de răspuns de 770 u.t., lucru ilustrat şi în figura 2.9.
Figură 2.9. Simularea algoritmului bazat pe priorităţi
În acest exemplu priorităţile task-urilor de la 1 la 5 au fost 12, 8, 4, 4, 12.
Deşi se remarcă un timp mediu de aşteptare şi un timp mediu de răspuns mai mare
decât în ultimii algoritmi studiaţi, acest algoritm are printre cele mai bune performanţe în
sistemele de timp real, garantând un timp minim de răspuns pentru anumite task-uri critice.
2.1.5. Algoritmi de tip Round-robin
O clasă de algoritmi foarte des utilizaţi în prezent este cea de tip Round-robin. Aceştia
sunt algoritmi preemptivi ce implică schimbul între task-uri la un interval de timp stabilit.
Astfel, procesorul executa cuante din fiecare task din coadă, aflat în starea ready, pe rând.
Fiind folosiţi preferenţiabil în sisteme interactive, aceşti algoritmi au mai multe variaţiuni care
ţin sau nu cont de prioritatea task-urilor.
18
În figura 2.10 se poate observa succesiunea task-urilor executate pentru o cuantă de
timp de 50 u.t.
Figură 2.10. Simularea algoritmului Round-robin
Timpul de aşteptare în acest caz este mult mai mare, fiind proporţional cu numărul de
task-uri înmulţit cu durata medie a unui task, însă timpul de răspuns este mult mai mic, fiind
limitat la numarul de task-uri înmulţit cu mărimea unei cuante de timp. Algoritmii de tip
Round-robin pot fi optimizaţi pentru a corespunde cerinţelor sistemului variând mărimea
cuantei de timp pentru a obţine rezultate mai bune.
Aceşti algoritmi pot avea caracteristici de timp real, introducând conceptul de
prioritate. Aşadar se poate ajunge la creşterea dimensiunii cuantei de timp sau a numărului
acestora pentru task-uri cu o prioritate ridicată.
Un astfel de algoritm bazat pe priorităţi şi pe cuante de timp este cel de Lottery
scheduling, care oferă un număr mai mare de cuante de timp task-urilor prioritare. Acest
algoritm nu duce decât la performanţe sporite în medie pentru task-urile cu prioritate mare,
nefiind recomandat sistemelor de timp real. Totuşi, avem garanţia executării cel puţin timp de
o cuantă a tuturor proceselor într-un ciclu complet, rezolvând problema "înfometării".
19
În simularea acestui algoritm am ales acordarea unei cuante de timp task-urilor cu
prioritate 12, a doua cuante pentru prioritatea 8, şi a 4 şi 8 cuante pentru task-uri cu priorităţile
4 şi 0. Ordinea de execuţie este vizibilă în figura 2.11.