Skip to content

processi, thread, Multitasking e multithreading

I processi

I processi furono introdotti nei sistemi operativi non appena vi furono i miglioramenti hardware che consentirono di gestire in modo accettabile i processi, senza provocare un consumo percentuale troppo elevato della CPU.

Il sistema operativo sappiamo infatti che si occupa di gestire tutti i sottosistemi (processi, memoria, comunicazioni di rete e in generale con le periferiche di I/O) e questa gestione occupa una fetta dei cicli di CPU disponibili. Ogni evoluzione dell’hardware consente al sistema operativo di avere a disposizione una quantità di cicli di clock maggiore e questo viene speso in parte per avere più potenza di calcolo da dare agli utenti ed in parte per avere dei sistemi di gestione più complessi che portino ad una gestione più efficace.

L’introduzione dei processi ha permesso ai sistemi operativi di gestire più programmi contemporaneamente, assegnando loro risorse come la CPU e la memoria in modo equo. In questo modo, i processi possono essere eseguiti in parallelo o in serie, a seconda delle risorse disponibili e delle priorità dei processi.

Inoltre, l’uso dei processi ha reso possibile l’esecuzione di programmi più complessi, che richiedono la collaborazione di più sottoprocessi per completare un compito. Ad esempio, un programma di elaborazione di immagini potrebbe suddividere il lavoro in più sottoprocessi, ciascuno responsabile dell’elaborazione di una porzione dell’immagine.

Ogni processo ha il proprio spazio di indirizzamento della memoria, risorse e stato di esecuzione. I processi possono essere creati, sospesi, ripresi ed eliminati durante l’esecuzione del sistema operativo.

Il process Control Block (PCB)

Il Process Control Block (PCB) è una struttura dati utilizzata dai sistemi operativi per tenere traccia delle informazioni relative ad ogni processo attivo nel sistema. Il PCB contiene informazioni sullo stato del processo, come l’ID del processo, le risorse allocate, lo stato del processo (ad esempio in esecuzione, sospeso, pronto), il programma di avvio e la tabella dei pagine della memoria.

Ecco un esempio di come potrebbe essere strutturato un PCB:

struct pcb {
int pid; // ID del processo
int state; // stato del processo (es. in esecuzione, sospeso)
int priority; // priorità del processo
struct mem_block *mem_table; // tabella delle pagine della memoria allocate al processo
struct cpu_context *cpu_context; // contesto di esecuzione del processo sulla CPU
struct file_descriptor *fd_table; // tabella dei descrittori dei file aperti dal processo
struct thread_list *thread_list; // lista dei thread appartenenti al processo
};

I prerequisiti per capire la struttura del PCB sono:

  • Conoscenza delle basi dei sistemi operativi, in particolare della gestione dei processi e dei thread.
  • Comprensione di come i sistemi operativi utilizzano le strutture dati per tenere traccia dello stato dei processi e delle risorse allocate.
  • Conoscenza delle principali informazioni che devono essere tenute traccia per ogni processo attivo nel sistema, come l’ID del processo, lo stato del processo, la priorità del processo e le risorse allocate.

Multiprogrammazione e multitasking

Una possibilità interessante quando si utilizzano i processi è quella di sfruttare tecniche di gestione di più processi contemporaneamente, quali multiprogrammazione e multitasking.

multiprogrammazione

La multiprogrammazione è una tecnica utilizzata dai sistemi operativi per gestire più processi contemporaneamente. In un sistema a multiprogrammazione, il sistema operativo tiene traccia di più processi in esecuzione e assegna loro risorse come la CPU e la memoria in modo equo. Ciò consente al sistema operativo di passare da un processo all’altro rapidamente, dando l’impressione che i processi vengano eseguiti contemporaneamente.

multitasking

Il multitasking è una caratteristica dei sistemi operativi che permette all’utente di eseguire più compiti contemporaneamente. Ad esempio, l’utente può avere in esecuzione un programma di elaborazione di testo mentre ascolta musica o scarica file da Internet. In realtà, il sistema operativo sta gestendo più processi contemporaneamente, ma li sta eseguendo uno alla volta sulla CPU. Grazie al multitasking, l’utente ha l’impressione che i compiti vengano eseguiti contemporaneamente.

Gli stati di un processo e la gestione con python

Lo stato di un processo rappresenta il suo status corrente all’interno del sistema operativo. I processi possono essere in uno dei seguenti stati:

  • Nuovo: il processo è stato creato ma non è ancora pronto per l’esecuzione.
  • Pronto: il processo è pronto per l’esecuzione sulla CPU e sta aspettando che il sistema operativo lo selezioni.
  • In esecuzione: il processo sta utilizzando la CPU per eseguire il proprio codice.
  • In attesa: il processo ha sospeso la propria esecuzione in attesa di un evento esterno, come l’apertura di un file o la ricezione di un segnale.
  • Terminato: il processo ha completato la sua esecuzione e sta per essere eliminato dal sistema operativo.

In Python, è possibile utilizzare la libreria multiprocessing per creare nuovi processi e gestire il loro stato. Ecco un esempio di come si può creare un nuovo processo e variarne lo stato:

import multiprocessing
import time
def worker():
print("Processo in esecuzione") # Simuliamo l'esecuzione del processo con una breve pausa
time.sleep(2)
print("Processo terminato")
if __name__ == "__main__":
# Creiamo un nuovo processo e lo avviamo
p = multiprocessing.Process(target=worker)
p.start()
# Verificiamo lo stato del processo
while True:
if p.is_alive():
print("Il processo è in esecuzione")
else:
print("Il processo si è terminato")
break # Simuliamo l'esecuzione del processo principale con una breve pausa
time.sleep(1)

In questo esempio, creiamo un nuovo processo utilizzando la classe Process della libreria multiprocessing. La funzione worker() viene eseguita dal processo creato. Utilizziamo il metodo is_alive() per verificare lo stato del processo e stampiamo un messaggio appropriato. Alla fine dell’esecuzione, il processo si termina e il suo stato diventa “terminato”.

I thread

I thread sono unità esecutive all’interno di un processo. In altre parole, un processo può contenere uno o più thread, che rappresentano sequenze indipendenti di istruzioni eseguibili contemporaneamente.

Abbiamo visto che un processo è rappresentato da un [[00-00 process control block (PCB)]] all’interno del sistema operativo. Allo stesso modo un thread verrà rappresentato ad un thread control block.

il Thread control block (TCB)

Il Thread Control Block (TCB) è una struttura dati utilizzata dai sistemi operativi per tenere traccia delle informazioni relative ad ogni thread attivo nel sistema. Il TCB contiene informazioni sullo stato del thread, come l’ID del thread, lo stato del thread (ad esempio in esecuzione, sospeso, pronto), il puntatore al stack del thread e il contesto di esecuzione del thread.

Ecco un esempio di come potrebbe essere strutturato un TCB:

struct tcb {
int tid; // ID del thread
int state; // stato del thread (es. in esecuzione, sospeso)
int priority; // priorità del thread
void *stack_ptr; // puntatore allo stack del thread
struct cpu_context *cpu_context; // contesto di esecuzione del thread sulla CPU
};

I prerequisiti per capire la struttura del TCB sono:

Le principali informazioni che devono essere tenute traccia per ogni thread attivo nel sistema sono:

  • ID del thread: un identificatore univoco assegnato al thread dal sistema operativo per distinguerlo dagli altri thread.
  • Stato del thread: indica lo stato di esecuzione del thread, ad esempio se è in esecuzione, sospeso o pronto per l’esecuzione. Questa informazione viene utilizzata dal sistema operativo per decidere quale thread eseguire quando più thread sono pronti per l’esecuzione.
  • Priorità del thread: indica la priorità relativa del thread rispetto agli altri thread nel sistema. La priorità viene utilizzata dal sistema operativo per decidere quale thread eseguire quando più thread sono pronti per l’esecuzione.
  • Puntatore allo stack del thread: indica l’indirizzo di memoria dell’ultima istruzione eseguita dal thread. Questa informazione viene utilizzata dal sistema operativo per ripristinare lo stato del thread quando viene sospeso e ripreso in esecuzione.

Inoltre, il TCB può contenere altre informazioni utili come ad esempio il contesto di esecuzione del thread sulla CPU, che include le istruzioni della CPU e i registri del thread.

I sistemi operativi utilizzano anche altre strutture dati per tenere traccia delle risorse allocate ai thread. Ad esempio, possono utilizzare una tabella di allocazione della memoria per tenere traccia dei blocchi di memoria assegnati ai thread e una tabella di gestione dei file per tenere traccia dei file aperti dai thread.

In generale, i sistemi operativi utilizzano queste strutture dati per monitorare lo stato dei thread e delle risorse allocate in modo da poter gestire efficacemente l’esecuzione dei programmi e garantire che le risorse siano utilizzate in modo corretto ed efficiente.

il thread control block e il process control block

Il Thread Control Block (TCB) e il Process Control Block (PCB) sono entrambi dati di struttura utilizzati dai sistemi operativi per gestire i thread e i processi rispettivamente. Il PCB contiene informazioni sullo stato del processo, come ad esempio l’ID del processo, le risorse allocate, lo stato del processo (ad esempio in esecuzione, sospeso, pronto), il programma di avvio e la tabella dei pagine della memoria.

Il TCB, invece, è una struttura dati simile al PCB ma specifica per i thread. Contiene informazioni sullo stato del thread, come ad esempio l’ID del thread, lo stato del thread (ad esempio in esecuzione, sospeso, pronto), il puntatore allo stack del thread e il contesto di esecuzione del thread.

In generale, il TCB è una sotto-struttura del PCB. Ciò significa che ogni processo ha un proprio PCB e tutti i thread all’interno del processo condividono lo stesso PCB ma hanno ciascuno il proprio TCB. In questo modo, il sistema operativo può gestire efficacemente le risorse tra i thread di uno stesso processo e allo stesso tempo tenere traccia dello stato di ogni singolo thread. Inoltre, il TCB contiene anche informazioni sulla priorità del thread, che viene utilizzata dal sistema operativo per decidere quale thread eseguire quando più thread sono pronti per l’esecuzione. La priorità dei thread può essere modificata dinamicamente durante l’esecuzione del programma, ad esempio in base al carico di lavoro o alle esigenze dell’applicazione.

TCP e PCB

Creare un thread in python

In Python, è possibile creare un thread utilizzando la classe threading.Thread. Ecco un esempio di come si può creare un thread in Python:

import threading
def funzione_thread():
print("Il thread sta eseguendo il suo lavoro")
# Creazione del thread
thread = threading.Thread(target=funzione_thread)
# Avvio del thread
thread.start()
# Attesa per l'esecuzione del thread (opzionale)
thread.join()

In questo esempio, la funzione funzione_thread viene eseguita in un thread separato quando si chiama il metodo start() sulla instanza della classe Thread. Il metodo join() viene utilizzato per attendere che il thread abbia terminato l’esecuzione prima di continuare con il resto del programma.