Il processo è un’istanza di un programma in esecuzione su un computer e rappresenta una delle unità fondamentali di gestione delle risorse da parte di un sistema operativo. Quando un programma viene eseguito, il sistema operativo crea un processo per esso, fornendogli le risorse necessarie, come CPU, memoria, accesso ai file e altri dispositivi.
Può essere visibile all’utente, come nel caso di un’applicazione durante la sua esecuzione, oppure può essere eseguito in background; per visualizzare la lista completa dei processi eseguiti su un computer e le relative risorse impiegate è possibile utilizzare un software comunemente chiamato task manager, mentre la gestione dei processi da parte del sistema operativo è affidata a un particolare programma, detto process scheduler, attraverso opportuni algoritmi di schedulazione.
1 - Differenza tra processo e programma
La differenza tra il concetto di processo e quello di programma è fondamentale in informatica, poiché indicano concetti distinti legati all’esecuzione di un’applicazione:
Un programma è un insieme di istruzioni scritte in un linguaggio di programmazione, che descrive quali operazioni un computer deve eseguire. È un’entità passiva e rappresenta il codice sorgente o il codice binario di un’applicazione, che viene memorizzato in un file su disco (es. un file eseguibile .exe su Windows, oppure un documento contenente codice sorgente scritto in un linguaggio di programmazione come Python o C). Un programma non fa nulla da solo finché non viene avviato.
Un processo è un’istanza di un programma in esecuzione sulla memoria centrale (RAM). È un’entità attiva che include il codice del programma, lo stato della CPU (come il contatore del programma e i registri), lo spazio di memoria (segmento di testo e dei dati, stack e heap) e le risorse utilizzate dal programma. Quando un programma viene eseguito, il sistema operativo crea un processo per gestirne l’esecuzione.
Ecco una tabella riassuntiva:
Caratteristica
Programma
Processo
Definizione
Un insieme di istruzioni memorizzate su disco
Un’istanza di un programma in esecuzione
Stato
Entità passiva
Entità attiva
Memoria
Non usa memoria attiva
Utilizza memoria (segmento di testo e dei dati, stack e heap)
Esecuzione
Non è in esecuzione
In esecuzione
Durata
Permanente fino a quando è cancellato o modificato
Temporanea, esiste solo durante l’esecuzione
Gestione
Non richiede gestione attiva
Gestito dal sistema operativo (CPU, memoria, I/O)
Relazione
Un programma può generare più processi
Ogni processo è basato su un programma
1.1 - Multipli processi, stesso programma
Sebbene due processi siano associabili allo stesso programma, sono tuttavia da considerare due sequenze d’esecuzione distinte: alcuni utenti possono, per esempio, far eseguire diverse istanze dello stesso programma di posta elettronica, così come un utente può invocare più istanze dello stesso browser. Ciascuna di queste è un diverso processo e, benché le sezioni di memoria contenenti il codice siano equivalenti, in realtà quelle dei dati, dell’heap e dello stack sono diverse. È inoltre usuale che durante la propria esecuzione un processo generi altri processi.
2 - Il PCB e le informazioni sul processo
Il blocco di controllo del processo (in inglese PCB, Process Control Block) è una struttura dati fondamentale utilizzata dal sistema operativo per memorizzare tutte le informazioni relative a un processo in esecuzione. Il PCB contiene informazioni cruciali per la gestione del processo, come il suo stato, l’allocazione delle risorse, la sua posizione nella memoria e molto altro. Ogni processo attivo nel sistema ha un PCB associato, che viene creato al momento della sua inizializzazione, memorizzato nella RAM e aggiornato durante l’intero ciclo di vita del processo.
2.1 - Informazioni contenute in un PCB
Le informazioni contenute in un PCB variano a seconda delle implementazioni nei vari sistemi operativi, ma in generale sono presenti:
Stato del processo: la condizione in cui si trova un processo in un dato momento durante la sua esecuzione nel sistema operativo (new, ready, running, waiting o terminated).
Identificatore del processo (in inglese PID, Process ID): un numero intero univoco assegnato al processo dal sistema operativo. Questo identificatore viene utilizzato per distinguere il processo dagli altri processi attivi.
Program Counter (abbreviato in PC): il PCB salva il valore contenuto nel Program Counter, il registro nella CPU che rappresenta l’indirizzo dell’istruzione successiva che il processo deve eseguire. Se il processo viene interrotto, il Program Counter consente di riprendere l’esecuzione dal punto in cui è stata interrotta, perché appunto conterrà il valore dell’indirizzo di memoria in cui è presente l’istruzione del processo da cui ripartire.
Contenuto dei registri della CPU: il PCB salva il contenuto dei registri della CPU (es. accumulatori, puntatori allo stack, registri di uso generale) nel momento in cui il processo è stato interrotto. Questo è essenziale per riprendere correttamente l’esecuzione del processo in un successivo cambio di contesto.
Informazioni sulla gestione della memoria: include dati su come la memoria è allocata al processo, come indirizzi di base e limiti di memoria del processo, oppure tabelle di paginazione o segmentazione, nel caso il sistema operativo utilizzi tecniche di gestione della memoria virtuale.
Informazioni di I/O: un elenco dei dispositivi di input/output utilizzati dal processo (es. file aperti, dispositivi hardware come stampanti, reti) e file descriptor che fanno riferimento ai file aperti dal processo.
Informazioni di schedulazione: un elenco delle informazioni utili alla schedulazione della CPU, come:
Priorità del processo: indica l’importanza relativa del processo rispetto agli altri.
Tempo di esecuzione accumulato: il tempo che il processo ha trascorso in esecuzione.
Algoritmo di schedulazione utilizzato: se il sistema operativo utilizza diverse politiche di schedulazione, nel PCB viene specificata quale viene usata.
Informazioni di accounting: dati utilizzati per tenere traccia del tempo CPU consumato dal processo, della quantità di risorse utilizzate e di eventuali statistiche di utilizzo, utili per la fatturazione o il monitoraggio delle prestazioni.
Informazioni sui segnali: elenco dei segnali che il processo può ricevere e le relative azioni da eseguire al ricevimento di un segnale (ad esempio, terminare il processo o ignorare il segnale).
2.2 - Funzione del PCB
Il PCB svolge un ruolo cruciale nel gestire i processi nel sistema operativo. Le sue principali funzioni includono:
Salvataggio del contesto del processo: quando un processo viene sospeso (ad esempio, per un cambio di contesto), il suo stato (registri CPU, Program Counter, ecc.) viene memorizzato nel PCB. Quando il processo viene ripreso, il contesto viene ripristinato.
Gestione della memoria: il PCB tiene traccia delle risorse di memoria utilizzate dal processo, assicurando che il processo non acceda a zone di memoria riservate ad altri processi o al sistema operativo.
Identificazione del processo: grazie al PID e alle altre informazioni contenute nel PCB, il sistema operativo può gestire un numero elevato di processi contemporaneamente, distinguendo un processo dall’altro.
Schedulazione e gestione della priorità: le informazioni di priorità e di tempo di esecuzione accumulate nel PCB consentono al sistema operativo di decidere quale processo eseguire in un dato momento, ottimizzando l’uso della CPU.
2.3 - Stato di un processo
Lo stato di un processo rappresenta la condizione in cui si trova un processo in un dato momento durante la sua esecuzione nel sistema operativo. Il sistema operativo utilizza questi stati per gestire i processi in modo efficiente, garantendo che la CPU venga utilizzata in modo ottimale.
Gli stati principali di un processo sono:
Stato new (nuovo): il processo è appena stato creato, ma non è ancora pronto per essere eseguito. Si trova in fase di inizializzazione, durante la quale vengono assegnate risorse come memoria e PID.
Stato ready (pronto): il processo è pronto per essere eseguito ma sta aspettando che la CPU diventi disponibile. In questo stato, il processo ha tutto ciò di cui ha bisogno per essere eseguito (memoria, dati, risorse), ma la CPU è attualmente occupata con un altro processo.
Stato running (in esecuzione): il processo sta attualmente utilizzando la CPU e le sue istruzioni vengono eseguite. Il sistema operativo ha assegnato il controllo della CPU al processo, che sta attivamente svolgendo il suo lavoro.
Stato waiting, blocked o sleeping (in attesa, bloccato o dormiente): il processo è in attesa di un evento esterno per continuare l’esecuzione. Questo evento può essere, ad esempio, il completamento di un’operazione di input/output (I/O), la ricezione di dati o un messaggio. In questo stato, il processo non può proseguire fino a che l’evento atteso non si verifica.
Stato terminated (terminato): il processo ha completato la sua esecuzione o è stato interrotto in modo anomalo (per esempio, a causa di un errore). In questo stato, tutte le risorse del processo vengono rilasciate e il processo non esiste più nel sistema.
Stato zombie (zombie): il processo ha terminato la sua esecuzione, ma il suo PID e alcune informazioni di stato rimangono in attesa che il processo padre ne legga il codice di uscita tramite la funzione wait(), liberando così completamente le risorse associate. Se il processo padre non gestisce correttamente il figlio terminato, gli zombie possono accumularsi e consumare risorse di sistema.
Questi termini sono piuttosto arbitrari e potrebbero variare a seconda del sistema operativo. Gli stati che rappresentano sono in ogni modo presenti in tutti i sistemi, anche se alcuni sistemi operativi introducono ulteriori distinzioni tra di essi. È importante capire che in ciascuna unità d’elaborazione può essere in esecuzione solo un processo per volta, sebbene molti processi possano essere pronti o nello stato di attesa.
2.3.2 - Utilità degli stati
La gestione degli stati permette al sistema operativo di organizzare l’esecuzione dei processi in modo efficiente: ad esempio, quando un processo è in attesa (waiting), il sistema operativo può assegnare la CPU a un altro processo nello stato ready, garantendo che la CPU non rimanga inattiva.
2.3.3 - Visualizzazione degli stati correnti dei processi
In UNIX, tramite il comando ps -axjf si può avere una visualizzazione ad albero della lista di tutti i processi presenti nel sistema (ovverosia presenti nella coda dei processi) e, nella colonna STAT, ogni codice è associato a uno stato o una caratteristica del processo ed essi possono essere combinati tra loro:
Codice STAT
Descrizione
S
Sleeping: il processo è nello stato sleeping e si trova all’interno della coda dei processi bloccati in attesa che accada un evento, come un segnale o che un input diventi disponibile.
Disk sleep: il processo è in uno stato sleeping non interrompibile e si trova all’interno della coda dei processi bloccati in attesa che un input o output diventi disponibile.
Zombie: il processo è nello stato zombie, ossia è terminato ma è in attesa di una wait() dal processo padre.
N
Nice: il processo è in esecuzione con bassa priorità.
W
Waiting for paging: il processo è in attesa che le pagine di memoria vengano scritte o lette dal disco (raro nei moderni sistemi Linux).
s
Session leader: il processo è un leader di sessione (es. una shell che avvia altri processi).
+
Il processo appartiene al gruppo di processi in esecuzione in primo piano (foreground) nel terminale.
l
Il processo è multithreaded (usato su alcuni sistemi come Solaris e Linux).
<
Il processo ha una priorità alta, quindi riceve più CPU rispetto ad altri.
2.4 - Process ID (PID)
L’identificatore del processo (in inglese PID, Process ID) è un numero intero univoco assegnato al processo dal sistema operativo. Questo identificatore viene utilizzato per distinguere il processo dagli altri processi attivi all’interno del kernel.
2.4.1 - Ottenere i PID tramite getpid() e getppid()
La funzione getpid() è una chiamata di sistema utilizzata per ottenere il PID del processo che la usa.
Il suo prototipo è il seguente:
#include <unistd.h>pid_t getpid();
dove:
pid_t restituito: PID del processo che ha invocato la funzione.
La funzione getppid() è una chiamata di sistema utilizzata per ottenere il PID del processo padre di quello che la usa.
Il suo prototipo è il seguente:
#include <unistd.h>ppid_t getppid();
dove:
ppid_t restituito: PID del processo padre del processo che ha invocato la funzione.
Le relazioni tra processi costituiscono una struttura ad albero. Il genitore di ogni processo ha a sua volta un genitore, fino ad arrivare alla radice: il processo init.
Esempio: visualizzazione dei PID del figlio e del padre
Ecco un semplice codice in C per visualizzare il PID del processo che sta eseguendo questo programma (il figlio) e quello del suo processo padre (ossia il processo del terminale che ha eseguito il programma):
#include <stdio.h>#include <unistd.h>int main(int argc, char *argv[]) { printf("Id del processo: %d\n", getpid()); printf("Id del processo padre: %d\n", getppid());}
2.5 - Spazio di memoria
Lo spazio di memoria di un processo rappresenta l’insieme di tutte le aree di memoria allocate per un processo dal sistema operativo. Questo spazio contiene il codice eseguibile, i dati e le strutture necessarie per far funzionare il programma. Ogni processo ha il proprio spazio di memoria isolato, separato da quello degli altri processi, garantendo che un processo non possa accedere o modificare direttamente la memoria di un altro processo: questa separazione migliora la sicurezza e la stabilità del sistema.
Lo spazio di memoria di un processo è solitamente diviso in:
Segmento di codice (o segmento testo): contiene il codice eseguibile del programma, cioè le istruzioni che la CPU eseguirà. Il segmento di codice è generalmente di sola lettura, per evitare che il codice del programma venga accidentalmente modificato.
Segmento dati: contiene le variabili globali e statiche del programma, divise in:
Dati inizializzati: l’insieme delle variabili globali e statiche a cui è stato assegnato un valore al momento della dichiarazione.
Dati non inizializzati (in inglese BSS, Block Started by Symbol): l’insieme delle variabili globali e statiche che sono dichiarate ma non esplicitamente inizializzate, quindi non hanno un valore inizialmente definito; in alcuni sistemi operativi vengono inizializzate a zero.
Heap: è l’area della memoria dinamica utilizzata per l’allocazione di memoria a runtime. Quando un programma richiede memoria dinamica (ad esempio tramite funzioni come malloc() in C o new in C++/Java), questa viene allocata nello heap, il quale viene gestito unicamente dal programmatore stesso (cioè il programmatore è l’unico responsabile della gestione e del rilascio della memoria).
Stack: area di memoria che viene utilizzata per memorizzare variabili locali, parametri di funzione e indirizzi di ritorno da funzioni. Ogni volta che viene chiamata una funzione, un nuovo “frame” viene aggiunto allo stack, che contiene le informazioni relative a quella particolare chiamata. Quando la funzione termina, lo stack viene “srotolato” e il frame della funzione viene rimosso. Lo stack si espande e si contrae automaticamente durante l’esecuzione del programma, e la sua gestione è di solito curata dal compilatore e dal sistema operativo.
Ecco una rappresentazione grafica dello spazio di memoria di un processo:
2.5.1 - Variazione della grandezza dei segmenti di memoria
Si può notare che le dimensioni dei segmenti di codice e di dati sono fisse, ovvero non cambiano durante l’esecuzione del programma, mentre le dimensioni di stack e heap possono ridursi e crescere dinamicamente durante l’esecuzione:
Ogni volta che si chiama una funzione, viene inserito nello stack una struttura dati detta record di attivazione (in inglese activation record) contenente i suoi parametri, le variabili locali e l’indirizzo di ritorno; quando la funzione restituisce il controllo al chiamante, il record di attivazione viene rimosso dallo stack.
Allo stesso modo, l’heap crescerà quando viene allocata memoria dinamicamente e si ridurrà quando la memoria viene restituita al sistema.
Visto che le sezioni dello stack e dell’heap crescono l’una verso l’altra, tocca al sistema operativo garantire che non si sovrappongano.
3 - Schedulazione dei processi
La schedulazione dei processi è il meccanismo attraverso il quale il sistema operativo decide quale processo deve essere eseguito dalla CPU in un dato momento. In un sistema multitasking, più processi competono per l’utilizzo della CPU ed esiste un programma specifico (detto process scheduler) il cui ruolo è quello di assegnare la CPU a questi processi in modo efficiente, garantendo che il sistema risponda correttamente alle richieste degli utenti e ottimizzi le prestazioni.
3.1 - Obiettivi della schedulazione dei processi
Gli obiettivi della schedulazione dei processi sono:
Massimizzare l’utilizzo della CPU: ridurre i tempi morti della CPU, assicurando che sia sempre in uso.
Condivisione equa: assicurare che tutti i processi abbiano la possibilità di essere eseguiti, evitando che alcuni processi monopolizzino le risorse.
Ridurre i tempi di risposta: ridurre il tempo che intercorre tra la richiesta di esecuzione di un processo e l’inizio della sua esecuzione.
Massimizzare la produttività: aumentare il numero di processi completati in un intervallo di tempo.
Ridurre il tempo di attesa: minimizzare il tempo che un processo trascorre in attesa di essere eseguito.
Garantire scadenze: nei sistemi in tempo reale, garantire che i processi rispettino le loro scadenze temporali.
3.2 - Code di schedulazione
Le code di schedulazione (in inglese scheduling queues) sono strutture di dati utilizzate dal sistema operativo per gestire i processi in diverse fasi del loro ciclo di vita. Queste code aiutano il sistema operativo a organizzare e determinare quale processo deve essere eseguito, aspettare o bloccarsi, basandosi su determinati algoritmi di schedulazione.
3.2.1 - Principali code di schedulazione
Le principali code di schedulazione sono:
Coda dei processi (job queue): contiene tutti i processi nel sistema, indipendentemente dallo stato in cui si trovano. Non appena un processo viene creato, viene aggiunto a questa coda.
Coda dei processi pronti (ready queue o run queue): contiene tutti i processi che sono pronti per l’esecuzione (cioè nello stato ready) e in attesa che la CPU sia disponibile. Lo schedulatore a breve termine sceglie i processi da questa coda per essere eseguiti. Questa coda è una delle più attive e importanti, poiché determina quali processi avranno accesso alla CPU.
Coda dei dispositivi (device queue): ogni dispositivo di I/O nel sistema ha una propria coda. Quando un processo richiede un’operazione di I/O, entra nella coda del dispositivo corrispondente e rimane bloccato finché l’operazione non viene completata. Dopo che l’I/O è completato, il processo può ritornare nella coda dei processi pronti. Per esempio, se un processo sta aspettando che venga completata la lettura da disco, sarà nella coda del disco fino al termine dell’operazione.
Coda dei processi bloccati (waiting queue): contiene processi che sono bloccati in attesa di un evento (cioè nello stato waiting), come l’input da un dispositivo o il completamento di una richiesta di I/O. Quando l’evento atteso si verifica, il processo ritorna nella ready queue. È simile alla device queue, ma può anche includere processi in attesa di altri tipi di eventi non legati ai dispositivi di I/O.
3.2.2 - Rappresentazione in memoria delle code di schedulazione
Ogni coda generalmente viene memorizzata come una lista concatenata, con un’intestazione della coda contenente i puntatori al primo PCB della lista che a sua volta comprende un campo puntatore che indica il successivo processo contenuto nella coda e così via:
3.3 - Tipi di schedulazione
Esistono diversi tipi di schedulazione, che variano in base a quando e come viene assegnata la CPU ai processi:
Schedulazione a lungo termine (in inglese job scheduler): decide quali processi devono essere caricati in memoria principale (RAM) dalla coda dei processi. Il suo scopo è quello di controllare il grado di multiprogrammazione, cioè il numero di processi che possono essere mantenuti in memoria e pronti per essere eseguiti contemporaneamente.
Schedulazione a medio termine (in inglese swapper scheduler): è responsabile del cosiddetto swapping dei processi, cioè dello spostamento temporaneo di processi in stato waiting dalla RAM a una memoria secondaria (per esempio su disco) per liberare memoria e fare spazio ad altri processi.
Schedulazione a breve termine (in inglese CPU scheduler): è l’unica schedulazione realmente necessaria in un sistema operativo, poiché decide quale processo tra quelli nello stato ready dovrà essere eseguito dalla CPU attraverso l’uso di diversi algoritmi di schedulazione. Questo processo avviene molto frequentemente (nell’ordine dei millisecondi).
3.4 - Il cambio di contesto
Il cambio di contesto (in inglese context switch) è l’operazione che il sistema operativo esegue per sospendere l’esecuzione di un processo attualmente in corso e passare l’esecuzione ad un altro processo. Questa operazione è essenziale per implementare il multitasking, ovvero la capacità di un sistema di eseguire più processi quasi simultaneamente, facendo in modo che ciascun processo ottenga la CPU a turno.
3.4.1 - Utilità e vantaggi del cambio di contesto
Il cambio di contesto serve principalmente per:
Multitasking: dovendo gestire più processi quasi simultaneamente, la CPU esegue ogni processo per un breve intervallo di tempo e poi passa a un altro processo, servendosi proprio del cambio di contesto e garantendo che ogni processo ottenga una porzione del tempo della CPU utile alla sua esecuzione.
Prevenzione del monopolio della CPU: in un sistema operativo multitasking, il cambio di contesto permette di sospendere temporaneamente un processo e riprendere l’esecuzione di un altro, assicurando che nessun processo monopolizzi la CPU. Questo permette a più programmi di essere eseguiti nello stesso momento senza che uno blocchi l’altro.
Gestione dei processi bloccati: quando un processo è bloccato (cioè è nello stato waiting, per esempio in attesa di un’operazione di I/O), il cambio di contesto consente alla CPU di passare immediatamente a un altro processo pronto per essere eseguito, ottimizzando l’utilizzo delle risorse.
Risposta a eventi esterni: il cambio di contesto può essere innescato da un interrupt (ad esempio, l’arrivo di un input da tastiera o il completamento di un’operazione di I/O) che richiede l’attenzione del sistema operativo. Quando si verifica un evento esterno, la CPU può eseguire il cambio di contesto per gestire l’evento e successivamente tornare al processo precedente.
Supporto alla concorrenza: nei sistemi multiprocessore o multithread, il cambio di contesto consente a diversi thread o processi di essere eseguiti contemporaneamente o in sequenza sulla stessa CPU, migliorando la concorrenza tra le operazioni.
Ottimizzazione del sistema: permette di bilanciare O-bound (che richiedono più operazioni di I/O) e processi CPU-bound (che utilizzano intensivamente la CPU), migliorando le prestazioni complessive del sistema.
3.4.2 - Come funziona il cambio di contesto
Le fasi principali di un cambio di contesto sono:
Salvataggio dello stato del processo corrente: il sistema operativo salva lo stato corrente del processo (o thread) in esecuzione, inclusi i valori dei registri della CPU, il valore del Program Counter che tiene traccia dell’istruzione successiva da eseguire, lo stato di un processo e le altre informazioni necessarie per riprendere l’esecuzione in futuro. Queste informazioni sono memorizzate nel PCB del processo.
Caricamento del nuovo contesto: il sistema operativo carica il contesto del prossimo processo da eseguire dal suo corrispettivo PCB, ripristinando i suoi valori dei registri della CPU e del Program Counter, quindi la CPU riprende l’esecuzione del nuovo processo da dove era stato interrotto l’ultima volta.
Ripresa dell’esecuzione: la CPU riprende l’esecuzione del nuovo processo dal punto in cui era stato sospeso.
Ecco un diagramma che illustra il cambio di contesto tra due processi P0 e P1:
3.4.3 - Costi del cambio di contesto
Il cambio di contesto è un’operazione che ha un costo in termini di tempo e risorse. Durante il tempo necessario per il cambio di contesto, nessun processo utente viene effettivamente eseguito: il cambio di contesto è puro overhead, perché il sistema esegue solo operazioni volte alla gestione dei processi e non alla computazione.
Il tempo necessario varia da sistema a sistema, dipendendo dalla velocità della memoria, dal numero di registri da copiare e dall’esistenza di istruzioni macchina appropriate (per esempio, una singola istruzione per caricare o trasferire in memoria tutti i registri). In genere si tratta di qualche millisecondo di computazione, ma la frequenza del cambio di contesto deve essere ottimizzata per evitare che un eccessivo numero di cambi riduca l’efficienza del sistema.
3.5 - Parametri di performance della schedulazione
Alcuni criteri utilizzati per valutare le prestazioni di un algoritmo di schedulazione includono:
Tempo di attesa: il tempo che un processo trascorre nella coda dei processi pronti prima di essere eseguito.
Tempo di turnaround: il tempo totale che un processo impiega dal momento in cui viene creato fino a quando termina (inclusi i tempi di attesa e di esecuzione).
Tempo di risposta: il tempo che intercorre tra la creazione di un processo e il primo istante in cui il processo inizia a essere eseguito.
Throughput: il numero di processi completati in un dato intervallo di tempo.
4 - Processi I/O-bound e CPU-bound
I concetti di processi I/O-bound e CPU-bound si riferiscono al tipo di risorse su cui un processo è più dipendente durante la sua esecuzione. La distinzione tra questi due tipi di processi è fondamentale per l’ottimizzazione delle prestazioni dei sistemi operativi e per la schedulazione dei processi.
4.1 - Processo I/O-bound
Un processo I/O-bound è un processo che passa la maggior parte del suo tempo in attesa di operazioni di I/O, come la lettura da o la scrittura su un disco, l’attesa di dati dalla rete, o l’interazione con dispositivi come tastiere e stampanti. Il tempo di esecuzione sulla CPU è relativamente breve rispetto al tempo trascorso in attesa di I/O.
4.1.1 - Caratteristiche principali dei processi I/O-bound
Le caratteristiche principali dei processi I/O-bound sono:
Uso intenso di I/O: questi processi effettuano molte operazioni di input e output e, quindi, tendono a essere spesso bloccati (quindi nello stato waiting) fino a quando le operazioni di I/O non sono completate.
Breve tempo di utilizzo della CPU: quando il processo ottiene il controllo della CPU, tende a eseguire solo un piccolo numero di calcoli prima di essere nuovamente bloccato per attendere l’I/O (ad esempio, un programma che attende frequenti input da parte dell’utente, come un editor di testo o un’applicazione interattiva, oppure un processo che trasferisce grandi quantità di dati da o verso un disco rigido).
Obiettivo della schedulazione: i processi I/O-bound beneficiano di una schedulazione che li rimuova rapidamente dallo stato di attesa (quando l’I/O è completato) e gli assegni prontamente la CPU, per minimizzare i tempi di attesa e garantire che le operazioni di I/O siano gestite efficientemente.
4.2 - Processo CPU-bound
Un processo CPU-bound è un processo che trascorre la maggior parte del suo tempo effettuando calcoli sulla CPU e richiede relativamente poche operazioni di I/O. Il tempo speso in attesa di operazioni di I/O è trascurabile rispetto al tempo di utilizzo della CPU.
4.2.1 - Caratteristiche principali dei processi CPU-bound
Le caratteristiche principali dei processi CPU-bound sono:
Uso intenso della CPU: Questi processi richiedono molto tempo di CPU per eseguire i loro calcoli e rimangono in esecuzione per lunghi periodi senza essere bloccati per operazioni di I/O.
Basso utilizzo di I/O: I processi CPU-bound raramente richiedono accesso a dispositivi di input e output, quindi tendono a non essere frequentemente bloccati (es. un programma di elaborazione scientifica o di simulazione che esegue complessi calcoli matematici, oppure un software che genera immagini grafiche complesse o esegue algoritmi di compressione dati).
Obiettivo della schedulazione: i processi CPU-bound possono richiedere quanti di tempo più lunghi, poiché tendono a utilizzare la CPU per periodi prolungati prima di completare il loro lavoro o di cedere la CPU.
4.3 - Differenze principali tra i due tipi
Ecco una tabella riassuntiva delle differenze principali tra i due tipi di processi:
Caratteristica
I/O-bound
CPU-bound
Uso principale
Predomina l’attesa per operazioni di I/O
Predomina l’uso intensivo della CPU per calcoli
Tempo in attesa
Passa molto tempo in attesa di I/O (stato waiting)
Passa la maggior parte del tempo eseguendo calcoli
Tempo sulla CPU
Utilizza la CPU solo per brevi periodi
Utilizza la CPU per lunghi periodi senza interruzioni
Esempi
Editor di testo, programmi di rete, trasferimenti dati
Elaborazione scientifica, rendering grafico, compressione dati
Schedulazione
Richiede una gestione rapida dell’I/O
Richiede tempi più lunghi di utilizzo della CPU
4.4 - Importanza per la schedulazione
Il sistema operativo deve tenere conto del tipo di processo (I/O-bound o CPU-bound) durante la schedulazione per ottimizzare le prestazioni del sistema:
Processi I/O-bound: vengono solitamente eseguiti con maggiore priorità o con quanti di tempo più brevi per evitare che rimangano inutilmente in attesa di operazioni I/O completate.
Processi CPU-bound: possono essere eseguiti con quanti di tempo più lunghi per evitare frequenti cambi di contesto e overhead associati, poiché tendono a utilizzare la CPU per periodi più estesi.
5 - Comunicazione tra processi (IPC)
Spesso, i processi hanno bisogno di comunicare e collaborare tra di loro. La comunicazione tra processi (in inglese IPC, Inter-Process Communication) riguarda le tecniche e i meccanismi attraverso cui i processi di un sistema operativo, che possono essere eseguiti in parallelo o separatamente, scambiano informazioni tra loro.
Le principali tecniche di comunicazione tra processi sono:
Pipe: permettono la comunicazione unidirezionale tra processi, tipicamente tra un processo padre e i suoi processi figli, trasferendo dati in modo sequenziale.
Code di messaggi: consentono a più processi di scambiarsi informazioni tramite messaggi strutturati, inviati a una coda condivisa; questa tecnica è particolarmente adatta per la comunicazione asincrona.
Memoria condivisa: permette a più processi di accedere a un’area di memoria comune per scambiare dati in modo molto rapido, anche se richiede un sistema di sincronizzazione per evitare conflitti di accesso.
Socket: utilizzati per la comunicazione tra processi su sistemi diversi (o anche sullo stesso sistema), sfruttano la rete per il trasferimento di dati e sono alla base della comunicazione in rete, sia locale che remota.
Chiamate di procedure remote (RPC): consentono a un processo di richiedere l’esecuzione di una funzione su un sistema remoto, come se fosse locale, semplificando la programmazione distribuita nascondendo i dettagli della comunicazione di rete.
6 - Multitasking
Il multitasking è la capacità di un sistema operativo (o di un software) di eseguire più processi contemporaneamente. Anche se il termine suggerisce che più compiti vengono svolti simultaneamente, in molti casi si tratta in realtà di una rapida alternanza tra diversi processi, dando all’utente l’impressione che tutti stiano avanzando nello stesso momento.
In un ambiente multitasking, il sistema operativo suddivide il tempo del processore tra più processi. Ogni processo ottiene una piccola quantità di tempo per eseguire le sue operazioni. Anche se i processi non vengono eseguiti tutti contemporaneamente, il cambio rapido tra di essi è così veloce che l’utente ha l’impressione di simultaneità.
6.1 - Vantaggi e svantaggi del multitasking
I principali vantaggi del multitasking sono:
Miglior utilizzo della CPU: sfrutta al massimo la capacità del processore eseguendo diverse attività, riducendo i tempi di inattività della CPU.
Efficienza e produttività: permette di eseguire più operazioni allo stesso tempo, migliorando la produttività, ad esempio facendo girare un programma in background mentre si lavora su un altro.
Esperienza utente migliorata: in applicazioni con interfaccia grafica (GUI), il multitasking permette agli utenti di continuare a utilizzare l’applicazione mentre altre operazioni vengono completate in background, mantenendo la reattività.
Condivisione delle risorse: i processi possono condividere risorse come la memoria, riducendo il consumo complessivo e ottimizzando l’uso delle risorse di sistema.
Flessibilità: facilita l’esecuzione di diversi tipi di attività su un unico dispositivo, come navigare su internet mentre si ascolta musica o si scaricano file.
I principali svantaggi del multitasking sono:
Overhead del sistema: cambiare rapidamente da un processo all’altro (ossia effettuare un cambio di contesto) richiede risorse di sistema e può rallentare le prestazioni generali.
Rischio di collisione: con più processi che accedono alle stesse risorse, come la memoria, può essere necessario gestire i conflitti di risorse e sincronizzare i processi, aumentando la complessità.
Maggiore complessità e possibilità di errori: la gestione di più processi o thread richiede un controllo avanzato e può portare a errori difficili da identificare, come deadlock o race conditions.
Riduzione delle prestazioni su sistemi limitati: su sistemi con meno risorse, il multitasking può ridurre l’efficienza complessiva, creando un sovraccarico che rallenta il sistema.
Problemi di sicurezza: se i processi non sono ben isolati, un processo può influenzare o interferire con altri, creando vulnerabilità e rischi per la sicurezza.
7 - Processo init in UNIX
In UNIX, il processo init è il primo processo che il kernel manda in esecuzione dopo che il computer ha terminato la fase di bootstrap ed è il processo padre di tutti gli altri processi del sistema. Si occupa dell’inizializzazione del sistema, della gestione degli stati di avvio e dello spegnimento del sistema e ha tipicamente PID1.
🏫 Lezioni e slide del Prof. Aldinucci Marco del corso di Sistemi Operativi (canale B), Corso di Laurea in Informatica presso l’Università di Torino, A.A. 2024-25:
🏫 Lezioni e slide del Prof. Schifanella Claudio del corso di Laboratorio di Sistemi Operativi (canale B, turno T4), Corso di Laurea in Informatica presso l’Università di Torino, A.A. 2024-25: