CAPITOLO 5:
MULTITASKING E MULTITHREADING








 

5.1: Multitasking
 

Il multitasking indica la possibilità di avere più processi che vengono eseguiti simultaneamente su un singolo sistema. Il sistema operativo deve gestire il processore e il tempo di esecuzione da assegnare ad ogni programma che deve essere eseguito.

Gli scopi si vogliono raggiungere con l’uso del multitasking sono:

Si hanno due modalità nella gestione di più task contemporaneamente: cooperative e preemptive multitasking.

Si parla di cooperative multitasking quando tutti i processi in esecuzione cooperano con il sistema operativo, condividendo il sistema e le sue risorse; ogni programma in esecuzione ha un controllo completo del sistema mentre è in esecuzione. Il sistema mette a disposizione una routine, che i processi possono chiamare periodicamente (nel momento in cui raggiungono un punto adeguato durante la loro esecuzione), che si occupa di controllare se ci sia qualche processo in attesa di poter ottenere l’uso della CPU: se tale controllo dà esito positivo, vengono controllate le priorità e il processo in attesa con più alta priorità viene mandato in esecuzione, fino al momento in cui anche questo non raggiunge un punto adeguato e si ha un nuovo cambiamento di contesto. Questo approccio richiede che tutti i programmi siano scritti in modo da chiamare frequentemente la routine per il cambio di contesto, requisito inaccettabile quando si devono effettuare grosse computazioni. Inoltre, nei sistemi che adottano un cooperative multitasking, può risultare molto difficile scrivere programmi ben formati per via delle enormi restrizioni che derivano dai momenti imposti dal sistema per i cambi di contesto.

Nel preemptive multitasking i programmi non rilasciano volontariamente il controllo del sistema, ma viene data loro l’impressione di avere un accesso senza interruzioni al processore ed un suo completo controllo. In un sistema di questo tipo si interrompe periodicamente un thread in esecuzione per controllare se ce ne siano altri in attesa: in caso affermativo, il sistema cambia automaticamente contesto. La scrittura dei programmi non è condizionata dalla scelta di momenti più adatti degli altri per i cambiamenti di contesto; se da un lato vengono rilasciate certe restrizioni, dall’altro ne vengono imposte delle altre per evitare problemi di inconsistenza dei dati nel caso in cui il programma venga interrotto durante l’aggiornamento di una struttura dati: si rende necessaria l’introduzione di meccanismi di locking delle risorse, in modo che soltanto il processo in esecuzione possa accedere alle risorse del sistema. Tali meccanismi devono essere il più trasparente possibile per l’utente.

In GEOS si adotta il secondo approccio, imponendo alle applicazioni il rispetto di alcune regole per il buon funzionamento del sistema; le applicazioni stesse, in gran parte sono isolate dall’ambiente multitasking.

 

5.2: Thread e multithreading
 

Un thread è una singola entità che viene eseguita nel sistema; può essere di due tipi:

Quando un thread event-driven viene creato, viene associato ad una classe per determinare i metodi da usare quando un messaggio è spedito direttamente al thread; in questo senso si può dire che un thread è l’istanza di una classe.

Si mantiene, anche, il concetto priorità, necessario per determinare quale thread mandare in esecuzione dopo un cambiamento di contesto; in particolare ogni thread ha:

L’uso di queste priorità avviene intelligentemente, evitando che queste permettano a qualche thread di usare la CPU più del loro quanto di tempo.

Ci sono due architetture per le applicazioni di GEOS:

Ogni applicazione può definire più thread per raggiungere vari scopi; un’applicazione con più di un thread può apparire straordinariamente veloce rispetto alle altre.

 

5.3: Semafori
 

È necessario un modo per evitare che due thread accedano simultaneamente alle stesse risorse di sistema: un meccanismo utile può essere quello che prevede l’uso di semafori.

Un semaforo è una struttura dati sulla quale è possibile effettuare tre operazioni di base:

Queste tre operazioni impediscono a tutti i thread di avere conflitti per l’accesso alle risorse condivise. Può essere utile pensare ad un semaforo come ad un flag che un programma può impostare per indicare che alcune risorse (quelle che sta usando) sono bloccate e non è concesso a nessun altro programma di accedervi.

Dal momento che un solo thread per volta può accedere alle risorse protette con il meccanismo di semafori, è importante osservare che il thread che richiede il lock su una risorsa sia responsabile del suo rilascio quando termina di utilizzarla.

Un problema strettamente interconnesso con la sincronizzazione degli accessi e la gestione del meccanismo di locking è quello dei deadlock: ossia situazioni per cui un thread A richiede il lock per una risorsa già bloccata dal thread B, il quale, a sua volta, richiede un lock per una risorsa bloccata da A. In questi casi il thread A interrompe la sua esecuzione in attesa che il thread B liberi una risorsa e il thread B interrompe la sua in attesa che il thread A liberi un’altra risorsa.
Per evitare queste situazioni, esistono delle regole che, se rispettate, garantiscono l’assenza di deadlock in un programma:

GEOS possiede parecchie risorse protette da semafori; dal momento che, però, i programmi delle applicazioni accedono a tali risorse solo attraverso le routine di libreria, il programmatore non ha necessità di essere a conoscenza di questi semafori, in quanto le operazione necessarie per la loro gestione sono effettuate da tali routine di libreria.