Simulazione di uno scheduler

saverio @ 23:18

1. ANALISI DEL PROBLEMA

Scopo di
questo progetto è simulare il comportamento di uno scheduler di processi, ossia
quel programma che nei moderni sistemi operativi ripartisce l’utilizzo di
risorse tra più processi concorrenti tenendo conto delle caratteristiche e
necessità di ognuno. A differenza di questi ultimi però, il nostro scheduler si
baserà unicamente sulla priorità di ciascun processo e in base a tale valore

assegnerà l’ipotetica risorsa.

Alla base del nostro scheduling, essendoci il concetto di priorità, c’è quindi
l’esistenza di un certo numero di fasce di priorità, N, per ciascuna delle
quali esisterà una coda di processi in attesa. Il funzionamento di questo
scheduler è essenzialmente ciclico: all’inizio del ciclo viene creato un numero
casuale n di processi per ciascuno dei quali sarà assegnata una priorità,
anch’essa in modo random tra 1 e la costante MP, poi si procederà
all’esecuzione di un numero qualsiasi di processi m, diverso da n, dopodiché
continuerà tale ciclo con la creazione e successiva esecuzione di processi
finchè non si supera una condizione limite che abbiamo imposto essere una
condizione temporale definita dalla
costante MAXTEMPO. E’ da notare che l’output del programma sta
nell’aggiornare un file, logstato.txt, in cui sono memorizzati gli stati delle
code aggiornati ad ogni ciclo; inoltre ad ogni creazione di processo tutte le
informazioni relative verranno scritte in un altro file, logprocessi.txt. Con
la visualizzazione di questi file sarà possibile conoscere sia le informazioni
relative ai processi (pid, key e time), sia lo stato delle K code al termine di
ogni ciclo.


2. STRUTTURE DATI

Nel precedente paragrafo è stato introdotto il
problema che tale programma si incarica di risolvere, ora ci addentriamo più
nello specifico di tale risoluzione.

A tale
scopo è di fondamentale importanza considerare quali sono le strutture dati
adottate. Innanzitutto diciamo che i processi sono rappresentati da strutture, nodoheap, che come mostrato in figura
hanno tre campi: un campo pid,
identificatore univoco del processo, un campo key
contenente la priorità di tale processo, e infine un campo time indicante l’istante in cui questi
è stato creato.

Tale
struttura base va ad essere parte essenziale delle code di priorità dei
processi, ossia strutture dati heap la cui proprietà di ordinamento parziale
viene usata per tener conto della maggiore o minore priorità di un processo
rispetto ad un altro.


L’implementazione delle code è stata realizzata tramite strutture, tipoheap, con un campo vett, puntatore alla struttura heap
vera e propria, un campo indicante il numero di elementi di tale heap, heapsize, e infine un campo intero allocato che viene utilizzato per
verificare se è necessaria o meno una ulteriore allocazione di memoria alla
coda in questione. Infatti potrebbe darsi che durante l’esecuzione del
programma venga creato un processo che dovrebbe essere inserito in una coda già
piena e che quindi necessita di ulteriore memoria. Usando questo ulteriore campo
sarà possibile allocare al processo un altro blocco di memoria, la cui
lunghezza base è stata da noi definita tramite la costante LUNGHEAP; viceversa nel caso in cui
non ci siano più elementi nella coda e il valore di allocato è ancora diverso
da zero si libererà memoria inutilmente allocata. Praticamente l’allocazione
della memoria alle code diventerà all’occorrenza dinamica evitando lo spreco di
spazio e tempo impliciti nell’uso di alcune funzioni del C, tipo realloc, poco affidabili ed
“economiche”. Inoltre abbiamo ritenuto buono tale approccio perché permette
nello stesso momento di avere anche code di dimensione diversa in dipendenza
dalla priorità e quindi dal fatto che code con priorità maggiore avranno un
numero di processi sempre minori essendo schedulati per primi.

Infine,
dato che, come già detto i processi sono scelti in base alla priorità, è utile
stabilire delle fasce di priorità. A tale scopo abbiamo definito un ulteriore
tipo di dato, tipoprocesso, contente il valore pkey indicante il range di priorità e
la coda relativa.

Con la definizione di queste tre strutture siamo
riusciti a implementare un array di N elementi ognuno dei quali contiene una
coda oltre ad altre informazioni sopra descritte. Quindi si è partizionato
l’insieme dei processi in fasce di priorità.

3. ORGANIZZAZIONE DEL PROGRAMMA

Nell’approccio
a questo problema abbiamo cercato di tenere in particolare considerazione la
modularità e flessibilità del progetto adottando quindi una metodologia di
programmazione Top-Down. Inizieremo quindi a documentare l’organizzazione del
nostro programma con lo stesso metodo.

Innanzitutto,
come ovvio, è basilare l’utilizzo di
una funzione di coordinazione, praticamente un main, che faccia da
collante tra i vari moduli del progetto. Sotto a questo che definiamo il
“livello più alto”, ci dovrà essere un insieme di procedure necessarie oltre
che alla inizializzazione dei file di output anche e soprattutto alla
simulazione dello scheduling stesso, quindi creazione e eliminazione dei
processi, e infine uno strato ultimo che si ponga a stretto contatto con le
strutture dati utilizzate e che quindi si dedichi alla gestione ottimale di
tali dati.

Abbiamo quindi fatto in
modo tale che la funzione di main, scheduling.c, utilizzando le librerie da noi appositamente create, heap.h,
per l’utilizzazione delle strutture dati definite precedentemente, e, simulazione.h,
per simulare l’azione di scheduling del nostro programma, potesse assolvere al
compito affidatole. Per ottenere tale risultato è ovvio che debbano essere intense le relazioni tra le function di gestione degli heap e le
function di simulazione vera e propria dello scheduling richiamate dalla main
function.

4. PROCEDURE DA SVILUPPARE

A questo punto,
individuate le linee guida e quindi i moduli da implementare, ci avventuriamo
più nello specifico di tali implementazioni. A differenza della precedente,
procediamo in una documentazione per così dire “Bottom-Up”.

HEAP.H

Come abbiamo già più
volte detto questa libreria contiene tutte quelle function che sono
indispensabili per manipolare nel migliore dei modi le strutture dati
appositamente create.

Per gestire le code con
priorità c’è bisogno di alcune basilari routine di gestione degli heap; quindi,
oltre alle semplici function per la determinazione del padre, figlio sinistro o
destro di un nodo, c’è bisogno della function heapify(
)
per il mantenimento della proprietà di ordinamento parziale degli heap
che in questo caso riguarda la priorità degli heap. A questa function verrà
passato in input oltre al puntatore all’heap, la dimensione di quest’ultimo e
l’indice dell’elemento da cui partire
per ripristinare la proprietà. Il tempo di esecuzione di heapify( ) è T(n) = O(log n).

heapify( )

VAR:
l,r,maggiore: integer /*dichiarazione degli indici*/

l =
sinistro(i); /*conserviamo il figlio sinistro dell’elemento i*/

r =
destro(i); /*conserviamo il figlio destro dell’elemento i*/


/*verifichiamo il maggiore tra i e l*/

if ((l <= heapsize) e (vettore[l].key > vettore[i].key))

then


maggiore = l

else


maggiore = i


endif


/*verifichiamo il maggiore tra maggiore e r*/

if ((r <= heapsize) e (vettore[r].key>vettore[maggiore].key))

then

maggiore = r;


endif

if
(maggiore != i) /*se abbiamo trovato un elemento maggiore diverso da i…*/


then
/*…quindi esisteva la violazione!*/


scambia(vettore[i].key,vettore[maggiore].key);


scambia(vettore[i].pid,vettore[maggiore].pid);


scambia(vettore[i].time,vettore[maggiore].time);


heapify( );
/*chiamata ricorsiva per verificare…*/


endif
/*…l’esistenza di una nuova violazione.*/

Una volta definite
queste funzioni essenziali per il funzionamento degli heap, definiamo le
seguenti tre funzioni indispensabili per il trattamento delle code con
priorità:

insert( ) serve a inserire un elemento passatogli in
input nella coda anch’essa passata in input. Restituirà un intero per segnalare
il completamento con successo dell’operazione. Per far questo prima espande lo
heap inserendo una nuova foglia e poi percorre un cammino da questa foglia
verso la radice per trovare il posto appropriato al nuovo elemento. Poiché la
lunghezza del cammino tra la nuova foglia e la radice è O(log n), la
complessità computazionale di tale algoritmo risulta essere T(n)=O(log n).

insert(
)

VAR i: integer

if (heap->heapsize >=
(heap->allocato * LUNGHEAP)) /*se l’heap è pieno*/

then

allunga(heap); /*chiama
la procedura pe allunghare l’array dell’heap*/

endif

heap->heapsize=
heap->heapsize+1; /*incrementa la dimensione dell’heap*/

i = heap->heapsize;
/*salva la lunghezza nell’indice manipolarlo*/

/*questo ciclo trova la
posizione giusta al nuovo elemento.*/

/*metre l’ipotetico padre
dell’elemento i è minore del nuovo elemento*/

while
((i>0) e (heap->vett[padre(i)].key < key))

do

/*sposta il
padre di una posizione*/

heap->vett[i].key = heap->vett[padre(i)].key;

heap->vett[i].pid =
heap->vett[padre(i)].pid;

heap->vett[i].time = heap->vett[padre(i)].time;

i = padre(i); /*prepara l’indice a una
nuova iterazione*/

endwhile

heap->vett[i].pid = pid;
/*registra il pid del nuovo elemento*/

heap->vett[i].key = key;
/*registra la key del nuovo elemento*/

heap->vett[i].time = time;
/*registra il time del nuovo elemento*/

return 0; /*ritorna un indicatore di
successo*/

popmax( ) è una funzione necessaria a rimuovere e
restituire il processo dell’insieme con la più alta priorità. Gli viene passato
l’heap e restituisce le informazioni e quindi il pid relativo al processo di
massima priorità estratto dalla coda d’appartenenza e un intero per segnalare
un eventuale errore. Nel suo lavoro utilizza heapify() vista in precedenza per
ripristinare l’eventuale violazione della proprietà dell’heap. Inoltre grazie
alle funzione accorcia() rilascia la memoria eventualmente allocata a una coda
vuota. Il tempo di esecuzione è T(n)= O(log n).

popmax()


pid = heap->vett[0].pid; /*salva il valore da sovrascrivere*/


key = heap->vett[0].key; /*salva il valore da sovrascrivere*/


time = heap->vett[0].time; /*salva il valore da sovrascrivere*/


if (heap->heapsize > -1) /*se l’heap ha almeno un elemento*/


then

heap->vett[0].key =
heap->vett[heap->heapsize].key; /*sovrascrive key*/

heap->vett[0].pid =
heap->vett[heap->heapsize].pid; /*sovrascrive pid*/

heap->vett[0].time =
heap->vett[heap->heapsize].time; /*sovrascrive time*/

heap->heapsize–;
/*accorcia la lunghezza dell’heap*/

/*a questo punto heap->vett[0] può violare
le proprietà di heap*/

heapify( ); /*elimina l’eventuale violazione*/

return 1; /*ritorna un indicatore di
successo*/


else /*l’heap è privo di elementi…*/

if (heap->allocato != 1) /*…ed ha piu’ di 1
indice di memoria allocato*/

then

accorcia(); /*chiama la funzione
per accorciare l’heap*/

endif

return 0; /*ritorna un indicatore di errore*/


endif

Queste due funzioni
appena viste sono quelle più corpose dell’header ma ci sono anche altre
funzioni allo stesso modo importanti: azzeraheap(
)
, funzione che dato in input un heap lo inizializza all’uso e nel caso
contenesse dei dati precedentemente, essi verranno cancellati; lunghezza(), dato in input un heap
ritorna il numero di elementi in esso contenuti; accorcia(
)
, dato in input un heap, accorcia l’area di memoria allocata per
inserire elementi nell’array; allunga( ), dato in input un heap,
allunga l’area di memoria allocata per inserire elementi. Queste ultime due
funzioni, allunga() e accorcia(), sono funzioni interne al programma che
rappresentano la nostra soluzione alla richiesta di dinamicità del problema in
quanto permettono l’estensione o il ridimensionamento della memoria allocata a
code che in tal modo possono anche essere di dimensioni diverse visto che lo
schedulamento eseguirà prima le fasce con maggiore priorità che risulteranno
quindi spesso vuote.

SIMULAZIONE.H

Anche in questo caso
ricordiamo, anche rischio di essere ripetitivi, che questo modulo deve
effettuare una simulazione del ciclo di scheduling, ossia del problema che
abbiamo rappresentato in modo figurato precedentemente. Quindi compiti
essenziali sono la creazione dei processi, estrazione di essi e successiva
creazione dell’output, ossia scrittura dei files.

Logicamente iniziamo
dalla creazione degli n processi:

per ciascun processo da
creare si dovrà cercare la coda di appartenenza in base alla key, generata in
modo casuale, attribuire a questo processo un pid unico che sarà calcolato in
modo che la prima cifra è data dalla coda di appartenenza e le seconde due dal
numero di processi già presenti nella coda (ad esempio il processo con key=789
sarà posto nella coda 7 e ipotizzando che in tale coda siano già presenti 3
processi avrà pid=703); infine scriverà queste informazioni più il time di
creazione nel file logprocessi.txt. Segnalerà inoltre il successo di queste
operazioni restituendo un intero di valore 1. Tale funzione sarà implementata
da una funzione inserisci() il
cui codice è descritto sotto in pseudocodice:

inserisci()

VAR: i,pid,key;: integer

key = random tra 1 e MP;
/*genera un numero casuale da 0 a MP che indica la priorità*/

ciclo di ricerca della
coda in cui inserire il processo in questione

calcolo del pid

insert(); /*inserisce il
processo nella coda*/

apertura file in cui
scrivere

return 1; /*ritorna un
indicatore di successo*/

crea ( )

var: i, totprocessi, time:
integer

for i=1,…,n do

inserisci i processi e
scrivi nel file processi

totprocessi = totprocessi + 1

/*incrementa il numero di processi*/

time = time +1 /*incrementa
il tempo*/

done

Ovviamente la funzione
appena vista inserisce e definisce un solo processo per volta, servirà quindi
necessariamente una funzione crea(),
a cui viene passato il numero di processi da creare e che quindi richiama
inserisci ciclicamente. Inoltre, al termine di ogni chiamata fatta ad inserisci
dovrà incrementare i contatori totprocessi, indicante quanti processi sono
stati creati fino a quell’istante e appunto time.

Abbiamo visto la
creazione, ora passiamo alla estrazione. Partiamo sempre dal caso più semplice
di estrazione e quindi esecuzione del singolo processo. Anche in questo caso si
dovrà innanzitutto calcolare quale sia la coda di appartenenza del processo,
poi si dovrà provvedere alla sua estrazione utilizzando la funzione popmax()
dell’header heap.h, e infine scriverne le relative informazioni nel file
ritornando 1 se tutte queste operazioni sono state terminate con successo.

elimina( )

VAR: pid,key,oldtime,i:
integer;

cerca la coda con priorità più alta dove ci sia almeno un
processo

/*non ci sono più processi nella coda…*/

if (non ci sono più processi)

then

stampa nel file ERRORE

return 0 /* ritorno errore /*

else

/*elimina il processo più grande dell’i-esima
coda*/

popmax();

scrivi i dati nel file

return 1; /*ritorna un indicatore di
successo*/

Anche in questo caso
tale funzione dovrà essere iterato per il numero di processi da creare, n
diverso però stavolta da quel n che prima indicava i processi da creare.

estrazione( )

int i;

for i=1,..,n do /*estrae n processi*/

if (elimina(processi,fileprocessi))

then

totprocessi = totprocessi -1 /*decrementa il numero di processi*/

time = time + 1; /*incrementa il
tempo*/

done

Infine per la corretta
simulazione del nostro programma sono presenti le semplici ma non per questo
meno importanti funzioni inizializzafile(
)
, che riceve in input il descrittore del file logprocessi.txt
preparando quest’ultimo a successive scritture, e outputfilestato( ), a cui saranno passati il puntatore ai processi e l’intero time in
modo che al termine di ogni ciclo sarà descritto lo stato delle code nel file utilizzando la funzione scrivilog(), che stampa tutti gli
elementi della coda nel file.

outputfilestato( )

int i;

for i=1,..,N /*per tutte le N code*/

do


/*stampa su file lo stato dell’i-esima coda*/


scrivilog( )

done

SCHEDULER.C

Al livello più alto ci
sarà un main a cui sarà affidato il compito di coordinare al meglio le funzioni
delle due librerie descritte finora e gestire l’eventuale iterazione con
l’utente. Infatti, dopo aver inizializzato le code e i file necessari, crea un
numero random n di processi da creare, chiama in esecuzione la funzione crea(),

main()

VAR: processi[N]: struct tipoprocesso
/*dichiare il vettore che contine N code*/

VAR: i,n,time,totprocessi: integer

VAR: fileprocessi: pointer to file

for i=1,..,N do /*per tutte le N code*/

azzeraheap( )
/*inizializza la coda*/

processi[i].pkey = (i+1)
* (MP/N) /*stabilisce la priorità per la coda*/

done

inizializzafile( ); /*inizializza il
file per i processi*/

azzeralog(); /*inizializza il
log-file per l’header heap.h*/

repeat

/*creazione del numero n
dei processi che si devono creare */

n = rand() %
(MAXPROCESSI-totprocessi+1);


crea(processi,n,&time,&totprocessi,fileprocessi);

/*creazione del numero n
dei processi che si devono creare */

n = rand() %
(totprocessi+1); /*il nuovo n sarà <= del vecchio n*/


estrazione(processi,n,&time,&totprocessi,fileprocessi);

outputfilestato(processi,time);



until (time < MAXTEMPO);

poi la funzione
estrazione() su n diverso dal precedente e infine outputfilestato() per
generare l’output su file. Questo comportamento viene effettuato finchè
time<MAXTIME, ossia finchè il contatore temporale non raggiunga il valore
stabilito dalla costante MAXTIME.

Tutti i moduli e
quindi le implementazioni in linguaggio C sono riscontrabili nel paragrafo
dedicato per l’appunto al codice sorgente.

CODICE SORGENTE

heap.h

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/******************************************************************************
*                             HEAP HEADER FILE                                *
*                funzioni per manipolare il dato astratto heap                /
 
/******************************************************************************
*   2∞ Esercizio di Laboratorio di Algoritmi e Strutture Dati mod. A gr.2     *
*                                                                             *
*                           S C H E D U L E R                                 *
*                                                                             *
*  Realizzato da:                                                             *
*  Divisato Antonio 50/358                                                    *
*  Ferrara Francesco Saverio 566/811                                          *
*  Liguori Vincenzo 408/301                                                   *
*                                                                             *
******************************************************************************/
 
/***********************   DEFINIZIONE STRUTTURA DATI  ***********************/
 
#define LUNGHEAP 8 /*lunghezza massima dell'heap*/
#define FILESTATO "logstato.txt" /*nome del file usato*/
 
typedef struct nodoheap { /*struttura di un singolo elemento dell'array*/
 int pid;   /*Process ID*/
 int key;   /*chiave che indica la priorit‡*/
 int time; /*tempo di creazione*/
} tipoelementoheap;
 
typedef struct heap{ /*struttura di un heap*/
 tipoelementoheap *vett; /*vettore contenente gli elementi*/
 int heapsize; /*intero contenente la lunghezza dell'heap*/
 int allocato; /*indice della memoria allocata per l'heap*/
} tipoheap;
 
/********************   FUNZIONI DI GESTIONE DELL'HEAP   *********************/
 
void azzeraheap(tipoheap *heap);
/******************************************************************************
* Dato in input un heap lo prepara all'uso. Se l'heap conteneva gi‡ dei dati  *
* essi saranno persi!!!                                                       *
******************************************************************************/
 
int lunghezza(tipoheap *heap);
/******************************************************************************
* Dato in input un heap ritorna il numero di elementi in esso contenuti.      *
******************************************************************************/
 
int popmax(tipoheap *heap, int *pid,int *key, int *time);
/******************************************************************************
* Dato in input un heap ritorna il valore dell'elemento pi˘ grande e lo       *
* elimina dall'heap. Questa funzione ritorna l'heap senza violazioni.         *
******************************************************************************/
 
int insert(tipoheap *heap,int pid,int key,int time);
/******************************************************************************
* Dato in input un heap e i due valori di un elemento, la funzione inserisce  *
* l'elemento nell'heap. Questa funzione ritorna l'heap senza violazioni.      *
******************************************************************************/
 
void accorcia(tipoheap *heap);
/******************************************************************************
* Dato in input un heap, accorcia l'area di memoria allocata per inserire     *
* elementi nell'array.                                                        *
******************************************************************************/
 
void allunga(tipoheap *heap);
/******************************************************************************
* Dato in input un heap, allunga l'area di memoria allocata per inserire      *
* elementi nell'array.                                                        *
******************************************************************************/
 
/*********************  IMPLEMENTAZIONE DELLE FUNZIONI  **********************/
 
int sinistro(int i)
{
 return 2*i+1; /*ritorna il figlio sinistro del nodo i*/
}
 
int destro(int i)
{
 return 2*i+2; /*ritorna il figlio destro del nodo i*/
}
 
int padre(int i)
{
 return (int)((i-1)/2); /*ritorna il padre del nodo i*/
}
 
void scambia(int *n1,int *n2)
{
 int temp;
 
 /*semplice procedura di scambio di due interi*/
 temp = *n1;
 *n1 = *n2;
 *n2 = temp;
}
 
void heapify(tipoelementoheap *vettore, int i,int heapsize)
{
 int l,r,maggiore; /*dichiarazione degli indici*/
 
 l = sinistro(i); /*conserviamo il figlio sinistro dell'elemento i*/
 r = destro(i); /*conserviamo il figlio destro dell'elemento i*/
 
 /*verifichiamo il maggiore tra i e l*/
 if ((l <= heapsize) && (vettore[l].key > vettore[i].key))
 {
 maggiore = l;
 }
 else
 {
 maggiore = i;
 }
 
 /*verifichiamo il maggiore tra maggiore e r*/
 if ((r <= heapsize) && (vettore[r].key>vettore[maggiore].key))
 {
 maggiore = r;
 }
 if (maggiore != i) /*se abbiamo trovato un elemento maggiore diverso da i...*/
 { /*...quindi esisteva la violazione!*/
 scambia(&vettore[i].key,&vettore[maggiore].key);
 scambia(&vettore[i].pid,&vettore[maggiore].pid);
 scambia(&vettore[i].time,&vettore[maggiore].time);
 heapify(vettore,maggiore,heapsize); /*chiamata ricorsiva per verificare...*/
 } /*...l'esistenza di una nuova violazione.*/
}
 
void azzeraheap(tipoheap *heap)
{
 heap->heapsize = -1; /*pone la lunghezza dell'heap al valore -1*/
 heap->allocato = 1; /*indica che sono state allocate 1*LUNGHEAP elementi*/
 
 /*alloca la memoria*/
 heap->vett = (tipoelementoheap *) malloc(sizeof(tipoelementoheap)*LUNGHEAP);
}
 
int lunghezza(tipoheap *heap)
{
 return heap->heapsize+1; /*restituisce il numero di elementi dell'heap*/
}
 
void accorcia(tipoheap *heap)
{
 /*siccome il riallocamento di un vettore impiega molto tempo, imponiamo che
 * esso venga accorciato solo quando Ë completamente vuoto. Per questo motivo
 * non c'Ë il controllo per eventuali elementi presenti nel vecchio vettore*/
 
 heap->allocato = 1; /*riduce al minimo l'idice di lunghezza*/
 free(heap->vett); /*libera la memoria*/
 
 /*alloca la nuova memoria*/
 heap->vett = (tipoelementoheap *) malloc(sizeof(tipoelementoheap)*LUNGHEAP);
}
 
int popmax(tipoheap *heap, int *pid, int *key, int *time)
{
 *pid = heap->vett[0].pid; /*salva il valore da sovrascrivere*/
 *key = heap->vett[0].key; /*salva il valore da sovrascrivere*/
 *time = heap->vett[0].time; /*salva il valore da sovrascrivere*/
 
 if (heap->heapsize > -1) /*se l'heap ha almeno un elemento*/
 {
 heap->vett[0].key = heap->vett[heap->heapsize].key;   /*sovrascrive key*/
 heap->vett[0].pid = heap->vett[heap->heapsize].pid;   /*sovrascrive pid*/
 heap->vett[0].time = heap->vett[heap->heapsize].time; /*sovrascrive time*/
 heap->heapsize--; /*accorcia la lunghezza dell'heap*/
 
 /*a questo punto heap->vett[0] puÚ violare le propriet‡ di heap*/
 heapify(heap->vett,0,heap->heapsize); /*elimina l'eventuale violazione*/
 
 return 1; /*ritorna un indicatore di successo*/
 }
 else /*l'heap Ë privo di elementi...*/
 {
 if (heap->allocato != 1) /*...ed ha piu' di 1 indice di memoria allocato*/
 {
 accorcia(heap); /*chiama la funzione per accorciare l'heap*/
 }
 return 0; /*ritorna un indicatore di errore*/
 }
}
 
void allunga(tipoheap *heap)
{
 int i;
 tipoelementoheap *temp; /*dichiara un array d'appoggio*/
 
 temp = (tipoelementoheap *) malloc(sizeof(tipoelementoheap)*((heap->allocato)+1)*LUNGHEAP);
 for(i=0 ; i <= heap->heapsize ; i++) /*salva tutti gli elementi*/
 {
 temp[i].key = heap->vett[i].key;
 temp[i].pid = heap->vett[i].pid;
 temp[i].time = heap->vett[i].time;
 }
 (heap->allocato)++; /*aumenta l'indice di allocazione*/
 free(heap->vett); /*libera la memoria*/
 heap->vett = temp; /*registra il nuovo vettore*/
}
 
int insert(tipoheap *heap,int pid,int key,int time)
{
 int i;
 
 if (heap->heapsize >= (heap->allocato * LUNGHEAP)) /*se l'heap Ë pieno*/
 {
 allunga(heap); /*chiama la procedura pe allunghare l'array dell'heap*/
 }
 heap->heapsize++; /*incrementa la dimensione dell'heap*/
 i = heap->heapsize; /*salva la lunghezza nell'indice manipolarlo*/
 
 /*questo ciclo trova la posizione giusta al nuovo elemento.*/
 /*metre l'ipotetico padre dell'elemento i Ë minore del nuovo elemento*/
 while ((i>0) && (heap->vett[padre(i)].key < key))
 {
 /*sposta il padre di una posizione*/
 heap->vett[i].key = heap->vett[padre(i)].key;
 heap->vett[i].pid = heap->vett[padre(i)].pid;
 heap->vett[i].time = heap->vett[padre(i)].time;
 i = padre(i); /*prepara l'indice a una nuova iterazione*/
 }
 heap->vett[i].pid = pid; /*registra il pid del nuovo elemento*/
 heap->vett[i].key = key; /*registra la key del nuovo elemento*/
 heap->vett[i].time = time; /*registra il time del nuovo elemento*/
 
 return 0; /*ritorna un indicatore di successo*/
}

simulazione.h

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/******************************************************************************
*                        SIMULAZIONE HEADER FILE                              *
*                funzioni per l'inizializzazione dei file e                   *
*                    la simulazione dello scheduling                          /
 
/******************************************************************************
*   2∞ Esercizio di Laboratorio di Algoritmi e Strutture Dati mod. A gr.2     *
*                                                                             *
*                           S C H E D U L E R                                 *
*                                                                             *
*  Realizzato da:                                                             *
*  Divisato Antonio 50/358                                                    *
*  Ferrara Francesco Saverio 566/811                                          *
*  Liguori Vincenzo 408/301                                                   *
*                                                                             *      
******************************************************************************/
 
/******************* DEFINIZIONE COSTANTI E STRUTTURE DATI *******************/
 
#define N 10
#define MP 1000
#define MAXPROCESSI 50
#define MAXTEMPO 350
#define FILEPROCESSI "logprocessi.txt"
 
typedef struct tipoprocesso{/*struttura della fascia di processi dell'insieme*/
 tipoheap coda; /*coda dei processi*/
 int pkey;      /*indice che indica la massima priorit‡ della coda*/
}tipoprocesso;
 
/************************ PROTOTIPI DELLE FUNZIONI ***************************/
 
void inizializzafile(FILE *fileprocessi);
/******************************************************************************
*  funzione che inizializza in write-mode il file datole in input per poter   *
*  effettuare successivi append                                               *
******************************************************************************/
 
void crea(tipoprocesso*processi,int n,int *time,int *totprocessi,FILE*fileprocessi);
/******************************************************************************
*  questa funzione crea un numero n datole in input di processi; per fare ciÚ * 
*  si avvale ciclicamente della funzione  inserisci() a cui passa il          *      
*  puntatore al file, alla coda dei processi e l'istante time di creazione.   *
*  Inoltre incrementa il numero totale di processi creati e il contatore      *
*  time.                                                                      *
******************************************************************************/
 
int inserisci(tipoprocesso *processi,int *time,FILE *fileprocessi);
/******************************************************************************
*  questa funzione crea un numero n datole in input di processi; per fare ciÚ *
*  si avvale ciclicamente della funzione  inserisci() a cui passa il          *
*  puntatore al file, alla coda dei processi e l'istante time di creazione    *                                                                             *
******************************************************************************/
 
void estrazione(tipoprocesso*processi,int n,int *time, int *totprocessi,FILE *fileprocessi);
/******************************************************************************
*  funzione che al cotrario di crea() estrae gli n processi dalle code        *
*  utilizzando ciclicamente la funzione elimina a cui passa puntatore al file,*
*  alla coda dei processi e l'istante time di creazione.                      *   
*  Inoltre incrementa il numero totale di processi creati e il contatore      *
*  time.                                                                      *
******************************************************************************/
 
int elimina(tipoprocesso *processi,FILE *fileprocessi);
/******************************************************************************
*  funzione che dato in input il puntatore al file e quello alle code Ë in    *
*  grado di riconoscere da quale coda andare ad estrarre e quindi terminare   *
*  ogni processo. Scrive inoltre nel file lo stato delle code e restituisce   *
*  un intero che indica 1 se l'operazione ha avuto successo, 0 in caso        *
*  contrario                                                                  *
******************************************************************************/
 
void outputfilestato(tipoprocesso *processi,int time);
/******************************************************************************
*  questa funzione si occupa dell'output del programma, ossia della stampa    * 
*  della situazione delle N code al tempo time                                *
******************************************************************************/
 
void azzeralog();
/******************************************************************************
* Inizializza il file dei log.                                                *
******************************************************************************/
 
int scrivilog(tipoheap *heap,int num,int time);
/******************************************************************************
* stampa tutti dli elementi della coda nel file                               *
******************************************************************************/
 
/******************** IMPLEMENTAZIONE DELLE FUNZIONI *************************/
 
void inizializzafile(FILE *fileprocessi) /*procedura per inizializzare il file*/
{
 fileprocessi = fopen(FILEPROCESSI,"w"); /*apre il file in write-mode*/
 close(fileprocessi); /*chiude il file*/
 /*adesso il file esiste su disco ed Ë vuoto, cioË preparato per aggiungere
 * elementi in append-mode*/
}
 
int inserisci(tipoprocesso *processi,int *time,FILE *fileprocessi)
{
 int i,pid,key;
 
 key = rand() % MP+1; /*genera un numero casuale da 0 a MP che indica la priorit‡*/
 
 for (i=0 ; key > processi[i].pkey ; i++); /*cerca la coda adatta per la priorit‡ key*/
 
 /*raccogli informazioni per dare un pid univoco al processo*/
 pid = lunghezza(&processi[i].coda);
 pid = pid + (i*100);
 
 insert(&processi[i].coda,pid,key,*time); /*inserisce il processo*/
 
 fileprocessi = fopen(FILEPROCESSI,"a"); /*apre il file*/
 /*effettua la registrazione del processo inserito*/
 fprintf(fileprocessi,"\t%4d\t\t%4d\t\t%4d\t\t%4d\n\n",pid,key,i+1,*time);
 close(fileprocessi); /*chiude il file*/
 
 return 1; /*ritorna un indicatore di successo*/
}
 
void crea(tipoprocesso *processi,int n, int *time, int *totprocessi,FILE *fileprocessi)
{
 int i;
 
 for (i=0 ; i<n ; i++) /*inserisce n processi nella coda*/
 {
 if (inserisci(processi,time,fileprocessi))
 {
 (*totprocessi)++; /*incrementa il numero di processi*/
 (*time)++;        /*incrementa il tempo*/
 }
 }
}
 
int elimina(tipoprocesso *processi,FILE *fileprocessi)
{
 int pid,key,oldtime,i;
 
 /*cerca la coda con priorit‡ pi˘ alta dove ci sia almeno un processo*/
 for (i=N-1 ; (i>=0) && (lunghezza(&processi[i].coda) == 0)  ; i--);
 
 /*non ci sono pi˘ processi nella coda...*/
 if (i == -1) /*...si prevede che questa situazione non si verifichi mai*/
 {
 fileprocessi = fopen(FILEPROCESSI,"a"); /*apre il file*/
 /*scrive un indicatore di errore*/
 fprintf(fileprocessi,"\tNaN\t\tNaN\t\tNaN\t\tNaN\n\n");
 close(fileprocessi); /*chiude il file*/
 return 0; /*ritorna un indicatore di errore*/
 }
 else /*ci sono processi da eliminare*/
 {
 /*elimina il processo pi˘ grande dell'i-esima coda*/
 popmax(&processi[i].coda, &pid, &key, &oldtime);
 
 fileprocessi = fopen(FILEPROCESSI,"a"); /*apre il file*/
 fprintf(fileprocessi,"\t%4d\t\t%4d\t\t%4d\t\t%4d\n\n",pid,key,i+1,oldtime);
 close(fileprocessi); /*chiude il file*/
 return 1; /*ritorna un indicatore di successo*/   
 }
}
 
void estrazione(tipoprocesso *processi,int n, int *time, int *totprocessi,FILE *fileprocessi)
{
 int i;
 
 for (i=0 ; i<n ; i++) /*estrae n processi*/
 {
 if (elimina(processi,fileprocessi))
 {
 (*totprocessi)--; /*decrementa il numero di processi*/
 (*time)++; /*incrementa il tempo*/
 }
 }
}
 
void outputfilestato(tipoprocesso *processi,int time)
{
 int i;
 
 for(i=0; i<N ; i++) /*per tutte le N code*/
 {
 /*stampa su file lo stato dell'i-esima coda*/
 scrivilog(&processi[i].coda,i+1,time); 
 }
}
 
void azzeralog()
{
 FILE *filestato; /*dichiarazione puntatore a file*/
 filestato = fopen(FILESTATO,"w"); /*apre il file in write-mode*/
 close(filestato); /*chiude il file*/
 /*adesso il file esiste su disco ed Ë vuoto, cioË preparato per aggiungere
 * elementi in append-mode*/
}
 
int scrivilog(tipoheap *heap,int num,int time)
{
 FILE *filestato; /*dichiarazione del file*/
 int i;
 
 filestato = fopen(FILESTATO,"a"); /*apre il file in append-mode*/
 
 if (filestato == NULL) /*se Ë stato ritirnato un indice di errore*/
 {
 printf("ERRORE: file %s non inizializzato!\n",FILESTATO);
 return 1; /*ritorna un indicatore di errore*/
 }
 else /*non Ë stato ritornato nessun indice di errore*/
 {
 /*scrive sul file*/
 fprintf(filestato,"STATO AL TEMPO %d DELLA %d∞ CODA:\n",time,num);
 for (i=0 ; i <= heap->heapsize ; i++) /*stampa tutti gli elementi della coda*/
 {
 fprintf(filestato,"%3d) Pid: %4d   key: %4d   Time: %4d\n",i+1,heap->vett[i].pid,heap->vett[i].key,heap->vett[i].time);
 }
 fprintf(filestato,"In totale i processi sono %d\n\n\n",(heap->heapsize)+1);
 
 close(filestato); /*chiudeil file*/
 return 0; /*restituisce un indicatore di successo*/
 }
}

scheduler.c

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <stdio.h>
#include <stdlib.h>
#include "heap.h"
#include "simulazione.h"
/******************************************************************************
*   2∞ Esercizio di Laboratorio di Algoritmi e Strutture Dati mod. A gr.2     *
*                                                                             *
*                           S C H E D U L E R                                 *
*                                                                             *
*  Realizzato da:                                                             *
*  Divisato Antonio 50/358                                                    *
*  Ferrara Francesco Saverio 566/811                                          *
*  Liguori Vincenzo 408/301                                                   *
******************************************************************************/
 
main()
{
 tipoprocesso processi[N]; /*dichiare il vettore che contine N code*/
 int i,n;
 int time=0; /*dichiara ed inizializza il tempo*/
 int totprocessi=0; /*dichiara ed inizializza il numero di processi totale*/
 FILE *fileprocessi;
 
 for (i=0 ; i<N ; i++) /*per tutte le N code*/
 {
 azzeraheap(&processi[i].coda); /*inizializza la coda*/
 processi[i].pkey = (i+1) * ((int) MP/N); /*stabilisce la priorit‡ per la coda*/
 }
 inizializzafile(fileprocessi); /*inizializza il file per i processi*/
 azzeralog(); /*inizializza il log-file per l'header heap.h*/
 
 fileprocessi = fopen(FILEPROCESSI,"a"); /*apre il file in append-mode*/
 /*scrive un'intestazione*/
 fprintf(fileprocessi,"=-=-=-=-=-=-=-=-=-=-=-=[INIZIO SIMULAZIONE]=-=-=-=-=-=-=-=-=-=-=-=\n");
 close(fileprocessi); /*chiude il file*/
 
 do {
 
 fileprocessi = fopen(FILEPROCESSI,"a"); /*apre il file in append-mode*/
 fprintf(fileprocessi,"\n\nFASE DI CREAZIONE E INSERIMENTO DEI PROCESSI AL TEMPO %d\n",time);
 
 n = rand() % (MAXPROCESSI-totprocessi+1); 
 
 fprintf(fileprocessi,"E' stato generato un numero casuale di %d processi.\n\n",n);
 fprintf(fileprocessi,"Tabella dei processi inseriti:\n\n");
 fprintf(fileprocessi,"\tpid\t\tkey\t\tn∞coda\t\tTempo\n\n");
 close(fileprocessi); /*chiude il file*/
 
 crea(processi,n,&time,&totprocessi,fileprocessi);
 
 fileprocessi = fopen(FILEPROCESSI,"a"); /*apre il file in append-mode*/
 fprintf(fileprocessi,"\n\nFASE DI ESTRAZIONE DEI PROCESSI AL TEMPO %d\n",time);  
 
 n = rand() % (totprocessi+1); /*il nuovo n sar‡ <= del vecchio n*/
 
 fprintf(fileprocessi,"Sto procedendo all'estrazione di %d processi\n\n",n);
 fprintf(fileprocessi,"Tabella dei processi estratti:\n\n");
 fprintf(fileprocessi,"\tpid\t\tkey\t\tn∞coda\t\tTempo\n\n");
 close(fileprocessi); /*chiude il file*/
 
 estrazione(processi,n,&time,&totprocessi,fileprocessi);
 
 outputfilestato(processi,time);
 
 } while (time < MAXTEMPO);
 
 fileprocessi = fopen(FILEPROCESSI,"a"); /*apre il file in append-mode*/
 fprintf(fileprocessi,"\n=-=-=-=-=-=-=-=-=-=-=-=[FINE SIMULAZIONE]=-=-=-=-=-=-=-=-=-=-=-=\n\n");
 close(fileprocessi); /*chiude il file*/
 printf("La simulazione e' terminata, l'output si trova nei file:\n %s\n %s\n \n",FILEPROCESSI,FILESTATO);
 printf("Premere <INVIO> per uscire");
 
 getchar(); /*attende che l'utente prema INVIO*/
}

Scrivi un commento