Definizione: processo

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.

Osservazione: 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:

CaratteristicaProgrammaProcesso
DefinizioneUn insieme di istruzioni memorizzate su discoUn’istanza di un programma in esecuzione
StatoEntità passivaEntità attiva
MemoriaNon usa memoria attivaUtilizza memoria (segmento di testo e dei dati, stack e heap)
EsecuzioneNon è in esecuzioneIn esecuzione
DurataPermanente fino a quando è cancellato o modificatoTemporanea, esiste solo durante l’esecuzione
GestioneNon richiede gestione attivaGestito dal sistema operativo (CPU, memoria, I/O)
RelazioneUn programma può generare più processiOgni processo è basato su un programma

Osservazione: più processi per lo stesso programma

Sebbene due processi siano associabili allo stesso programma, sono tuttavia da considerare due sequenze d’esecuzione distinte. Per esempio, l’utente può aprire più finestre dello stesso browser e, di conseguenza, generare più istanze dello stesso programma: ciascuna istanza è 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.

1 - Il PCB e le informazioni sul processo

Definizione: blocco di controllo del processo (PCB)

Il blocco di controllo del processo (in inglese PCB, Process Control Block) è una struttura dati fondamentale utilizzata dal sistema operativo per memorizzare nella RAM tutte le informazioni relative a un processo dal momento della sua inizializzazione e aggiornato durante l’intero ciclo di vita del processo.

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 valore contenuto nel Program Counter della CPU nel momento in cui il processo è stato interrotto, per consentire di riprendere poi l’esecuzione correttamente dall’ultima istruzione eseguita prima dell’interruzione stessa. Non viene aggiornato di continuo, ma solo nel momento in cui si effettua il cambio di contesto, perché altrimenti sarebbe inutile e poco efficiente tenerlo sincronizzato.
  • Stato dei registri della CPU: i valori contenuti in alcuni registri della CPU di uso comune (es. accumulatori, puntatori allo stack, registri di uso generale) nel momento in cui il processo è stato interrotto. Come per il Program Counter, non vengono aggiornati di continuo, ma solo nel momento in cui si effettua il cambio di contesto, perché altrimenti sarebbe inutile e poco efficiente tenere tutti i valori sincronizzati.
  • Informazioni sulla gestione della memoria: 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 dei processi, 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: un 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).

1.1 - Stato di un processo

Definizione: 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 esistenza nel sistema operativo. Serve a gestire i processi in modo efficiente, garantendo che la CPU venga utilizzata in modo ottimale senza che rimanga inattiva: per esempio, quando un processo è in attesa, il sistema operativo può assegnare la CPU a un altro processo già pronto per essere eseguito.

Gli stati principali di un processo sono new, ready, running, waiting e terminated.

Attenzione: gli stati potrebbero cambiare nei vari sistemi operativi

A seconda del sistema operativo, i nomi degli stati potrebbero variare o, in alcuni casi, potrebbero esserci ulteriori distinzioni tra i vari stati, oltre ai soliti 5 (new, ready, running, waiting e terminated).

Vediamo ora la definizione completa di ogni stato.

Definizione: stato new (nuovo)

Un processo entra nello stato new (nuovo) non appena viene creato e ci rimane finché non sarà pronto per essere eseguito. Durante questa fase di inizializzazione vengono assegnate risorse come memoria e PID.

Definizione: stato ready (pronto)

Un processo entra nello stato ready (pronto) quando è pronto per essere eseguito ma sta aspettando che la CPU diventi disponibile perché è occupata da altri processi.

Definizione: stato running (in esecuzione)

Un processo è nello stato running (in esecuzione) quando utilizza la CPU e le sue istruzioni vengono eseguite.

Definizione: stato waiting (in attesa)

Un processo è nello stato waiting (in attesa) (o anche blocked (bloccato) oppure sleeping (dormiente)) quando è in attesa di un evento esterno per continuare l’esecuzione, come il completamento di un’operazione di input/output (I/O), la ricezione di dati o un messaggio.

Definizione: stato terminated (terminato)

Un processo entra nello stato terminated (terminato) quando 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 operativo.

1.1.1 - 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 STATDescrizione
SSleeping: 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.
RRunning: il processo è nello stato running e si trova all’interno della coda dei processi pronti pronto per essere eseguito o già in esecuzione.
DDisk 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.
TStopped: il processo è nello stato blocked, messo in pausa da un segnale.
ZZombie: il processo è nello stato zombie, ossia è terminato ma è in attesa di una wait() dal processo padre.
NNice: il processo è in esecuzione con bassa priorità.
WWaiting for paging: il processo è in attesa che le pagine di memoria vengano scritte o lette dal disco (raro nei moderni sistemi Linux).
sSession 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.
lIl processo è multithreaded (usato su alcuni sistemi come Solaris e Linux).
<Il processo ha una priorità alta, quindi riceve più CPU rispetto ad altri.

1.2 - Identificatore del processo (PID)

Definizione: identificatore del processo (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.

1.2.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.

1.3 - Spazio di memoria

Definizione: spazio di memoria di un processo

Lo spazio di memoria di un processo rappresenta l’insieme di tutte le aree di memoria allocate per un processo dal sistema operativo e 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:

  1. 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.
  2. 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.
  1. 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).

  2. 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:

Spazio di memoria di un processo.png

1.3.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.

6 - 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 PID 1.

Fonti

  • Abraham Silberschatz, Peter Baer Galvin, Greg Gagne - Sistemi Operativi (10ᵃ Edizione) - Pearson, 2019 - ISBN: 9788891904560.
    • Capitolo 3: processi
    • Capitolo 4: thread
    • Capitolo 5: schedulazione della CPU
  • 🏫 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:
  • 🏫 Appunti di Carlos Palomino del corso di Sistemi Operativi, Corso di Laurea in Informatica presso l’Università di Torino, A.A. 2024-25 (caricati sul repository GitHub del Team Studentesco Informatica):