Processi e Thread
Processi
modificaCon il termine processo non si indica solo un programma in esecuzione ma anche l'insieme delle sue risorse (program counter, stack, dispositivi, ecc.).
Nell'ambito della multiprogrammazione abbiamo più processi che si contendono la CPU. Essi vengono eseguiti uno alla volta e la CPU passa continuamente da un processo all'altro. La parte del sistema operativo che si occupa di decidere quale processo va mandato in esecuzione si chiama scheduler dei processi. Il cambio di un processo, chiamato anche context switch (commutazione di contesto), può essere causato ad esempio dall'arrivo di un processo a priorità più alta oppure da una system call. Quando ciò avviene è necessario interrompere il processo in corso e conservare il suo stato all'interno del PCB (Process Control Block): una struttura dati pensata per contenere tutto quello che servirà al processo per poter poi ripartire (dati identificativi del processo e dell'utente, program counter, registri della CPU, informazioni sull'utilizzo di memoria e CPU, stato dei file aperti, ecc.). Un problema della commutazione di contesto è il grande overhead che viene a crearsi: mentre si cambia il contesto si ha l'esecuzione delle system call e il sistema non fa lavoro utile. Questo può rivelarsi un collo di bottiglia per sistemi ad alto parallelismo.
Gerarchia dei processi
modificaI processi possono essere creati al boot del sistema (spesso in questi casi si tratta dei cosiddetti processi demone), in seguito ad un'apposita system call o su richiesta dell'utente. Nei sistemi UNIX il primo processo che viene avviato viene chiamato init e ad esso viene associato PID 1. I successivi processi avviati possono a loro volta avviare dei processi figli e questi acquisiranno lo stesso PID del padre. Se un processo padre muore il figlio viene ereditato da init. Questa gerarchia dei processi non è così rigida nei sistemi Windows. In questi infatti il processo padre possiede una handle al figlio ma questa può anche essere passata ad altri processi.
Thread
modificaCome già spiegato nella gestione della memoria, ogni processo è indipendente dall'altro, sia come istruzioni sia come memoria. Se creando un programma si ha la necessità di eseguire due azioni contemporaneamente, ciò è impossibile data la sequenzialità intrinseca nel concetto di processo. Quindi si dovrebbero creare due processi, ma avendo essi la memoria separata, essi dovrebbero continuamente scambiarsi dati, introducendo un grande overhead. Uno schema simile farebbe diminuire drasticamente le prestazioni della macchina e l'esecuzione in contemporanea diventerebbe impossibile ai fini pratici. È qui che vengono in aiuto i thread: dei sottoprocessi chiamati anche "flussi di controllo" o "processi leggeri".
I thread hanno svariati vantaggi:
- Costo ridotto (in termini di risorse richieste) per l'attivazione
- Commutazione di contesto molto meno onerosa rispetto ad un processo
- Condivisione della memoria
- Possibilità di utilizzare memoria separata
- Controllo e chiusura di essi relativamente semplice
Ogni thread possiede un suo stato che può essere: pronto, in esecuzione, bloccato.
Un processo, anche se non utilizza thread, ha comunque la funzione principale (in C main()
) che viene vista come un il singolo thread di un processo.
Un esempio molto comune di utilizzo dei thread è un server multi-client. Il server (ad esempio uno di chat) deve interagire con più client (ad esempio se un client invia al server il testo "ciao" il server lo deve inviare a tutti gli altri client). Per fare questo si può attivare un thread per ogni utente connesso che gestisce la comunicazione esclusivamente per quel singolo client. Una volta che il client si disconnette il thread termina.