Avanti Indietro Indice

9. Come fa il computer a evitare che i processi si intralcino tra loro?

Lo scheduler del kernel si prende cura di dividere il tempo tra i processi. Il vostro sistema operativo deve dividere tra i processi anche lo spazio, per evitare che non sconfinino oltre la porzione di memoria loro assegnata. Le operazioni compiute dal sistema operativo per risolvere questo problema si chiamano gestione della memoria.

Ogni processo del vostro repertorio ha la propria area di memoria core, come luogo dal quale eseguire il proprio codice e dove immagazzinare le variabili e i risultati. Potete pensare a questo insieme come formato da un segmento codice, di sola lettura (che contiene le istruzioni del processo) e da un segmento dati (che contiene tutte le variabili immagazzinate dal processo). Il segmento dati è sempre unico per ogni processo, mentre nel caso due processi usino lo stesso codice Unix automaticamente fa in modo che condividano un unico segmento codice, come misura di efficienza.

L'efficienza è importante, perché la memoria core è costosa. A volte non ne avete abbastanza per contenere per intero tutti i programmi che il computer sta eseguendo, specialmente se usate un grosso programma quale un server X. Per ovviare a questo problema Unix usa una strategia chiamata memoria virtuale. Non cerca di tenere in core tutti i dati ed il codice di un processo. Tiene piuttosto caricato solo un working set relativamente piccolo; il resto dello stato del processo viene lasciato in uno speciale spazio swap sul vostro disco fisso.

Come i processi sono in esecuzione Unix tenta di anticipare i cambiamenti del working set per avere in memoria solo le parti che servono davvero. Riuscirci in modo efficace è ingegnoso e complesso, pertanto non cercherò di descriverlo tutto qui, ma si basa sul fatto che il codice e i riferimenti ai dati tendono a comparire a gruppi, ed è probabile che un nuovo gruppo si colleghi a luoghi vicini a quelli di uno precedente. Quindi se Unix tiene caricati i dati ed il codice usati più di frequente (o di recente) di solito riuscirà a risparmiare del tempo.

Notate che in passato quel "A volte" di due paragrafi fa era un "Quasi sempre", perché la dimensione della memoria era tipicamente ridotta rispetto alla dimensione dei programmi in esecuzione, quindi il ricorso allo swap era frequente. Oggi la memoria è molto meno costosa e persino i computer di fascia bassa ne hanno parecchia. Sui moderni computer monoutente con 64MB di memoria e oltre è possibile eseguire X ed un tipico insieme di programmi senza neppure ricorrere allo swap.

Anche in questa felice situazione la parte del sistema operativo chiamata gestore della memoria mantiene un importante ruolo da svolgere. Deve garantire che i programmi possano modificare soltanto il proprio segmento dati; deve cioè impedire che del codice difettoso o malizioso in un programma rovini i dati di altri programmi. A questo scopo tiene una tabella dei segmenti dati e codice. La tabella è aggiornata non appena un processo richiede più memoria oppure libera memoria (quest'ultimo caso ricorre di solito all'uscita dal programma).

Questa tabella è usata per passare dei comandi a una parte specializzata dell'hardware sottostante chiamata MMU o unità di gestione della memoria. I processori moderni hanno MMU incorporate. La MMU ha la peculiare capacità di porre dei delimitatori attorno alle aree di memoria, in modo che un riferimento che sconfina venga rifiutato e faccia scattare uno speciale interrupt.

Se avete mai visto un messaggio del tipo "Segmentation fault", "core dumped" o qualcosa del genere, questo è esattamente quello che è successo: un tentativo da parte del programma in esecuzione di accedere alla memoria al di fuori dal proprio segmento ha fatto scattare un interrupt fatale. Questo rivela un bug nel codice del programma; il core dump (scarico della memoria) che lascia dietro di sé costituisce una informazione diagnostica volta ad aiutare il programmatore nell'individuazione del problema.


Avanti Indietro Indice