Gestione dei processi
Ai primordi dell'informatica il SO poteva essere visto come un unico programma di base che ripeteva in continuazione un loop (un ciclo) di interrogazione delle risorse disponibili e delle richieste dell'applicativo, assegnando poi le risorse in modo rozzo ed inefficiente (è il caso dello MS-DOS).
Oggi praticamente tutti i SO hanno un'organizzazione a processi (o task nella terminologia inglese) ed uno scheduler, il quale assegna risorse ai vari processi e li manda in esecuzione. Quando lo HW sottostante è strettamente monoprocessore ci potrà essere un solo processo alla volta in esecuzione. Con le architetture di oggi è abbastanza usuale il poter avere più processi contemporaneamente in esecuzione.
Lo scheduler applica ai processi le politiche di scheduling prestabilite per l'assegnazione delle risorse, tra le quali la CPU è solitamente la più importante, ma memoria e periferiche di I/O non possono essere trascurate. Possono essere possibili più thread all'interno di un processo. In questo caso lo scheduling dovrà (almeno) essere a due livelli, il livello dei processi e quello dei thread.
Scheduling
modificaLo scheduling comprende tre code:
- Job queue: insieme di tutti i processi e thread
- Ready queue: processi pronti e in attesa per essere eseguiti (dalla o dalle CPU)
- Code dei dispositivi: insieme dei processi in attesa di una risposta da una periferica I/O
Il sistema operativo fornisce almeno uno scheduler che si occupa di spostare i processi da una coda all'altra. Nei sistemi convenzionali gli scheduler vengono divisi in:
- Scheduler a lungo termine: viene chiamato così perché entra in azione in tempi relativamente lunghi (alcuni secondi). Il suo compito è quello di controllare quali nuovi processi vogliono entrare nella ready queue e dare un orario di entrata.
- Scheduler a breve termine: viene chiamato così perché entra in azione molto frequentemente (la sua esecuzione è intorno a 10-20 volte al secondo) e si occupa di gestire la priorità dei processi, mettendoli in coda per essere eseguiti.
Stato dei processi
modificaIl sistema operativo assegna uno stato a un processo (o thread). Lo stato viene influenzato dalla disponibilità di processore e memoria e dalle richieste del processo. Normalmente si distinguono 5 stati: new, ready, running, waiting, terminated
- Da new a ready: il processo viene spostato dallo scheduler alla ready queue dove rimane in attesa (admitted)
- Da ready a running: lo scheduler decide che il processo può utilizzare la cpu, quindi assegna la CPU al processo. (scheduler dispatch)
- Da running a
- Waiting: il processo deve attendere una risposta da una periferica I/O quindi deve essere spostato per liberare la CPU. (I/O event wait)
- Ready: lo scheduler decide che il processo deve terminare momentaneamente la sua esecuzione (ad esempio perché è scaduto il tempo assegnato) e lo rimette in coda. (interrupt)
- Da running a terminated: il processo invia il segnale di termine e lo scheduler lo sposta nello stato di terminated, in attesa che sia liberata la sua memoria. (exit)
L'utilizzo della memoria
modificaAnche se la quantità di memoria disponibile nel sistema è molto ampia, il sistema operativo definisce dei limiti per la quantità di memoria che può utilizzare il processo, definita in base a: numero delle istruzioni, memoria disponibile e altri fattori derivanti dal tipo di sistema operativo. La memoria utilizzata da un processo segue uno schema preciso (di solito lineare):
Vediamo di analizzare tutte le parti:
- Text segment: è la prima parte che troviamo. Contiene tutte le istruzioni del processo. Questa memoria non cambia MAI e per questo ha dimensione fissa.
- Data segment: contiene tutte le variabili globali di un programma (cioè che persistono sempre). Il suo valore è fisso perché alloca la memoria necessaria prima dell'esecuzione del programma. Se i dati vengono inizializzati modifica il valore (ma non la dimensione!) dei segmenti di questo livello.
- Heap: l'heap è la zona di memoria con dimensione variabile (è relazionata allo stack) dove il programmatore può allocare e deallocare la memoria a suo piacimento. Quando una variabile viene inizializzata o "deinizializzata" l'heap modifica la sua dimensione (si ingrandisce o si restringe).
- Stack: anche lo stack è variabile. Questa volta però, non è il programmatore che lo modifica, ma il sistema operativo. Serve a contenere le variabili non globali (es: delle funzioni) e tutte le operazioni delle chiamate a funzioni. Se si rende necessario, il sistema operativo può diminuire lo spazio per la heap per utilizzarlo per lo stack. Questa operazione causa malfunzionamenti del programma perché il programmatore aveva previsto di allocare x dati e se ne trova allocati di meno.
In qualunque caso, la memoria di un programma è sempre limitata. Se un programma cerca di evadere dalla sua zona per osservare i dati di un altro processo o guadagnare maggiore memoria viene bloccato e il programma andrà in segmentation fault, causandone la terminazione.