Concetti di programmazione multiprocesso

I processi

Sui nostri pc siamo abituati ad installare diversi software o programmi. Questi rimangono sul disco fisso sopiti finche non decidiamo di eseguirli. Quando esegui un programma sul computer, questo non è più solo un file statico su disco, ma parte di esso o tutto viene spostato nella ram ovvero la memoria di lavoro: diventa un processo.

Un processo è un’istanza di un programma in esecuzione. Il processo non è più statico ma si evolve, modificando le sue variabili, eseguendo le righe di codice con cui è stato programmato. Pensa a un programma (il codice) come una ricetta e al processo come lo chef che la sta eseguendo: ha gli ingredienti, gli utensili e sta attivamente cucinando.

Ogni processo necessita di risorse (CPU, memoria, file aperti, dispositivi vari come scheda video o la stampante) per operare. Chiaramenete la CPU è quella più importante e preziosa, che richiede uno scheduling accurato. (approfondisci qui Scheduling)

Il pcb, le memorie stack e heap

Ogni processo è gestito dal sistema operativo che tiene traccia delle sue informazioni vitali tramite il PCB (Process Control Block).

  • PCB (Process Control Block): Viene detto il descrittore del processo. È una struttura dati che contiene tutte le informazioni necessarie per gestire e controllare un processo. È come la carta d’identità del processo e include:
    • Stato del processo (in esecuzione, pronto, bloccato, ecc.).
    • Program Counter (l’indirizzo della prossima istruzione da eseguire).
    • Registri della CPU (per chi ha avuto la possibilità di studiare l’Assembly i 64-bit general-purpose registers (RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP).
    • Informazioni sulla gestione della memoria.
  • Memoria del Processo: La memoria allocata al processo è tipicamente divisa in sezioni:
    • Code/Text: Contiene il codice eseguibile del programma.
    • Data: Contiene le variabili globali e statiche.
    • Stack: Usato per gestire le chiamate a funzione (e i loro valori di ritorno) e per le variabili locali e i parametri di funzione. Cresce e decresce in modo automatico.
    • Heap: Usato per l’allocazione dinamica della memoria (es. con malloc o new nei linguaggi di programmazione ad oggetti). La memoria qui deve essere gestita esplicitamente dal programmatore altrimenti è facile creare errori di sistema.

Primi Programmi e Architettura Monocore

Nei primi computer, le CPU erano monocore (avevano un solo “cervello” o unità di calcolo). I programmi venivano eseguiti in modo sequenziale e top-down.

  • Programmazione Top-Down: Si parte da un problema generale (il top) e lo si scompone in sottoproblemi più piccoli e gestibili. Questo corrisponde spesso a suddividere il codice in funzioni o sottoprogrammi che vengono richiamati in sequenza.

In un sistema monocore, in ogni istante di tempo, solo un processo può essere effettivamente in esecuzione sulla CPU.

Multitasking e l’illusione del Parallelismo

Sebbene la CPU sia monocore, un utente può aprire contemporaneamente il browser, un editor di testo e un lettore musicale. Questo è reso possibile dal multitasking implementato dal sistema operativo.

  • Multitasking: È la capacità di un sistema operativo di eseguire più processi “contemporaneamente” (o almeno di darne l’illusione).
  • Illusione del Parallelismo: Il sistema operativo utilizza uno scheduler che interrompe un processo in esecuzione dopo un brevissimo intervallo di tempo (chiamato time slice o quanto), salva il suo stato (nel PCB) e carica lo stato di un altro processo (operazione chiamata context switch).
  • Questo cambio rapido e continuo tra i processi è così veloce che all’occhio umano sembra che stiano lavorando tutti nello stesso momento, creando l’illusione del parallelismo.

La Diffusione dei Multicore (Anni ’90/’00) 🚀

Con l’aumento della richiesta di potenza di calcolo e i limiti fisici nell’aumentare la velocità di un singolo core, l’industria si è mossa verso l’integrazione di più unità di calcolo nello stesso chip.

  • Multicore: Architettura in cui un singolo processore fisico contiene più core di elaborazione indipendenti (es. Dual-Core, Quad-Core, ecc.).

La diffusione di queste architetture, in particolare dagli anni 2000, ha reso possibile il vero parallelismo.

Multiprocesso: Fork, Join e Problemi

L’architettura multicore ha permesso di passare dal multitasking (falso parallelismo) al multiprocessing (vero parallelismo).

  • Multiprocesso: L’esecuzione di più processi contemporaneamente su core diversi.
  • Fork (Biforcazione): È una primitiva del sistema operativo che permette a un processo padre di creare una copia esatta di sé stesso (il processo figlio). Padre e figlio sono processi distinti che possono poi eseguire codice diverso in parallelo.
  • Join (Attesa): È la primitiva usata dal processo padre per attendere la terminazione di uno o più processi figli prima di continuare la propria esecuzione.

Problemi con il Multiprocesso

Sebbene il multiprocessing garantisca il parallelismo, ha degli svantaggi:

  1. Context Switch Costoso: Passare da un processo all’altro richiede il salvataggio e il caricamento di tutto il PCB e della mappa di memoria, che è un’operazione lenta per la CPU.
  2. Il fork di processi crea una copia in memoria del processo da padre a figlio. Questo implica un raddoppio nell’uso della memoria per contenere variabili e le varie istruzioni/righe di codice, nonché un sistema per eseguire solo parte di esse in base al fatto che sia in esecuzione il padre o il figlio.
  3. Condivisione Difficile: Per far comunicare due processi (padre e figlio) è necessario usare meccanismi specifici come pipe, socket o memoria condivisa, poiché per default la loro memoria è separata e protetta.

La Nascita dei Thread: L’Evoluzione Leggera

Per superare i problemi di lentezza e difficoltà di comunicazione del multiprocessing, è stato introdotto il concetto di thread.

  • Thread (o LWP – Lightweight Process): È un flusso di esecuzione all’interno di un processo. Il thread esegue solo una piccola parte del codice del processo, solitamente una funzione separata o separabile dal flusso principale.
  • Multithreading: È la capacità di un singolo processo di eseguire più thread contemporaneamente.

Vantaggi dei Thread

I thread sono la soluzione ideale per il parallelismo perché:

  1. Context Switch Veloce: Il cambio tra thread dello stesso processo è molto più veloce del cambio tra processi.
  2. Condivisione Facile: Tutti i thread all’interno di un processo condividono la stessa area di memoria (Code, Data, Heap) e i file aperti. Ogni thread ha però il proprio Stack e il proprio Program Counter.

In sintesi, un processo è come un condominio con le sue fondamenta (memoria condivisa), mentre i thread sono gli inquilini che vivono lì, ognuno con la propria pila di lavoro (Stack) ma che possono facilmente accedere alle aree comuni.

Ultima modifica 29 Ottobre 2025