Sunteți pe pagina 1din 16

DEI - Department of Information Engineering

Università degli Studi di Padova

Facoltà di Ingegneria
Corso di Laurea MAGISTRALE in Ingegneria Informatica

Corso di Sistemi Real Time

Progetto di tipo 3:
Simulazione di uno Scheduler
FIFO

Studente: Professore:
Nicolo’ Paganin matr.607267 Prof. Sergio Congiu

Assistente:
Prof. Antonio Barbalace

Anno Accademico 2008/2009 18.08.2009


.
Indice

1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Descrizione del Programma . . . . . . . . . . . . . . . . . . . 6
2.1 Classe Task . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Classe TaskInstance . . . . . . . . . . . . . . . . . . . 7
2.3 Classe TaskSet . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Classe Scheduling . . . . . . . . . . . . . . . . . . . . . 8
2.5 Metodo Main . . . . . . . . . . . . . . . . . . . . . . . 9
3 Verifica del funzionamento e output . . . . . . . . . . . . . . . 10
4 Codice Sorgente . . . . . . . . . . . . . . . . . . . . . . . . . . 11
0.1 Introduzione 5

1 Introduzione

Lo scheduler e’ un componente fondamentale dei sistemi operativi multitask-


ing, cioe’ quelli in grado di eseguire piu’ processi (task) concorrentemente.
Lo scheduler si occupa di fare avanzare un processo interrompendone tempo-
raneamente un altro, realizzando cosi’ un cambiamento di contesto (context
switch).
Generalmente, computer con un processore sono in grado di eseguire un pro-
gramma per volta, quindi per poter far convivere piu’ task e’ necessario usare
lo scheduler.
Esistono vari algoritmi di scheduling (FIFO, LIFO, EDF...) che permettono
di scegliere, secondo politiche differenti, quale task far proseguire: l’obiettivo
del progetto e’ realizzare un algoritmo che, dati i parametri temporali di un
insieme di task, ne produca una schedulazione secondo la politica FIFO.
Sara’ inoltre necessario verificare il corretto funzionamento dell’algoritmo per
il set di task proposto.
Il termine FIFO e’ acronimo inglese di First In First Out (Primo ad entrare,
primo ad uscire): il nome dell’algoritmo rispecchia effettivamente la politica
di gestione dei task adottata.
FIFO e’ tipicamente un algoritmo a priorita’ dinamica per gestire task peri-
odici, implementato secondo il funzionamento base di uno queue: i job rilas-
ciati vengono sistemati in una coda ed eseguiti assegnando priorita’ massima
d’esecuzione al primo job rilasciato (che si trova all’inizio della coda). Un
politica del genere, chiaramente, offre il vantaggio di migliorare il tempo di
risposta dei job rilasciati per primi ma, questa stessa caratteristica si trasfor-
ma in un notevole svantaggio perche’ processi piu’ prioritari rilasciati dopo
processi meno prioritari hanno meno priorita’ e questo potrebbe portare ad
uno schedule non feasible

Il programma realizzato ricevera’ in ingresso l’insieme di task di fine pag.


121 del libro considerati della stessa priorita’ come da specifiche, ciascuno
dei quali e’ rappresentato come segue:

hp
TASKSET={Ti = (φi , pi , ei , Di ) : i ∈ [i, n]} n=
pi
6 Indice

• Fase Iniziale (φi ) = Tempo di rilascio del Job Ji.1 del task Ti ;
• Periodo (pi ) = e’ il minimo intervallo temporale fra tempi di rilascio di
job consecutivi, del task i-esimo;
• Tempo di esecuzione (ei ) = e’ il massimo tempo di esecuzione di tutti
i Job di Ti ;
• Deadline (Di ) = Tempo massimo di risposta;

Per semplicita’ di calcolo, i parametri d’ingresso sopra citati saranno sempre


dei valori interi: e’ semplice verificare che, qualora si presentassero valori
decimali, la moltiplicazione di questi per una potenza di 10, (per ogni singolo
parametro di ciascun task esaminato) non influenzerebbe la schedulabilita’ e
l’esecuzione dell’algoritmo.

2 Descrizione del Programma


Il programma e’ composto da quattro classi fondamentali:
• Task
• taskInstance
• taskSet
• Scheduling

Le prime due servono a modellare il problema in questione, mentre le rima-


nenti ne rappresentano la soluzione poiche’ al loro interno viene effettuata la
schedulazione vera e propria dei task.
Oltre a queste, ed a un immancabile metodo main, sono dichiarati anche un
paio di funzioni utility esterne, richiamate piu’ volte all’interno delle classi:
si tratta delle funzioni MCD e mcm che, dati in ingresso dei parametri interi,
restituiscono rispettivamente il massimo comune divisore e il minimo comune
multiplo.
Per il calcolo del MCD viene implementato l’algoritmo di Euclide, mentre per
il calcolo del mcm viene effettuato il rapporto tra il prodotto dei due valori
in input ed il massimo comune divisore fra questi (calcolato con la funzione
di cui sopra).
Segue una descrizione dettagliata delle classi precedentemente presentate.
0.2.1 Classe Task 7

2.1 Classe Task


La classe Task costruisce l’oggetto di tipo Task; questo, come da specifiche,
e’ caratterizzato da cinque campi: phase, period, executionTime, deadline e
name; i primi quattro campi sono rappresentati da numeri interi e il loro
nome e’ autoesplicativo, mentre l’ultimo campo e’ una stringa utilizzata per
distinguere univocamente ciascun task (ottenuta unendo il nome del task con
l’indice dell’occorrenza).

2.2 Classe TaskInstance


Come gia’ precedentemente accennato, anche questa classe viene usata per
modellare il problema. L’oggetto che costruisce questa classe e’ costituito dai
due campi task e index.
Il primo campo e’ un riferimento ad un oggetto di tipo Task, mentre il secondo
e’ l’indice che conta l’occorrenza di ciascun task. Dentro la classe e’ possibile
scorgere una serie di metodi che permettono di manipolare gli oggetti in essa
costruiti:

• setIndex: e’ un metodo utilizzato per settare l’indice che rappresenta


l’occorrenza di ciascun task;

• getIndex: restituisce l’indice fornito dal metodo precedente. L’indice


sara’ usato per la costruzione di una chiave alfanumerica associata al
task;

• getStartTime: calcola l’effettivo istante di rilascio del job;

• getName: restituisce una stringa che identifica univocamente ciascun


job, ottenuta concatenando il nome del tipo del task (T1,T2, ...) con
l’indice che rappresenta l’occorrenza di ciascuno dei loro job e che ne
sancisce l’ordine di arrivo.

I metodi appena descritti, escludendo il primo, sono dichiarati come const e


agiscono in sola lettura sull’oggetto trattato.
8 Indice

2.3 Classe TaskSet


Questa classe estende la classe vector (appartenente alle librerie del linguag-
gio C++), al fine di creare un vettore di tipo Task. In pratica, questa classe
riceve in ingresso l’insieme di task da schedulare e crea un opportuno vettore
che li contiene.
I metodi utilizzati all’interno della classe sono soltanto due: add e hyperPe-
riod.
Il primo metodo permette di aggiungere un elemento in coda al vettore, at-
traverso l’ausilio della primitiva pushback. E’ importante sottolineare che
add e’ di tipo void, percio’ non restituisce alcun valore in uscita.
Il secondo metodo, invece, restituisce un valore intero che rappresenta la du-
rata dell’iperperiodo; e’ proprio qui che vengono usate le funzioni mcm e
MCD precedentemente descritte. Per il calcolo del minimo comune multiplo
si procede a coppie: si calcola il minimo comune multiplo dei primi due nu-
meri e successivamente si reitera l’operazione di calcolo tra il valore appena
calcolato e un nuovo valore di input, finche’ non viene esaurita la lista.

2.4 Classe Scheduling


Questa classe si occupa della schedulazione vera e propria dei task utilizzan-
do due strutture dati fondamentali: il vettore e lo stack.
Come la classe TaskSet questa classe estense la clsse vector. Viene creato in-
fatti, per simulare lo scheduler di tipo FIFO, un array contenente puntatori
ad oggetti di tipo taskInstance ordinati secondo l’ordine di rilascio effettivo.
Quando tale vettore e’ stato creato deve essere testata la schedulabilità .

Le funzioni implementate per soddisfare i requisiti proposti sono 4:

• add: Inserisce nel vettore i job e li ordina per inserimento. Ogni volta
che viene invocato determina la posione del job nel vettore in modo che
alla fine il vettore risulti ordinato per data di rilascio del job.
• Schedule: Questa funzione serve per la costruzione del vettore. Calcola
l’iperperiodo, e quindi il numero di job che devono essere rilasciati per
ciascun task nell’iperperiodo e li inserisce nel vettore tramite il metodo
add descritto in precedenza.
0.2.5 Metodo Main 9

• TestSchedulabulity: e’ un metodo booleano che restituisce true se il set


di task e’ schedulabile, falso in caso contrario. Esso va a controllare se
per ciascun task in arrivo se il tempo a disposizione e’ sufficiente per
garantirne l’esecuzione e in caso affermativo, schedula il task ed aggior-
na le variabili di stato che verranno usate per il controllo successivo; in
caso contrario esce dal ciclo e segnala che il set di task non e’ feasible.
• print: e’ una funzione ausiliaria e serve solo per stampare i dati relativi
alla schedulazione di ciascun task e quindi il contenuto della coda.

2.5 Metodo Main


All’interno del programma principale vengono dichiarati i seguenti task:

• T1( 0, 300, 100, 300)


• T2( 50, 400, 100, 100)
• T3( 75, 750, 20, 750)

Questi tre task periodici vengono cosi’ inseriti nel vettore tasks attraverso la
seguente istruzione: tasks.add(Ti ). Al vettore cosi’ costituito viene applicato
il metodo schedule, per la costruzione dell’iperperiodo e, infine, applicando il
metodo testSchedulability si puo’ verificare la schedulabilita’ dell’insieme di
task fornito.
10 Indice

3 Verifica del funzionamento e output


Il set di task proposto e’: T1 = (0, 3, 1, 3), T2 = (0.5, 4, 1, 1), T3 = (0.75,
7.5, 2, 7.5).

La prima operazione da fare per valutare la schedulabilita’ di un sistema di


task e’ quella di calcolare il fattore di utilizzazione:

P3 ei 1 1 2
U= i=1 = + + = 0.85 < 1
pi 30 4 7.5

Proviamo ora a vedere, ripercorrendo i passi fatti dallo scheduler, se il set di


task e’ schedulabile:

• al tempo 0 viene rilaciato T1 1 e rimarra’ in esecuzione per 1.


• al tempo 0.5 viene rilasciato T2 1 Dato che il processore e’ occupato
con T1 1, T2 1 viene inserito nella coda (insieme a T3 1 al tempo 0.75).
Nell’istante 1 in cui finisce T1 1 viene schedulato T2 1. Avendo tempo di
esecuzione 1, T2 1 finira’ all’istante 2. Dato che ha una deadline relativa
di 1, avrebbe dover finito al tempo 1.5.

Il set di task non e’ schedulabile con uno scheduler FIFO.

Di seguito viene mostrato l’output pricipale fornito dal programma:

Figura 1: output SchedulerFIFO

Il set di task risulta quindi non schedulabile, i dati di Inizio, Fine e Deadline
sono tutti moltiplicati per un fattore 100 in modo da rendere i valori proposti
dal libro interi.
0.4 Codice Sorgente 11

4 Codice Sorgente

//*************************
//Nicolo’ Paganin
//Matr:607267
//Data: 18.08.2009
//*************************

#include <iostream>
#include <vector>
#include <sstream>

using namespace std;

// - Funzioni utility
int MCD( int a, int b )
{
int r = b;
while( r != 0 )
{
r = a % b;
a = b; b = r;
}
return a;
}

int mcm( int a, int b )


{
return a * b / MCD( a, b );
}

// - Strutture dati
class Task
12 Indice

{
public:
int phase,
period,
executionTime,
deadline;
string name;

Task( int p, int t, int e, int d, string n )


{
phase = p;
period = t;
executionTime = e;
deadline = d;
name = n;
}
};

class TaskInstance
{
int index;
public:
Task &task;
TaskInstance( Task & t, int j ) : task(t), index(j) {}

void setIndex( int i )


{
index = i;
}
int getIndex() const
{
return index;
}
float getStartTime() const
0.4 Codice Sorgente 13

{
return index * task.period + task.phase;
}
string getName() const
{
stringstream ss;
ss<<task.name<<"_"<<index+1;
return ss.str();
}
};

class TaskSet : public vector<Task>


{
public:
void add( const Task & t )
{
this->push_back(t);
}
int hyperPeriod() const
{
int result = 0;
if( ! this->empty() )
{
result = (*this)[0].period;
for( int i = 1; i < this->size(); i++ )
{
result = mcm( result, (*this)[i].period );
}
}
return result;
}
};

class Scheduling : public vector<const TaskInstance *>


14 Indice

{
public:
void add( const TaskInstance * t )
{
vector<const TaskInstance *>::iterator it;
for( it = this->begin(); it < this->end() && t->getStartTime()
>= (*it)->getStartTime(); ++it );
this->insert( it, t );
cout<< "Task added: "<< t->getName() << endl;
}

void schedule( TaskSet & taskSet )


{
int hp = taskSet.hyperPeriod();
cout<<("\nI TASK IN ESAME SONO: ")<<taskSet.size()<<"\n"<<endl;
for( int i = 0; i < taskSet.size(); i++ )
for( int j = 0; j < hp / taskSet[i].period; j++)
add( new TaskInstance( taskSet[i], j ) );

bool testSchedulability()
{
bool ok = true;
float usedTime = 0;
cout << "\nTask I.\t|\tInizio\t|\tFine\t|\tDeadline\n";
for( int i=0; ok && i<size(); i++)
{
const TaskInstance & t = *(this->at(i));

if( usedTime < t.getStartTime() )


usedTime = t.getStartTime();
0.4 Codice Sorgente 15

cout << t.getName()<<"\t|\t" <<


usedTime << "\t|\t" <<
usedTime+t.task.executionTime << "\t|\t" <<
t.getStartTime()+t.task.deadline<< endl;

if( usedTime + t.task.executionTime > t.getStartTime()


+ t.task.deadline)
ok = false;
else
usedTime += t.task.executionTime;
}
return ok;
}

void print()
{
cout<<"Vista della coda:"<<endl<<endl;
cout<<"Pos.\t|\t "<<"Nome\t|\t "<<"Inizio\n";
cout << "--------+---------------+---------------\n";
for(int i=0; i<size(); i++)
{
cout<<i<< "\t|\t"<<this->at(i)->getName()<< "\t|\t"<<
this->at(i)->getStartTime()<<endl;
}
}
};

int main()
{
const Task T1( 0, 300, 100, 300, "T1" );
const Task T2( 50, 400, 100, 100, "T2" );
const Task T3( 75, 750, 200, 750, "T3" );
16 Indice

TaskSet tasks;
tasks.add(T1);
tasks.add(T2);
tasks.add(T3);

Scheduling s;
s.schedule( tasks );
cout<<endl<<endl;
s.print();

cout<< "\nHyperperiod: " <<tasks.hyperPeriod() <<endl;


cout<<"\nTest della Schedulabilita’:"<<endl<<endl;
if( s.testSchedulability() ) cout<<"\n\nTask Schedulabili!\n"<<endl;
else cout<<"\n\nTask NON Schedulabili!\n"<<endl;
}

S-ar putea să vă placă și