di  -  giovedì 15 settembre 2011
Nell’articolo precedente abbiamo parlato del meccanismo delle Syscalls, e abbiamo visto come fare per passare il controllo al kernel, per poi tornare al codice utente.

In un sistema del genere, dove i task degli utenti sono separati rispetto al kernel del Sistema Operativo, è possibile eseguire un certo numero di task in modo concorrente. L’alternarsi dei task in rapida successione consente di rendere l’illusione che tali task vengano eseguiti contemporaneamente, anche se il sistema è dotato di una singola unità di calcolo (sistema con singola CPU).

Esistono svariati modi per raggiungere lo scopo, ma affrontiamo qui soltanto i due principali: multiprogrammazione cooperativa e prelativa meglio conosciuti come, rispettivamente, cooperative e preemptive multitasking. Entrambi i metodi presentano molti caratteri comuni e una sostanziale differenza. Vediamo prima le analogie.

Task Context
Con questo termine si definisce un pacchetto di informazioni utile per definire lo stato corrente di un task. Per semplicità non parleremo di Thread e Processi (che sono anch’essi dei task), perchè la loro implementazione differisce moltissimo da un sistema operativo all’altro. Pertanto ci limiteremo a considerare il task come l’unità minima di esecuzione, che su alcuni sistemi potrebbe coincidere con il processo, il thread, il fiber o altra forma di unità di esecuzione. Su alcune implementazioni, addirittura, la distinzione tra Thread e Processi è talmente sfumata da essere del tutto fuorviante.

Lo stato corrente di un Task dipende strettamente dall’architettura del calcolatore ed in genere comprende il contenuto di tutti i registri della CPU (o di una parte di essi, in base alle specifiche dell’architettura) oltre a varie informazioni accessorie come il numero identificativo del task (usato internamente dal kernel per allocare le risorse), l’elenco dei file aperti, l’elenco dei lock acquisiti (mutex e/o semafori), le variabili d’ambiente, ecc…
Per consentire la multiprogrammazione, è necessario un meccanismo che consenta di eseguire queste operazioni:

  • interrompere il task corrente
  • salvare tutte le informazioni necessarie a caratterizzarlo nell’apposito Task Context
  • scegliere il nuovo task da eseguire (Scheduling)
  • caricare le informazioni del nuovo Task Context sostituendo quello attuale

Se il meccanismo viene ripetuto varie volte al secondo, si avrà la percezione di un sistema che esegue contemporaneamente un certo numero di task diversi. In gergo tale meccanismo prende il nome di Context Switch.

La differenza tra i due principali tipi di multitasking sta proprio nel meccanismo che interrompe il task corrente. Nel multitasking cooperativo l’interruzione avviene tramite una particolare Syscall chiamata in gergo yield, mentre nel multitasking prelativo il task viene interrotto per mezzo di un’interrupt esterno, generato solitamente da un timer programmabile (la cui frequenza di interruzione può essere fissa o variabile a runtime). Ne consegue che, nel primo caso, il task non viene interrotto finchè questo non cede volontariamente il posto (quindi coopera con gli altri task) chiamando la relativa Syscall; nel secondo caso, invece, il task viene forzatamente interrotto tramite un’interrupt generato dall’hardware, il quale viene a sua volta intercettato dal relativo interrupt handler, come accennato nell’articolo precedente.

Il secondo e l’ultimo meccanismo, cioè il salvataggio ed il caricamento del Context, sono relativamente semplici perchè si tratta di salvare e ricaricare un certo numero di registri della CPU in un determinato buffer di memoria, precedentemente allocato all’atto della creazione del task stesso.

La parte invece più complessa, e sulla quale si scervellano da anni eserciti interi di informatici teorici e matematici, è la parte relativa allo Scheduling. Esistono un’infinità di metodi diversi per scegliere il task successivo da eseguire, e larga parte dell’efficienza di un Sistema Operativo dipende da questo piccolissimo componente che è lo Scheduler.

Vedremo nel prossimo articolo come si distinguono i vari tipi di Scheduler, e quali sono le problematiche ad essi relative.

14 Commenti »

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.

  • # 1
    Cesare Di Mauro
     scrive: 

    In AmigaOS i Task erano veramente minimali: non avevano nemmeno l’elenco delle risorse allocate e le informazioni sull’ambiente d’esecuzione.

    AmigaDOS, invece, introduceva il concetto di processo, ma solo per gestire alcune informazioni addizionali (ormai non ricordo più quali. Forse c’erano anche le variabili d’ambiente) per i programmi che venivano lanciati.

    Una cosa interessante era che si poteva cambiare lo scheduler “live”, in tempo reale. :D

  • # 2
    awert
     scrive: 

    Considerata la brevità…lo definirei “articulus interruptus”….

  • # 3
    Antonio Barba (Autore del post)
     scrive: 

    @Cesare: si, per questo sono rimasto sul generico, proprio per l’ampia varietà di implementazioni possibili.

    @awert: hai ragione, ma preferisco ritornare sul tono divulgativo e trattare poche cose alla volta. Ho ricevuto molti commenti del tipo “Bello ma non c’ho capito niente” riguardo all’articolo precedente a questo. Il mio scopo non è raccontare le cose a chi conosce già la fine, ma incuriosire i newbye ad approfondire l’argomento :-)

  • # 4
    banryu
     scrive: 

    @Antiono Barba: Bell’articolo, e ci ho capito tutto. Sono uno di quelli che “non conosce già la fine”, e mi piacciono molto i tuoi articoli dal taglio divulgativo, per i quali ringrazio.
    Suppongo (e spero) che nei prossimi presenterai i vari tipi di scheduler addentrandoti un po’ di più nei dettagli.
    A tal proposito pensi di fare un solo articolo o più di uno (sugli scheduler, intendo)?

  • # 5
    Antonio Barba (Autore del post)
     scrive: 

    @banryu: grazie!
    Penso di trattare gli algoritmi di scheduling suddividendoli per “classi”, in modo da approfondire un pochino ciascuna classe caratterizzandone i principali pro e contro :-)

  • # 6
    Abele
     scrive: 

    OT: Quando un articolo che spiega come mai è una stronzata misurare la qualità di un OS in base alla quantità di RAM occupata?
    Capisco che l’utente medio di HWupgrade non sia laureato in informatica ma si leggono le stesse castronerie da 10 anni.

  • # 7
    Antonio Barba (Autore del post)
     scrive: 

    @Abele: la gestione della memoria è una parte fondamentale di ogni Sistema Operativo. In generale i sistemi moderni tendono ad usare molta Ram per tenere una cache più ampia possibile, per ridurre gli accessi ai dischi (3 ordini di grandezza più lenti). Il consumo di Ram può anche derivare da sprechi, senza dubbio, quindi non si può stabilire una regola generale ma bisogna esaminare caso per caso per stabilire se un elevato consumo sia dovuto ad una buona politica di caching, oppure a difetti di programmazione che provocano Memory Leaks.

    C’è anche da considerare che i moderni sistemi di sviluppo (ad esempio i linguaggi .Net, Java e Objective-C) consentono di ridurre notevolmente lo spreco di memoria Ram grazie a meccanismi di Garbage Collecting, che non sono la panacea di tutti i mali ma sicuramente danno una mano.

    Non si nasce imparati e non mi aspetto che l’utente medio di computer conosca questi dettagli. Comunque, mi hai dato un buono spunto che terrò in considerazione per i prossimi articoli ;-)

    Ciao!

  • # 8
    Marco
     scrive: 

    Intervengo anch’io per la prima volta, caro Antonio, anche se ti seguo da tanto: solo per ringraziarti della scelta di rimanere “divulgativo”, e per manifestarti tutti i miei complimenti per la tua bellissima rubrica.
    Per chi è soltanto, modestamente, un “cultore della materia”, senza nessuna preparazione specifica a livello accademico, i tuoi articoli – insieme ad altri di AppuntiDigitali – sono una vera “manna”.
    Continua così, grazie!

  • # 9
    Marco
     scrive: 

    “C’è anche da considerare che i moderni sistemi di sviluppo (ad esempio i linguaggi .Net, Java e Objective-C) consentono di ridurre notevolmente lo spreco di memoria Ram”

    A me pare il contrario… parlo di Java almeno: tenersi in memoria una virtual machine, un compilatore JIT, un ottimizzatore di codice, tutti gli oggetti allocati e dereferenziati in attesa del garbage collector, cache per il bytecode compilato, 512KiB di stack per ogni thread (impostazione di default), payload per gli oggetti più banali (Integer wrapper di int) di svariati bytes…

    http://shootout.alioth.debian.org/u32/which-language-is-best.php?gcc=on&csharp=on&java=on&xfullcpu=0&xmem=1&xloc=0&nbody=1&fannkuchredux=1&meteor=1&fasta=1&fastaredux=1&spectralnorm=1&revcomp=1&mandelbrot=1&knucleotide=1&regexdna=1&pidigits=1&chameneosredux=1&threadring=1&binarytrees=1&calc=chart

    In media C# (Mono) usa 5x la memoria del C, Java (implementazione di riferimento) 10x.

    Se parli di memory leaks invece non è certo uno spreco, ma un palese errore di programmazione.

  • # 10
    Antonio Barba (Autore del post)
     scrive: 

    @Marco commento #8: grazie!
    @Marco commento #9: il discorso sui linguaggi è OT, ma posso dire in due righe che ho lavorato su progetti “mastodontici” (leggasi: 15 mila file sorgente C++, diversi milioni di righe di codice) dove il peso dell’eseguibile finale arrivava a malapena a 37 MB compilato in modalità Debug, 12-13MB in Release (comprese anche le DLL di cui si compone il programma).
    In un programmino di 50 linee di codice, puoi misurare incrementi 5x e 10x come indica quel sito, ma su progetti “reali” l’incremento si attesta su pochi punti percentuali. Comunque, anche se fosse 2x (cifra stratosfericamente esagerata), sarebbero sempre 20-25MB di codice eseguibile nel caso di un progetto “enorme”, quindi risparmiare su pochi megabyte, nel 2011 non è certamente più un problema, anche perchè ci sono molti altri vantaggi che compensano abbondantemente questi pochi svantaggi.
    So di non essere stato molto esauriente, ma non voglio prolungare l’OT ulteriormente. Avremo modo di discutere di questo in un altro articolo, oppure in privato o sui maggiori social networks se vuoi :-)

  • # 11
    Cesare Di Mauro
     scrive: 

    Un articolo è meglio. O:-)

  • # 12
    Marco
     scrive: 

    @Antonio
    Uso C e Java da una vita per lavoro e devo dire che non sono d’accordo su niente di quello che dici :-)
    Anche se l’overhead fosse “solamente” 2x (eccessivamente ottimista, ma ammettiamo che sia così) cmq non vedo come Java possa “ridurre notevolmente lo spreco di memoria” :-DDD

    @Antonio, Cesare
    Concordo, meglio l’articolo! :-D

  • # 13
    Marco71
     scrive: 

    Buon pomeriggio…penso si debba scrivere newbie (newbies al plurale) e non “newbye”.
    Grazie.

    Marco71.

  • # 14
    Antonio Barba (Autore del post)
     scrive: 

    @Marco #12: farò una ricerca approfondita il più possibile obiettiva, ed un articolo dettagliato in merito, lo prometto :-)
    @Marco71: ok :-D

Scrivi un commento!

Aggiungi il commento, oppure trackback dal tuo sito.

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.