OS Writing [1]: CPU sotto processo

Una delle realtà dell’elettronica di oggi è che è ovunque. I motivi per cui è ovunque sono parecchi, uno dei quali è il sempre più basso costo di componenti anche sofisticati, come quelli programmabili. Poter comprare un dispositivo programmabile semplifica di parecchio le cose, visto che programmare è più semplice che realizzare dell’hardware ad hoc. Però (e qui il gatto inizia a mangiarsi la coda), il fatto che programmare sia (relativamente) semplice ha portato alla creazione di tutta una serie di altro software concepito per rendere ancora più semplice programmare. Stiamo parlando di ambienti di sviluppo dedicati e, ovviamente, sistemi operativi. I sistemi operativi embedded sono belli e cari perché in certi casi ti rendono la vita più semplice, ma sono anche brutti e antipatici perché, spesso e volentieri, per renderti la vita più semplice fanno tante cose di cui non è detto tu abbia bisogno, a discapito delle prestazioni del tuo sistema. Per esempio, a che serve caricare del software per gestire tutte le periferiche possibili e immaginabili se tutto quello di cui hai bisogno è schedulare un paio di processi? Iniziamo con questo una serie di articoli rivolti agli amanti estremi del fai-da-te, ai progettisti di software o semplicemente ai curiosi, e a chi gradisce avere un sistema operativo sul proprio micro ma non vuole che sia anche un peso sul core (perdonate il gioco di parole).

PROGRAMMI E PROCESSI

Cominciamo con una domanda: perché e (soprattutto) quando ci serve un sistema operativo? In altre parole, perché Windows è lì? Tralasciando le risposte fideistiche (perché è il Verbo di Dio, perché è un parto del Maligno a seconda dei casi), Windows è lì perché è l’unico modo in cui un unico processore può far girare più programmi contemporaneamente senza fare pasticci. Un sistema operativo è un programma che controlla altri programmi, che decide quando mandarli in esecuzione e come e quando questi possono accedere a tutte le risorse che gli hardware-isti hanno messo a disposizione dei software-isti, ossia porte USB, porte Ethernet, tastiere, mouse, schede video, link di comunicazione satellitari, Space Shuttle o centrali nucleari che si voglia. Se volete che Word giri insieme a Chrome, ci serve qualcuno che mandi in esecuzione Word, ce lo tenga per un pò, gli tolga la CPU, mandi in esecuzione Chrome e via di questo passo. Per permettere a Chrome di inviare per mail un file di Word, ci serve qualcuno che eviti che un utente sconsiderato modifichi il file di Word mentre Chrome lo sta inviando. Tanto per fare due esempi. Da un punto di vista più generale, un sistema operativo è quel programma che fa credere agli altri programmi di avere tutte le risorse a propria incondizionata ed esclusiva disposizione. Tutto per me, dice Word! Tutto per me, dice Chrome! Nessuno dei due ha ragione, ma entrambi ne sono convinti.

Mmmm… ok, bene. Come si fa? Per cominciare, dobbiamo introdurre un paio di concetti. Il primo è quello di processo. A grandi linee, un processo è un programma in esecuzione. No, non è la stessa cosa. Un programma è un pò di codice compilato che è stato salvato sul disco. Un processo è un programma che è stato preso dal disco, caricato in memoria, e gli sono state assegnate un pò di risorse, per esempio dei dispositivi “virtuali”, o delle pagine di memoria. Una nota tra parentesi: tutte le volte che usiamo la parola “virtuale” come aggettivo per qualcosa lo facciamo per dire che quel qualcosa (i dispositivi, in questo caso) non sono fisici e tangibili, ma sono quelli “finti”, “emulati” dal sistema operativo, che il processo crede di avere a disposizione mentre non è così.

Oltre a delle risorse, un processo ha anche uno stato. Immaginate ad un certo punto di interrompere l’esecuzione del processo. I registri del processore conterranno certi valori, giusto? Il program counter conterrà l’indirizzo dell’istruzione corrente, gli altri registri conterranno i risultati delle operazioni che il processo ha compiuto fino a quel punto. Immaginate ora di riprendere l’esecuzione. Può passare anche un’ora o un mese, ma fintanto che il contenuto dei registri non cambia, il processo riprenderà l’esecuzione esattamente da dove aveva lasciato. In più, il processo non si accorgerà minimamente del fatto che è passata un’ora o un mese.

Immaginate ora di prendere i registri, salvarli da qualche parte, pasticciarli in qualche modo buffo per un pò, ri-caricare i registri salvati e poi far ripartire il processo. Di nuovo, il nostro processo non si accorgerà né di essere stato interrotto, né che nel mezzo avete fatto qualcosa con i “suoi” registri.

Figura 1: Timesharing

Questo ci porta sostanzialmente al secondo concetto, quello di timesharing, e alla più semplice strategia per permettere a più processi di condividere la CPU. Ogni tot tempo interrompiamo l’esecuzione del primo processo, salviamo il suo stato, carichiamo lo stato del secondo processo e lo mandiamo in esecuzione. Dopo lo stesso tot tempo, interrompiamo il secondo processo, salviamo il suo stato, ri-carichiamo lo stato del primo processo e lo ri-mandiamo in esecuzione. Nessuno dei due processi si accorgerà mai né di essere stato interrotto né che, mentre era fermo, un altro processo ha fatto qualcosa, perché quando l’esecuzione riprende, lo stato della CPU era lo stesso che avevano lasciato quando l’esecuzione era stata interrotta. Questa è una delle cose che fa il sistema operativo, e questo è ciò di cui vi parleremo oggi.

TUTTO AL MOMENTO GIUSTO

Il timesharing si appoggia completamente al concetto di interrupt. Un interrupt è, senza troppa fantasia, l’interruzione del normale svolgimento delle operazioni, a causa di un qualche motivo esterno. Ad esempio, un interrupt può essere lanciato (si dice così) nel momento in cui viene premuto un tasto di una tastiera, o cliccato il pulsante di un mouse, o ricevuto un bit su una seriale. Si tratta di eventi che possono capitare senza preavviso e che vanno gestiti subito. E’ inutile visualizzare a schermo il tasto che avete premuto dieci minuti dopo averlo premuto, o ricordarsi di salvare i dati della seriale quando quei dati ormai sono andati persi nel paradiso dei bit. Indipendentemente da quello che sta facendo, il sistema deve fermare tutto e gestire l’interrupt. Deve interrompersi, per l’appunto.

Tra le varie sorgenti di interrupt che un processore può avere c’è anche il cosiddetto RTI , Real-Time Interrupt. Se avete mai frugato sul datasheet di un microcontrollore, avrete magari notato che, se c’è una periferica che tutti i micro hanno, è il timer. Bene, l’RTI è tutta un’altra cosa. Si tratta pur sempre di un interrupt lanciato a intervalli regolari, ma si tratta di intervalli molto più lenti di quelli che si possono ottenere con i timer interni ai micro, dell’ordine dei millisecondi. Un bel pò, dal punto di vista di un processore. Se i timer interni servono a contare eventi, a generare PWM o cose del genere, l’RTI serve semplicemente a fermare la CPU a intervalli regolari, nel caso avesse qualcosa da fare a intervalli regolari. L’applicazione più stupida di un RTI è mantenere un orologio: non ha senso incrementare il tempo misurato a intervalli dell’ordine del nanosecondo, i millisecondi bastano. Un’applicazione più intelligente è proprio il timesharing: non ha senso interrompere un processo ogni nanosecondo, i millisecondi vanno più che bene.

Dunque, abbiamo questo RTI, questo timer lento che tiene in riga la CPU, ricordandole gli impegni da mantenere. Che accade quando il timer decide che il tempo è scaduto? A livello hardware, come abbiamo visto, quello che accade è che il processore salva l’indirizzo dell’istruzione successiva (4 byte dopo il valore corrente del program counter, quindi PC + 4) in un registro dedicato, che abbiamo chiamato XP, e carica nel PC un particolare indirizzo di memoria. In questo indirizzo di memoria c’è semplicemente un’istruzione di salto alla prima istruzione di un pò di codice dedicato alla gestione dell’interrupt. Se dessimo un’occhiata alla mappa di memoria con il nostro codice, vedremmo probabilmente una cosa di questo tipo:

Figura 2: Mappa della memoria

[...]

ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2623 parole ed è riservato agli ABBONATI. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici che potrai leggere in formato PDF per un anno. ABBONATI ORA, è semplice e sicuro.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend