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

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.

2 - Schedulazione dei processi

Definizione: 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 tra di loro 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.

Esistono diversi tipi di schedulazione, che variano in base a quando e come viene assegnata la CPU ai processi: a lungo, a medio e a breve termine.

Definizione: schedulazione a lungo termine

La schedulazione a lungo termine รจ un tipo di schedulazione dei processi in cui la CPU decide quali processi devono essere prelevati dalla coda dei processi per essere spostati nella memoria principale (RAM) nella coda dei processi pronti. 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.

Definizione: schedulazione a medio termine

La schedulazione a medio termine รจ un tipo di schedulazione dei processi 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.

Definizione: schedulazione a breve termine

La schedulazione a breve termine รจ un tipo di schedulazione dei processi ed รจ 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).

2.1 - Code di schedulazione

Definizione: 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, determinando quale processo deve essere eseguito, aspettare o bloccarsi, basandosi su determinati algoritmi 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:

Le principali code di schedulazione sono la coda dei processi (job queue), la coda dei processi pronti (ready queue o run queue), la coda dei dispositivi (device queue) e la coda dei processi bloccati (waiting queue).

Vediamo le definizioni delle varie code di schedulazione.

Definizione: coda dei processi ( job queue)

La coda dei processi (job queue) รจ una coda di schedulazione che contiene tutti i processi esistenti nel sistema operativo, indipendentemente dallo stato in cui si trovano. Non appena un processo viene creato, viene aggiunto a questa coda.

Definizione: coda dei processi pronti ( ready queue o run queue)

La coda dei processi pronti (ready queue o run queue) รจ una coda di schedulazione che 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.

Definizione: coda dei dispositivi ( device queue)

La coda dei dispositivi (device queue) รจ una coda di schedulazione propria di ogni dispositivo di I/O nel sistema operativo. 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.

Definizione: coda dei dispositivi ( device queue)

La coda dei dispositivi (device queue) รจ una coda di schedulazione propria di ogni dispositivo di I/O nel sistema operativo. 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.

Definizione: coda dei processi bloccati ( waiting queue)

La coda dei processi bloccati (waiting queue) รจ una coda di schedulazione che 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 coda dei processi pronti. รˆ simile alla coda dei dispositivi, ma puรฒ anche includere processi in attesa di altri tipi di eventi non legati ai dispositivi di I/O.

2.2 - Algoritmi di schedulazione

Un algoritmo di schedulazione รจ un algoritmo che definisce le regole secondo cui il sistema operativo assegna la CPU ai processi pronti allโ€™esecuzione, cioรจ quelli nello stato ready elencati nella coda dei processi pronti. รˆ un componente fondamentale del dispatcher del kernel e influisce su tempo di risposta (responsiveness), utilizzo della CPU, throughput ed equitร  tra processi.

Esistono vari algoritmi di schedulazione utilizzati per determinare quale processo deve essere eseguito dalla CPU in un dato momento. I vari algoritmi di schedulazione hanno caratteristiche diverse che li rendono piรน adatti a determinati scenari (interattivi, in tempo reale, ecc.), e la scelta dellโ€™algoritmo giusto dipende dai requisiti specifici del sistema.

1. First-Come, First-Served (FCFS)

  • Descrizione: I processi vengono eseguiti nellโ€™ordine in cui arrivano. Il primo processo che arriva รจ il primo a essere eseguito.
  • Vantaggi: Semplice da implementare.
  • Svantaggi: Puรฒ causare il problema della convoy effect, dove i processi piรน lunghi bloccano lโ€™esecuzione di quelli piรน brevi, aumentando i tempi di attesa.

2. Shortest Job Next (SJN) o Shortest Job First (SJF):

  • Descrizione: Il processo con il tempo di esecuzione piรน breve viene eseguito per primo.
  • Vantaggi: Minimizza il tempo di attesa medio.
  • Svantaggi: Difficile da implementare, poichรฉ รจ necessario conoscere in anticipo la durata di ogni processo. Puรฒ causare starvation (processi lunghi potrebbero non essere mai eseguiti).

3. Round Robin (RR):

  • Descrizione: Ogni processo viene eseguito per un intervallo di tempo fisso (detto quantum), dopodichรฉ la CPU passa al processo successivo in coda. Se un processo non termina nel suo quantum, viene reinserito in coda per essere eseguito piรน tardi.
  • Vantaggi: Fornisce equitร , poichรฉ ogni processo ha unโ€™opportunitร  di essere eseguito in modo ciclico.
  • Svantaggi: Se il quantum รจ troppo lungo, il comportamento diventa simile a FCFS. Se รจ troppo breve, si ha un overhead elevato per i frequenti cambi di contesto.

4. Priority Scheduling:

  • Descrizione: Ad ogni processo viene assegnata una prioritร , e la CPU viene assegnata al processo con la prioritร  piรน alta. In caso di paritร  di prioritร , si puรฒ usare un altro criterio, come lโ€™FCFS.
  • Vantaggi: Puรฒ essere utilizzato per dare la precedenza a processi importanti.
  • Svantaggi: Puรฒ causare starvation per i processi con prioritร  bassa, che potrebbero attendere a lungo prima di essere eseguiti. Il problema puรฒ essere risolto con tecniche di aging, che aumentano la prioritร  di un processo man mano che rimane in attesa.

5. Multilevel Queue Scheduling:

  • Descrizione: I processi vengono suddivisi in piรน code, ciascuna con una diversa prioritร  (ad esempio, processi interattivi in una coda e processi batch in unโ€™altra). Ogni coda puรฒ avere il proprio algoritmo di schedulazione.
  • Vantaggi: Permette di gestire diversi tipi di processi in modo efficace.
  • Svantaggi: La separazione rigida tra code puรฒ causare squilibri nel carico della CPU.

6. Multilevel Feedback Queue Scheduling:

  • Descrizione: Simile al multilevel queue, ma i processi possono โ€œscendereโ€ o โ€œsalireโ€ da una coda allโ€™altra in base al loro comportamento (es. se un processo usa troppo la CPU, puรฒ essere spostato in una coda a prioritร  inferiore).
  • Vantaggi: Piรน flessibile rispetto al multilevel queue, permette di adattare la schedulazione alle caratteristiche di esecuzione dei processi.
  • Svantaggi: Puรฒ essere complesso da implementare e configurare correttamente.

Ogni PCB ha dei puntatori, per cui il context switch si fa spostando questi puntatori.

cambiare schema linklinklink

5. Segnali (Signals)

  • I segnali sono un metodo per inviare notifiche asincrone a un processo. Un processo puรฒ ricevere segnali che indicano la necessitร  di eseguire qualche azione (es. terminazione, interruzione).
  • Esempi: kill(), signal().

6. Semafori (Semaphores)

  • I semafori sono variabili che vengono utilizzate per gestire lโ€™accesso concorrente alle risorse condivise, come la memoria condivisa. Aiutano a evitare situazioni come il race condition.
  • Esempi: semget(), semop().

7. File temporanei

  • I file temporanei possono essere usati per lo scambio di dati tra processi. I dati vengono scritti su file e letti da altri processi.

9. Database e Sistemi di File

  • Un processo puรฒ scrivere dati in un database o file, e un altro processo puรฒ leggerli. Questo metodo รจ meno efficiente rispetto ad altri, ma puรฒ essere utile per la persistenza e condivisione di grandi quantitร  di dati.

6. Coordinamento e sincronizzazione tra processi Nei sistemi con processi concorrenti, รจ necessario coordinare lโ€™esecuzione di piรน processi per evitare problemi come race conditions (condizioni di corsa) o deadlock (stallo). Le operazioni per la sincronizzazione includono:

  • Mutua esclusione: Garantisce che un solo processo possa accedere a una risorsa condivisa alla volta (utilizzando semafori o mutex).
  • Semafori: I semafori sono variabili utilizzate per controllare lโ€™accesso a risorse condivise, permettendo ai processi di attendere in modo sicuro lโ€™accesso a una risorsa.
  • Monitor: I monitor sono costrutti di alto livello per gestire la mutua esclusione e la sincronizzazione dei processi.

7. Forking e clonazione di processi Alcuni sistemi operativi (come Unix/Linux) permettono a un processo di creare una copia di se stesso tramite lโ€™operazione di fork. Il processo figlio risultante รจ una replica del processo padre, con la stessa memoria, ma con un diverso PID.

8. Gestione dei segnali (Signals) I processi possono inviare e ricevere segnali, che sono notifiche asincrone inviate dal sistema operativo o da altri processi per segnalare eventi come errori, richieste di terminazione o altri eventi specifici. Ad esempio, in Unix/Linux, il comando kill invia segnali a un processo (ad esempio, SIGTERM per richiedere la terminazione del processo).

9. Esecuzione di processi in foreground e background Nei sistemi multiutente, รจ possibile eseguire un processo in foreground (in primo piano) o in background (sullo sfondo). Un processo in background puรฒ continuare a essere eseguito mentre lโ€™utente esegue altre attivitร . link linklink ATTENZIONE: DA NON CONFONDERE CON PARALLELISMO O MULTITHREADING

Puรฒ essere:

7 - Chiamate di sistema

7.1 - Variabile globale errno

errno รจ una variabile globale definita nella libreria <errno.h> che viene impostata dalle chiamate di sistema e dalle funzioni della libreria standard per segnalare un errore. Non viene mai azzerata automaticamente, quindi il suo valore รจ significativo solo dopo che una funzione segnala un errore (ad esempio restituendo -1 o NULL).


7.2 - Dove si trova errno?

errno รจ definita in <errno.h>, quindi per usarla bisogna includere questa libreria:

#include <errno.h>

รˆ una variabile globale ma thread-local, il che significa che ogni thread ha la propria copia di errno.


7.3 - Come funziona errno?

  • Quando una funzione di sistema fallisce, imposta errno con un codice dโ€™errore specifico.
  • Lโ€™utente puรฒ controllare errno subito dopo lโ€™errore per capire cosa รจ andato storto.
  • Se una funzione ha successo, il valore di errno non viene modificato.

7.3.1 - Esempio base

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
 
int main() {
    if (access("file_non_esistente.txt", F_OK) == -1) {
        printf("Errore! Codice errno: %d\n", errno);
        perror("Descrizione errore");
    }
    return 0;
}

7.3.1.1 - Output atteso:

Errore! Codice errno: 2
Descrizione errore: No such file or directory
  • access() verifica se il file esiste.
  • Poichรฉ il file non esiste, restituisce -1 e imposta errno a ENOENT (2).
  • perror() stampa il messaggio associato a errno.

7.4 - Principali valori di errno

errno assume diversi valori a seconda del tipo di errore. Alcuni esempi comuni:

Codice errnoValoreDescrizione
EPERM1Operazione non permessa
ENOENT2File o directory non trovata
EIO5Errore di input/output
ENOMEM12Memoria insufficiente
EACCES13Permesso negato
EEXIST17Il file esiste giร 
ENOTDIR20Il percorso non รจ una directory
EISDIR21Il file รจ una directory
EINVAL22Argomento non valido
EAGAIN11Risorsa temporaneamente non disponibile

Lโ€™elenco completo si trova in <errno.h>.


7.5 - Come stampare il messaggio di errore?

Per ottenere una descrizione testuale di errno, si usano:

  1. perror() \to Stampa direttamente il messaggio di errore.
  2. strerror(errno) \to Restituisce una stringa con il messaggio di errore.

7.5.1 - Esempio con strerror()

#include <stdio.h>
#include <errno.h>
#include <string.h>
 
int main() {
    errno = EACCES;  // Simula un errore di permesso negato
    printf("Errore: %s\n", strerror(errno));
    return 0;
}

Output atteso:

Errore: Permission denied

7.6 - Conclusione

  • errno รจ una variabile globale che contiene lโ€™ultimo errore generato.
  • Non viene azzerata automaticamente, quindi va controllata subito dopo un errore.
  • Si usa perror() o strerror() per stampare il messaggio di errore.
  • Molte funzioni standard impostano errno quando falliscono (ad es. open(), read(), write()).

8 - Per conoscere gli errori (reminder)

#include <stdio.h>
#include <string.h>
...
// --- stampa l'elenco degli errori noti sul sistema
int idx = 0; // numero errore
for( idx = 0; idx < sys_nerr; idx++ )
printf( "Error #%3d: %s\n", idx, strerror( idx ) );

%%

Fonti

  • Abraham Silberschatz, Peter Baer Galvin, Greg Gagne - Sistemi Operativi (10แตƒ Edizione) - Pearson, 2019 - ISBN: 9788891904560.
  • ๐Ÿซ 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):