home - inizio - precedente - successivo autore: Simone Sollena

Sistemi operativi

Gestione dei processi

Un processo può essere definito semplicemente come un programma in esecuzione. I primi sistemi operativi consentivano l'esecuzione di un solo programma per volta, questo aveva il completo controllo del sistema e di tutte le sue risorse. Un maggiore controllo sui programmi in esecuzione e sulle risorse da parte del sistema operativo rende possibile l'esecuzione di più programma concorrentemente, che prendono il nome di processi.
Un processo è l'unità di lavoro in un sistema, i processi si suddividono in processi del sistema operativo, che eseguono il codice di sistema, e processi utente, che eseguono il codice utente.
L'esecuzione di un processo avviene in modo sequenziale, ciò vuol dire che in qualsiasi momento può essere eseguita al massimo una sola istruzione di un processo.
Un programma non è un processo, infatti, mentre un programma, che è un file memorizzato su disco, è un'entità passiva, un processo è un'entità attiva, ed è costituito, oltre che dal codice di un programma in esecuzione, anche dall'attuale valore del program counter, dal contenuto dei registri della CPU e dalle variabili globali e temporanee del programma in esecuzione.

Ogni processo subisce continuamente un cambiamento di stato, i possibili stati di un processo sono:

Su ciascun processore un solo processo per volta può trovarsi in stato di running.
Quando un processo viene creato è nello stato New, passa allo stato Ready e quando ottiene la disponibilità della CPU passa in Running, in seguito ad un interrupt torna in Ready mentre durante l'attesa di un'operazione di I/O o di un evento passa in Waiting e poi in Ready. Quando l'esecuzione è terminata, da Running passa in Terminated.

Tutte le informazioni relative ai vari processi sono memorizzate nel PCB, Process Control Block, esso contiene:

Ogni nuovo processo creato viene messo in una coda di processi, detta ready queue; come detto precedentemente, ogni PCB contiene un puntatore al successivo processo in coda, l'intestazione della coda contiene i puntatori al primo ed all'ultimo PCB.
Avendo più processi in esecuzione contemporaneamente le risorse e i dispositivi di I/O vengono condivisi, un processo potrebbe richiedere l'utilizzo di un dispositivo di I/O mentre questo è occupato a servire un altro processo, per far fronte a questo problema vengono create diverse code, ogni dispositivo ha la propria coda.
La coda dei processi viene detta anche coda di scheduling, lo scheduler ha il compito di selezionare i processi per l'esecuzione.
Nei sistemi batch abbiamo uno scheduler a lungo termine, detto anche scheduler di job, ed uno scheduler a breve termine, detto scheduler di CPU, che ha i compito di selezionare i processi dalla ready queue e passarli alla CPU per l'esecuzione e viene eseguito almeno una volta ogni 100 millisecondi. Lo scheduler a lungo termine controlla invece il grado di multiprogrammazione, ossia il numero di processi in memoria e può essere richiamato per aggiungere un nuovo processo nella ready queue solo quando un altro processo ha terminato la sua esecuzione.
I processi possono essere I/O bound o CPU bound, un processo è detto I/O bound quando dedica la maggior parte del tempo ai dispositivi di I/O e non all'esecuzione di calcoli, è detto invece CPU bound quando impiega la maggior parte del tempo nell'utilizzo della CPU e poco per operazioni di I/O.
Se lo scheduler a lungo termine seleziona troppi processi CPU bound la CPU viene continuamente utilizzata mentre le code dei dispositivi di I/O restano vuote ed in attesa, se al contrario vengono selezionati troppi processi I/O bound la CPU resta a lungo senza far nulla. Per ottimizzare le prestazioni ed avere una velocizzazione del calcolo è necessario che lo scheduler a lungo termine selezioni una combinazione equilibrata di processi I/O bound e CPU bound.
I sistemi time sharing generalmente non hanno uno scheduler a lungo termine, ma può essere introdotto uno scheduler a medio termine, detto anche swapping, che permette di ridurre temporaneamente il livello di multiprogrammazione: tramite l'operazione di swap out viene tolto un processo dalla ready queue e salvato in una memoria di swap che generalmente è la memoria secondaria, e può essere reintrodotto in un secondo tempo nella ready queue, in memoria centrale, senza che la sua esecuzione debba essere ripresa da capo. Ciò può essere fatto quando il sistema richiede più memoria e questa è occupata dalla ready queue, o per ottimizzare il mix dei processi.
Il salvataggio dello stato di un processo vecchio ed il caricamento dello stato di un nuovo processo o di un processo precedentemente sospeso è chiamato context switch, quest'operazione richiede un certo tempo, detto overhead, perché durante questo tempo il sistema non svolge alcun lavoro utile.

Un processo, tramite la system_call fork può creare nuovi processi, il processo creante è detto padre mentre quelli creati vengono detti figli.
Ogni processo ha bisogno di accedere a delle risorse, fisiche e logiche, CPU, dispositivi di I/O, ecc. Le risorse allocate al processo figlio possono essere condivise con il processo padre o indipendenti ed assegnate dal sistema operativo. Quando un processo figlio viene avviato, i parametri di inizializzazione gli possono essere passati dal padre. Il padre infine può continuare la sua esecuzione concorrentemente con i figli o può attendere che i figli abbiano terminato l'esecuzione.
Generalmente un processo termina quando ha eseguito l'ultima istruzione, tramite la system_call exit a quetso punto richiede la sua cancellazione ed i valori di uscita vengono ritornati al processo padre. In alternativa, un processo figlio può terminare perché il padre ha richiamato la system_call abort, ciò può avvenire per uno dei tre motivi:

infatti, senza un processo padre, il sistema operativo non saprebbe a chi riportare i risultati delle attività di un processo figlio.

I processi possono essere indipendenti o cooperanti tra loro; si dicono indipendenti quando l'esecuzione non influenza altri processi e non può essere influenzata da altri processi, si dicono cooperanti quando la loro esecuzione può influenzare altri processi e può essere influenzata da altri processi. Nei processi cooperanti il risultato dell'esecuzione non è deterministico in quanto, poiché dipende anche da altri processi, uno stesso input non produce sempre uno stesso risultato.
La cooperazione tra processi permette la condivisione dei dati e accelerazione di calcolo, infatti, in certi casi, un task può essere eseguito più velocemente dividendolo in subtask da eseguire in parallelo su più processori.

Un thread è definito come l'unità base di utilizzo della CPU, è parte di un processo, i processi possono essere di tipo tradizionale (heavyweight), che possono essere considerati come task composti da un solo thread, o possono contenere più thread eseguiti concorrentemente tra loro. Un task è sempre composto da almeno un thread e un singolo thread può trovarsi in un solo task.
Più thread facenti parte di un processo condividono lo stesso codice, lo stesso spazio indirizzabile e le risorse del sistema. Grazie alla condivisione di questi elementi, per svolgere compiti simili l'utilizzo di più thread in un task piuttosto che di più processi permette risparmi sull'utilizzo delle risorse e sui tempi di esecuzione poiché la commutazione tra i diversi thread non richiede né il salvataggio di molti dati né il ricorso a sistema call, ed è quindi più veloce rispetto al context switching.