Sistemi operativi/Introduzione
Cosa sono i Sistemi Operativi
[modifica | modifica sorgente]Un sistema operativo è il programma che agisce da intermediario fra utente e macchina. Fornisce infatti all'utente vari strumenti, quale un ambiente per l'esecuzione di altri programmi e una visione astratta del hardware.
Evoluzione
[modifica | modifica sorgente]I sistemi operativi nascono e si sviluppano di pari passo con la complessità delle macchine su cui nascono. La prima generazione dei computer, attorno agli anni '50, non prevedeva un sistema operativo, bensì un operatore umano che gestisse tutti gli aspetti delle operazioni di controllo. Con la seconda generazione, a cavallo degli anni '60, le operazioni dovevano essere automatizzate. Ma è solo con la terza generazione che si assiste alla nascita dei veri e propri sistemi operativi moderni. Un sistema operativo può essere di vari tipi, cioè multi/monoutente e multi/monoprocesso. Storicamente, si tende a suddividere così i sistemi operativi:
- monoutente, monoprocesso: esempi MS DOS, Mac System 1-5
- monoutente, multiprocesso: esempi Windows 1.0, 2.0, 3.0, 3.1, 3.11, 95, 98, Me, Mac System 6-7, Mac OS 8
- "multiutente", multiprocesso: esempi Windows NT, 2000, XP, 2003, Mac OS 9
- multiutente, multiprocesso: esempi Unix, GNU/Linux, Mac OS X, BSD
Un sistema multiprocesso ha ovviamente un maggior numero di vincoli imposti su memoria e risorse, che un sistema monoprocesso. Infatti, mentre in origine il problema non si poneva (non esistendo nemmeno un sistema operativo), già con la seconda generazione di processori il problema delle risorse era di primissimo piano.
Famiglie di sistemi operativi
[modifica | modifica sorgente]I primi sistemi operativi erano, per loro natura, sistemi monolitici, data la bassissima necessità di uso di interfacce particolari, in quanto tutto veniva fatto apposta sull'hardware che si utilizzava. Erano semplicemente delle applicazioni che gestivano e monitoravano i processi che venivano eseguiti sulla macchina. Tutto il programma del sistema operativo veniva caricato in memoria e mandato in esecuzione sulla macchina.
Presto arrivò il paradigma di programmazione ad oggetti. Con esso arrivarono i cosiddetti sistemi modulari con i loro vantaggi e i loro svantaggi. Nei sistemi modulari il sistema operativo è composto da più programmi, che vengono caricati in memoria ed eseguiti in base alle necessità del momento.
Tra i componenti del sistema operativo è particolarmente importante la sua parte centrale, il kernel (o nucleo) il quale gestisce le risorse hw principali (CPU, RAM, clock, e spesso almeno un terminale utente). Fondamentalmente, nel kernel monolitico era necessario avere tutti i driver (i programmi che gestiscono le periferiche) caricati nel sistema, il che garantiva una grande velocità, ma nel contempo aumentava le risorse necessarie. Al contrario un kernel modulare è generalmente molto leggero, ma aumenta il suo tempo di accesso ai driver, i quali sono salvati in moduli esterni (solitamente su disco).
Esiste un'ulteriore famiglia di sistemi modulari, detti a microkernel, che godono di un'ulteriore leggerezza del processo. In questo caso anche alcune funzioni tipiche del kernel vengono eseguite da elementi esterni al kernel in senso stretto. Questo modello viene spesso associato al modello client-server. In effetti ogni servizio è garantito da una particolare applicazione.
È infine necessario notare un ulteriore paradigma: il modello a livelli gerarchici. Questo, realizzato nel sistema THE, riprende in alcuni aspetti l'idea dei layer tipica del mondo delle comunicazioni.
Un processo in un sistema operativo è un'applicazione caricata in memoria e pronta per essere eseguita. Un sistema operativo ha dunque il compito di gestire l'esecuzione dei vari processi, quindi, di assicurare ad ogni processo la disponibilità delle risorse richieste. La risorsa principale di cui ogni processo non può fare a meno è la cpu ( central process unit ), che è il cuore di un calcolatore. Nella gestione dei processi, dunque, il sistema operativo deve avere particolare cura nell'assegnare tale risorsa ai vari processi, per effettuare ciò è necessario che il sistema operativo abbia il cosiddetto scheduler dei processi. A esso è affidato il compito di schedulare i processi secondo un ordine preciso, in modo tale da minimizzare i tempi di attesa di ogni processo e massimizzare l'utilizzo della cpu. Sono state sviluppate diverse tecniche per implementare gli scheduler, le quali comunque si dividono in due grandi categorie che sono: scheduler preemptive e scheduler non preemptive. La differenza fra queste due categorie è data dal fatto che nel primo caso lo scheduler di sistema ha il diritto di togliere ad un processo in esecuzione sulla cpu tale risorsa per assegnarla ad un altro processo, nel secondo ciò non accade (e la CPU viene rilasciata quando il processo termina). Ovviamente il primo approccio risulta più proficuo in quanto, su pc con una sola cpu, è possibile simulare un sistema multiprogrammato (sistema nel quale più processi sono eseguiti in contemporanea ), e in tali sistemi è possibile aumentare il grado di multi programmazione. Come contro ha però, ovviamente, una complessità maggiore, infatti gestire i vari cambi di contesto fra processi risulta un'operazione estremamente complicata.
I moderni sistemi operativi sono solitamente sistemi che utilizzano schedulatori del tipo preemptive, assicurano infatti di poter avere più programmi in esecuzione virtualmente in contemporanea. Sono molte le tecniche per l'implementazione effettiva dello scheduler dei processi, le più importanti sono: FCFS, SJF, Scheduler per priorità, Scheduler con code a priorità multiple, round robin.
Il primo è il più semplice da implementare ma il meno efficace, infatti esso serve i processi in ordine di arrivo ( FIRST COME FIRST SERVED ), ciò può rendere possibile che un processo che impiega 1 millisecondo a terminare debba aspettare 100 millisecondi per essere eseguito poiché un processo x arrivato prima di esso sia in esecuzione, questa tecnica è, come si vede, utilizzata per schedulatori non preemptive. Una versione per schedulatori preemptive è la round robin, che assegna ad ogni processo un quanto di tempo, dopodiché allo scadere di tale periodo se il processo non ha terminato l'esecuzione viene posto in fondo alla coda per essere rieseguito dopo che tutti gli altri processi hanno utilizzato il loro quanto di tempo, quindi il tempo di attesa è dato dal quanto di tempo per il numero di processi presenti nella coda.
L'algoritmo SJF ( Shortest Job First ) cerca di fare una previsione sui tempi di esecuzione dei vari processi presenti nella coda dei processi pronti, esso, quindi, esegue il processo il quale ha un tempo previsto di esecuzione minore, tale algoritmo sarebbe la soluzione ottima in tale problema di ottimizzazione, ma nella realtà non è comunque possibile prevedere esattamente quanto un processo ci impieghi a terminare. Un problema legato a tali scheduler è dato dal fatto che se non si tiene in considerazione quanto un processo stia aspettando nella coda dei processi pronti si corre il rischio che processi con un tempo di esecuzione molto grande non siano mai eseguiti, perché nel sistema continuano ad arrivare processi con un tempo di esecuzione minore; una soluzione a tale problema è la possibilità di associare una priorità ai processi che aumenta con il passare del tempo, cosicché processi che aspettano da molto tempo abbiano una priorità maggiore e possano, dunque, essere eseguiti. Schedulatori per priorità schedulano i processi in base a quale priorità abbiano, ad esempio un processo di sistema ha sicuramente una priorità maggiore di un processo utente, quindi tali processi saranno eseguiti prima dei processi a bassa priorità, anche qui bisogna prestare attenzione da quanto tempo un processo sia in attesa che la cpu gli venga assegnata, quindi più passa il tempo e più la priorità di un processo deve crescere. Con tale tecnica abbinata agli scheduler preemptive è possibile togliere ad un processo in esecuzione la cpu nel momento in cui un processo con priorità minore viene aggiunto alla coda dei processi pronti.
L'ultima tecnica, scheduler con code a priorità multipla, utilizza anziché una coda a priorità più code, alle quali sono assegnate varie priorità. È possibile dunque operare nel seguente modo: i processi appena eseguiti passano nella prima coda, alla quale è assegnato una alta priorità, e quindi i suoi processi saranno eseguiti con maggiore frequenza, e ad ognuno di tali processi sarà assegnato un quantitativo di tempo molto breve, alla fine del quale se il processo non è terminato viene spostato nella coda con priorità immediatamente inferiore ad essa e con un quantitativo di tempo maggiore per ogni processo, e così via fino ad arrivare alla coda con priorità minima.