Ciao a tutti,
ultimamente in un altro thread ci stavamo inoltrando sul discorso Sistemi Operativi. Mi rendo conto che, a volte, quando si entra nello specifico e si iniziano ad usare termini per così dire "tecnici" non sempre tutti ne sono a conoscenza o non sempre tali termini sono universalmente noti nello stesso modo.
Ho così pensato, visto che sono sistemista oltre che programmatore, di fornire, per chi fosse interessato, un pò di spiegazioni sui sistemi operativi, sul loro funzionamento teorico e quindi le loro implementazioni pratiche, almeno le più note.
Non ho nessuna intenzione di dire che X è meglio di Y, ma i confronti nasceranno naturalmente, per gli esseri umani è praticamente impossibile non confrontare direttamente due casi pratici assegnando dei ruoli. Se non altro, alla fine, almeno avremo le idee più chiare e partiremo tutti dagli stessi presupposti.

Iniziamo col dare una definizione di S.O.: "Un sistema operativo è l'insieme di servizi atti a rendere più agevole la comunicazione dei software applicativi con l'hardware sottostante".
In pratica il compito di un sistema operativo è quello di occuparsi di gestire l'hardware della macchina permettendo al software applicativo di occuparsi di altre cose. Questo non significa necessariamente che il software applicativo non deve conoscere l'hardware della macchina, semplicemente che chi programma non deve riscrivere ad esempio la routine per leggere un file dal disco fisso in quanto c'è già chi la scritta per lui e quindi sarebbe sciocco non adoperarla.
E' altresì vero che, un S.O. (Sistema Operativo), si occupa di gestire le risorse fisiche decidendo come distribuirle in modo, per così dire, sicuro.
Questo discorso era essenzialmente valido all'inizio, quando i computer erano grossi come stanze e facevano centinaia di operazioni al secondo. Col tempo l'informatica è divenuta un fenomeno di massa (o globalizzato) e la miniaturizzazione ci ha messo lo zampino. Risultato? Centinaia di componenti HW che fanno tutti la stessa cosa, ma di marche differenti, che comunicano con gli altri dispositivi ognuno secondo un proprio stile (i cosiddetti "protocolli"). Nel frattempo, però, non sono nati centinaia di S.O. ma in genere ve ne sono pochi che devono far funzionare, più o meno, tutto.
Così al S.O. è stato addebitato anche il compito di "astrarre" l'hardware. Ovvero far si che il software applicativo non debba più impegnarsi nel conoscere il tipo di hardware su cui gira, ma se il S.O. mette a disposizione una funzione che svolge un determinato compito deve limitarsi a chiamarla, sarà compito poi di quest'ultima eseguire il suo lavoro "al meglio".
In questo modo il S.O. prende letterlamente il controllo della macchina cercando di nasconderla del tutto al software applicativo e così se prima si poteva comunque optare per accedere, anche in modo rischioso, all'hardware in maniera diretta, ma magari in modo più efficiente, ora non si può fare altrimenti che adoperare le vie regolari. Soprattutto in un ambito denominato multitasking e che vedremo più avanti.

I sistemi operativi si dividono un due grandi categorie: Monotasking e Multitasking.
Un sistema si definisce monotasking quando permette l'esecuzione di un solo software applicativo alla volta. Per poter eseguire un altro software applicativo bisogna quindi terminare prima il software corrente e poi eseguire il nuovo. Il più famoso dei sistemi operativi monotasking è stato l'MS-DOS. Oggi tale tipo di sistema operativo sopravvive esclusivamente in ambito industriale per i compiti "mission critical". Il vantaggio di questo tipo di sistema era dato dal fatto che l'applicativo si impossessava completamente dell'hardware e quindi poteva farci sopra il bello e cattivo tempo. Lo svantaggio è dovuto al fatto che se devo trasportare delle informazioni da un applicativo ad un altro devo prima fare il lavoro sul primo, salvarlo, chiudere l'applicativo, aprire il secondo applicativo, aprire il file e quindi continuare il lavoro.
Si definisce un S.O. multitasking quando è in grado di eseguire, apparentemente o fattivamente, più programmi applicativi "contemporaneamente". Il virgolettato è d'obbligo in quanto la risorsa principale di un computer è il processore (dove per processore si intende il singolo core) e questo è comunque in grado di eseguire un'operazione alla volta (lasciamo un attimo da parte i processori paralleli). Quindi se ho due processori (o due core) al massimo il mio multitasking reale è in grado di eseguire due programmi contemporaneamente. Ma allora cos'è il multitasking? Un processore è in grado di eseguire miliardi, ormai, di operazioni al secondo, come il nostro cervello del resto, ma i tempi di reazione del nostro organismo, infinitamente più lenti, perchè più complessi, rendono il PC più veloce, ovvero lo mettono in uno stato di attesa relativo al nostro input. Si è così pensato di sfruttare questi tempi di attesa congelando il programma che in quel momento è aspetta i nostri ordini, per farne eseguire un altro e risvegliare il primo solo quando noi "comodamente" decidevamo di inviare la tanto agognata informazione. Osservando ulteriormente la situazione si è constatato che, in ogni caso, il numero di operazioni che è in grado di eseguire il computer era comunque ancora troppo elevato per due soli programmi. Si è pensato, quindi, di suddividere il tempo in fette (o timeslice) e di assegnare ad ogni singolo programma (denominato più correttamente processo) la sua bella fetta di tempo.
Pertanto se un processore è in grado di eseguire cento milioni di operazioni a secondo ed io divido un secondo per dieci timeslice ho, in modo molto grossolano ovviamente, la possibilità di eseguire fino a dieci processi in sequenza, ognuno dei quali può eseguire dieci milioni di operazioni prima di passare il controllo al successivo. Visti i normali tempi di reazione di un essere umano, a quest'ultimo sembrerà che i programmi vengano eseguiti tutti contemporaneamente. In questo caso però subentra un problema non da poco. Le risorse HW sono limitate, processori, RAM, disco fisso, VRAM, mentre ci sono molti pretendenti al loro utilizzo. In questo caso il S.O. assume oltre che il ruolo di astrazione anche quello di controllo e blocco delle risorse. In pratica se prima era sconsigliato ai software applicativi accedere alle risorse hardware, ora è, almeno in teoria, vietato. Vengono introdotti i meccanismi di gestione della memoria avanzati come lo spazio di indirizzamento privato, in pratica ogni programma vive all'interno di un suo spazio di memoria e non può uscire da esso ne può allargarlo senza il dovuto permesso. Può eventualmente comunicare con il mondo esterno (gli altri processi) ma solo sfruttando i meccanismi messi a disposizione dal S.O. senza alcuna possibilità di contravvenire a questa regola. Chi si ricorda di Amiga sa che una peculiarità di quel sistema operativo era quella di non avere, in teoria, indirizzi fissi, eccetto il 4, questo perchè un task (o processo) può essere caricato ogni volta in una porzione differente di memoria.
Logicamente partendo da quanto detto può sembrare che meno programmi girano e meglio è ed in effetti è così, questo perchè la timeslice assegnata ai singoli task è più ampia. Ma il meccanismo della rotazione ha i suoi svantaggi. Immaginate una fila di persone che deve prendere un medicinale ad uno sportello, ma non tutte le persone stanno male allo stesso modo quindi c'è chi deve prendere il medicinale più frequentemente e chi deve prenderlo meno frequentemente. Il meccanismo della rotazione (denominato tecnicamente "round-robin") non fa differenze, così avremo qualcuno che stara bene, qualcuno che soffrirà per mancata assunzione del medicinale e qualcuno che soffrirà per sovradosaggio. Lo sportellista (denominato "scheduler" o schedulatore) deve quindi valutare di volta in volta quando far tornare il paziente e fissa una specie di appuntamento, o meglio gli assegna una priorità dicendogli che anzichè tornare tra dieci persone potrà tornare tra tre. Questo è il meccanismo dei livelli di priorità. Un processo che ha necessità di maggiori cure, ovvero risorse, con maggior frequenza, può chiedere una priorità più alta, se possibile lo schedulatore la accorda, altrimenti dovrà rimettersi in fila normalmente. Va da se che nell'ambito del multitasking lo schedulatore è forse il processo in assoluto più importante di tutto il S.O. un bug al suo interno può portare a conseguenze devastanti.
E se succede un'emergenza o se il medicinale dev'essere assunto sotto sorveglianza? Nel tempo è stato tenuto conto di queste condizioni ed hanno creato le due principali branche del multitasking: il multitasking preempitivo e quello non-preempitivo.
Per spiegarli dobbiamo introdurre il concetto di livello di esecuzione. Immaginate un bersaglio delle frecce (quello a cerchi concentrici). Il livello più al centro è quello che da più punti, ma è anche quello che è più difficile da colpire. Tutti lo mirano, pochi ci riescono. Ecco immaginate che il cuore del S.O. (il da molti citato "kernel" o nocciolo) si trova lì (in realtà li si trova l'hardware, ma è un altro discorso). Mano mano che ci si allontana dal centro i punti e la difficoltà diminuiscono. I programmi applicativi si trovano nell'anello più esterno. Va da se che più all'esterno ci si trova più sono i passaggi che il nostro processo dovrà effettuare, in modo implicito, per eseguire un'operazione. Se il programma viene scritto secondo determinate regole, sempre più stringenti, può cercare di essere eseguito nei cerchi più interni, con indubbi vantaggi sulla priorità. Tornando al discorso dello sportello, in caso di emergenza è così possibile che una persona interrompa le normali procedure per avere una cura di emergenza e che il tutto venga ripristinato solo a cura completamente somministrata. Questo è il caso del multitasking non-preempitivo.
Ovvero un processo eseguito a livello più interno (riferendoci ai bersagli) non può assolutamente essere interrotto nella sua esecuzione da processi aventi livello più esterno, almeno fino a che il primo processo non rilascia esplicitamente il controllo. Ricadono in questa condizione tutti i processi di livello di sistema, quali quelli di accesso all'hardware o di sincronia. Va da se che se si scrive un programma in grado di girare a livello di sistema può impossessarsi della macchina bloccandola in quanto non può essere interrotto. Nel caso di multitasking non-preempitivo il livello di esecuzione è più importante della priorità.
Nei sistemi preempitivi (Windows 95/98/ME) semplicemente non esistono i livelli di esecuzione, in pratica esistono due cerchi il S.O. ed i programmi (e non sono neanche così ben definiti), per cui qualsiasi processo con un adeguato livello di priorità può prendere il sopravvento sugli altri.
Come conseguenza diretta del multitasking abbiamo avuto la multiutenza. Semplicemente i programmatori si sono chiesti, visto che gli umani sono così "lenti", possiamo permetterci di attendere più input, ognuno per uno specifico task, da più persone contemporaneamente, tanto il "server" sarà comunque più veloce. Da qui si è pensato di individuare prima con il termine "server" una sola macchina e poi un gruppo di macchine identificate logicamente come una singola macchina e denominate "cluster". Ma per chi fa grafica 3D il netrendering sa che è una disciplina a sé.
Negli ultimi anni la potenza dei PC è diventata letteralmente enorme, la loro capacità di lavorare anche in sinergia li ha resi ancora più incredibili e si è passati dalle centinaia, alle migliaia, ai milioni ed infine ai miliardi di operazioni al secondo, e la disponibilità di sistemi multiprocessore prima e multicore poi al grande pubblico non ha fatto altro che rimarcare ancora di più questa condizione.
I programmatori, sempre loro, sono partiti da una funzione di sistema di Unix (c'è sempre lui di mezzo) la "fork()", questa funzione permette di spezzare un processo in due e rendere i due processi indipendenti ma aventi alla base le stesse informazioni, o meglio uno riceve la copia delle informazioni dell'altro. Si sono chiesti e se invece di copiare le risorse le condividessero? Sono così nati i thread.
Cosa sono quindi i thread? Sono figli di un processo che condividono con quest'ultimo le stesse risorse (fisiche e temporali) e la stessa priorità e livello di esecuzione.
Quali sono i vantaggi? Beh in grafica 3D sono enormi, ed in genere in tutte quelle condizioni dove è possibile eseguire il calcolo parallelo. Ma anche in tutte quelle condizioni in cui lo stesso codice deve essere eseguito contemporaneamente, come ad esempio i server Web. Mi spiego meglio.
Come funziona un server web? Immaginate un centro informazioni che ha a disposizione una serie di opuscoli ed il cui compito e distribuirli su richiesta. Inizialmente le persone si mettevano in fila davanti allo sportello, ognuno faceva la sua richiesta, riceveva il suo opuscolo e se ne andava, quindi veniva il turno della persona successiva e si ricominciava.
Se si voleva rispondere a più richieste bisgnava aprire nuovi sportelli in tutto e per tutto identici al primo (si potrebbe dire clonati) col risultato si di andare più veloci, ma con aumento dei costi per gli stipendi (due persone costano più di una) e con la diminuzione dello spazio vitale in cui muoversi. Poi qualcuno ha detto: "Hei! Ma l'impiegato ha due mani! E se prendesse due opuscoli contermporaneamente?", a parte che se fosse uno statale e lavorasse in Italia l'impiegato avrebbe fatto sicuramente il gesto dell'ombrello, questi sono i thread.

Per ora mi fermo qui. A presto con il resto, sempre se vi fa piacere....