Informatica per script kiddies 10 – I sistemi operativi

Eccoci al nostro appuntamento mensile con Informatica per script kiddies!

Oggi parliamo dei sistemi operativi, quei software che utilizziamo tutti i giorni e delle cui funzioni, nonostante questo, tendiamo ad avere un’idea un po’ fumosa.

I sistemi operativi infatti, nonostante siano dei programmi a tutti gli effetti, vivono in quel limbo che sembra sempre un po’ essere a metà tra hardware e software, e non sentiamo quasi mai di starlo direttamente utilizzando.

La faccia del sistema non è davvero il sistema

Il primo punto che trovo sia interessante capire dei sistemi operativi è che non sono la faccia che mostrano all’utente.

Siamo tutti piú o meno abituati a distinguere i tre sistemi operativi con cui abbiamo piú a che fare sui computer, Windows, GNU/Linux (da qui lo chiamerò Linux), e Mac OS in base all’aspetto che hanno. In realtà, quell’aspetto è dato dai programmi che piú comunemente sono installati per utilizzare il sistema, e che potrebbero essere sostituibili o intercambiabili.

Chi ha a che fare con Linux, ad esempio, si accorge ben presto che esistono moltissimi diversi software per disegnare il desktop e per navigare tra file e directory, e che due differenti installazioni del sistema possono essere estremamente diverse, fino ad arrivare sui cellulari, con Android, che pur essendo senz’altro un sistema Linux non ne ha “l’aspetto”.

Chi poi utilizza i computer a un livello un po’ piú profondo del normale, si rende conto che le interfacce per dare i comandi testuali al computer possono essere persino comuni a piú sistemi operativi. Se Windows utilizza soprattutto due sistemi di riga di comando (cmd e Power Shell), i sistemi diffusi in ambito Linux, come bash o zsh sono i medesimi anche su Mac e funzionano, con accorgimenti, persino su Windows.

E allora che differenza c’è tra i sistemi?

Un sistema operativo è un software che viene avviato durante la procedura di accensione del computer, e serve a fornire servizi agli altri software.

In un’epoca molto primordiale della storia dell’informatica, i software dovevano contenere tutto il necessario per funzionare, comprese le cose utili a far partire la macchina e a gestirne gli aspetti fondamentali. Presto si iniziò a separare – e memorizzare direttamente sulla macchina – tutto il necessario all’avvio (quello che oggi si chiama BIOS, o firmware UEFI a seconda dei casi), e a caricare del codice di gestione, circa sempre lo stesso, per fornire al software caricato successivamente un po’ di routine pronte all’uso.

Da tempo, e ancora oggi, sono invece i sistemi operativi a fornire uno strato di routine e funzioni utili a tutti gli altri programmi. Per fare un esempio, ma ne vedremo degli altri, un po’ tutti i programmi si trovano a dover scrivere cose sul disco del computer. Invece di costringere tutti i programmatori a scrivere del codice per scrivere sul disco – cosa che presupporrebbe tra l’altro di avere informazioni sulla struttura della macchina – si installa un sistema operativo che fornirà a tutti i programmi un set di funzioni per chiedergli di scrivere su disco, cosa che poi farà lui. Molto comodo.

Le vere differenze tra i sistemi operativi, quindi, riguardano che funzionalità offrono ai software e il modo in cui le offrono. In termini pratici, la differenza tra diversi sistemi sta in che software ci gira: chi scrive un programma, infatti, utilizza le funzioni che il sistema gli offre. Scrivere un programma per un sistema significa utilizzare delle cose che su un altro sistema non ci sono o sono diverse, e quindi il programma non girerà su sistemi diversi (questo in generale, ci sono ovviamente linguaggi, come abbiamo detto nello scorso articolo, il cui compilatore o interprete nasconde le differenze).

I servizi

Vediamo una panoramica dei servizi – alcuni li si danno molto per scontati, altri meno – che solitamente un sistema offre ai programmi che ci girano.

Divisione di tempo e risorse

Uno dei servizi piú “nascosti” all’utente – peraltro relativamente recente – è la divisione di tempo e risorse tra i diversi software.

Quando utilizziamo un PC, oggi, siamo abituati a vederlo far girare tanti programmi in contemporanea. Non è sempre stato cosí: non molto tempo fa, i programmi potevano girare solo uno per volta, perché l’hardware ha un limite alle cose che può fare contemporaneamente. I sistemi operativi moderni si occupano di far girare tutti i software che l’utente vuole a brevissimi turni, in base a una serie di criteri e priorità molto complessa, dando l’illusione che tutto giri contemporaneamente.

Gestione degli utenti

Questa è un po’ piú evidente: i sistemi operativi moderni offrono solitamente un insieme di funzionalità utili a dare a ogni utilizzatore della macchina un suo spazio in cui lavorare senza pestare i piedi agli altri, solitamente anche accedendo alla macchina in piú utenti per volta. Spesso viene gestita anche la sicurezza degli utenti, impedendo a categorie di utenti di fare operazioni, e vietando fisicamente che alcuni utenti vedano il lavoro degli altri.

Accesso alle periferiche

Le periferiche di un computer, dai dischi alle stampanti, hanno due grandi problemi: li vogliono usare tutti e sono drammaticamente lenti rispetto ai tempi con cui il PC lavora. Una delle primissime cose di cui i sistemi operativi si sono occupati è proprio questa: nascondere la lentezza di oggetti come le stampanti ai software, che possono far finta che una cosa da stampare sia già stata stampata e continuare a fare quello che stavano facendo, mentre il sistema fa il lavoro sporco.

Il sistema insomma fornisce un’interfaccia a cui i programmi possano dire “scrivi su disco”, “stampa questo foglio”, e lui possa dire “ok, me ne occupo appena posso” senza che ogni programma debba aspettare una risposta dalla periferica, o peggio ancora debba aspettare che un altro programma smetta di usarla. Lo stesso vale anche – e forse ormai soprattutto – per le periferiche di comunicazione come l’interfaccia di rete.

Accesso alla memoria e relativa sicurezza

Un altro importante compito del sistema operativo è quello di gestire la memoria (la RAM, insomma), suddividerla fra i software che girano sul computer e gestirne la sicurezza, ovvero vietare che un software vada a leggere o scrivere le aree di memoria utilizzate da un altro software. Anche questo è un compito abbastanza trasparente e poco “visibile”, ma è di fondamentale importanza, e il suo meccanismo di funzionamento può generare differenze enormi nell’efficienza, nella sicurezza e nella reattività tra i vari sistemi e tra versioni diverse del medesimo sistema.

Utilità lato utente

Infine, c’è la cosa piú visibile: di solito nel pacchettone “sistema operativo” sono inclusi una miriade di software di utilità, grafici e non (spesso la stessa interfaccia grafica è tra questi, come avviene su Windows) per la gestione delle funzionalità del sistema. Software molto comuni sono quelli per la gestione degli utenti, per la gestione dei processi, per sapere quanto le risorse sono utilizzate e da chi, e per la gestione della coda di stampa (e di altre code). Inoltre, in molti sistemi ci si aspetta siano presenti determinate utilità, come alcuni editor di testo e una qualche interfaccia minima per l’esecuzione di comandi, che pur non facendo rigorosamente parte del sistema stesso è un po’ come se invece ne fossero dei mattoncini non trascurabili.

Come “è fatto” un sistema operativo

Su come costruire fisicamente un sistema operativo ci sono moltissime scuole e dibattiti che durano letteralmente da decenni, quindi non esiste un modo solo per farli. In generale, comunque, un sistema è composto da un software che si occupa di fare tutte le operazioni che vengono considerate fondamentali e comuni, detto Kernel, e una serie di piccoli software satelliti che si occupano di fare cose considerate secondarie.

I sistemi con un piccolo kernel e molti software attorno vengono detti “a microkernel”, e hanno il vantaggio di avere una migliore semplicità di intervento sul kernel e un’altissima flessibilità (posso non includere tutti i pezzi di sistema che non servono per la specifica macchina). Quelli con un kernel piú imponente che fa piú o meno tutto sono invece detti “a kernel monolitico”, e a fronte di una maggiore complessità di intervento offrono una maggiore velocità e una minore quantità di codice necessaria per scrivere l’intero sistema.

I sistemi piú diffusi oggi in ambito PC hanno un approccio intermedio, separando diverse parti in moduli che possono essere riscritti e ricompilati senza toccare il kernel, che però non è snellissimo. Alcuni, come Mac OS X, pendono piú verso l’essere microkernel, e altri come Linux e Windows 10 sono un po’ piú verso il monolitico e vengono detti “a macrokernel”.

Come “si usa” un sistema operativo

Solitamente, i sistemi operativi sono dei software che vengono tenuti in esecuzione e restano in ascolto di segnali, detti interrupt. Un interrupt può venire lanciato tanto da un software in esecuzione (attraverso delle funzioni chiamate syscall) quanto da una periferica hardware, e richiede al sistema di fare una specifica operazione. Il sistema operativo, quando può, si scorre tutti gli interrupt ancora non gestiti e avvia le proprie routine legate alla specifica richiesta. Nel farlo, segue le sue proprie regole di gestione delle risorse a cui dà accesso (ad esempio, se un software vuole scrivere un’area di memoria che non può scrivere, riceverà un errore).

Per un programmatore (o, piú spesso oggi, per chi scrive compilatori e interpreti, che spesso hanno le loro funzioni che nascondono queste complessità), quindi, le operazioni gestite dal sistema operativo sono accessibili tramite delle funzioni di linguaggio che eseguono delle syscall.

Uno dei motivi per cui, a parità di hardware, un programma compilato per un certo sistema operativo non girerà su un altro è proprio questo. Se un programma è compilato per un sistema, eseguirà le syscall di quel sistema, che non verranno riconosciute dall’altro.

Ci sono sistemi con le medesime syscall?

Fare un sistema operativo è un affare complesso, e i sistemi operativi che abbiamo oggi provengono da una lunga genealogia di sistemi, che si è ramificata e stratificata. Diversi sistemi operativi hanno quindi progenitori comuni.

Come dicevamo, i sistemi oggi piú diffusi sono la versione 10 di Windows, la versione 10 di Mac OS, e svariate distribuzioni di Linux, che talvolta differiscono tra loro anche a livello di cose proprie del sistema.

Mac OS X (10, appunto) e Linux hanno un progenitore comune, o meglio utilizzano un set di syscall, detto POSIX, comune. Una parte consistente delle syscall sono quindi sovrapponibili, anche se tra i due sistemi ci sono ormai sufficienti differenze da non renderli molto intercompatibili. L’ampia disponibilità di software, soprattutto a livello di gestione fine del sistema, in comune tra i due è dovuta, in ogni caso, a questo.

Windows 10 ha invece una genealogia completamente diversa, e discende dai sistemi Windows NT, pensati per server e applicazioni professionali ai tempi dei Windows 9x (che erano completamente diversi, sotto il cofano). In comune con POSIX ha poco e nulla, ma ci sono cose che curiosamente, per motivi storici, si somigliano molto, come la gestione della rete Ethernet. Negli ultimi anni, Windows sta lavorando molto a un meccanismo che si chiama WSL, e che traduce syscall POSIX (nel set diffuso da Linux) in syscall Windows 10. Su Linux esiste da molti anni una cosa che fa l’operazione inversa, e che si chiama WINE, che però si scontra spesso col fatto che le specifiche di funzionamento di Windows non sono pubbliche. C’è chi spera che un giorno passare software da un sistema all’altro possa diventare molto piú semplice di quanto non lo sia oggi.