Sommario
< Home
Stampa

Processi e risorse

Cosa sono i processi

Partiamo dal concetto di applicazione. Per essa in ambito informatico si intende una entità digitale pronta ad essere eseguita, e può essere composta ad esempio da codice macchina contenuto in un file eseguibile (es. nei sistemi Windows con estensione “.exe”) o da uno script di un linguaggio ad alto livello (es. JavaScript, Python) eseguibile da un “runtime” (applicativo eseguibile che esegue gli script).

Al lancio (“run”) di una applicazione, il sistema operativo crea un nuovo oggetto, detto “processo“, a cui è dedicata una porzione della memoria della macchina fisica (eventualmente con il supporto della memoria virtuale), dove viene inizialmente memorizzato il codice pronto da eseguire, lo heap e lo stack. Al processo viene associato un record (detto PCB, Process Control Block) che viene salvato in una tabella (detta di processi). Questo record contiene in particolare:

PCB
– PID (identificativo);
– stato del processo;
c.)
– pagine di memoria dedicate, posizione dello stack;
– registri di memoria;
– risorse utilizzate;
– altri metadati (es. richieste di I/O, tempo di cpu, ec

Il sistema operativo si occupa di fornire al processo le risorse di macchina (con architettura di Von Neumann) all’applicativo in esecuzione. Come visto in precedenza però, dal punto di vista del sistema operativo, i processi in esecuzione sono molti, quindi occorre suddividere la memoria in parti (tramite il meccanismo di paginazione) e fornirle ai singoli processi. Inoltre il sistema operativo suddivide il lavoro della cpu frazionando il tempo di esecuzione ed utilizzando un algoritmo di scheduling che assegna una certa quantità di tempo (detta quanto), a turno, a ciascun processo.

Dal punto di vista del programma in esecuzione, invece esso si comporta come se fosse l’unico processo in esecuzione. Il processo vedrà la sua memoria come se fosse l’unica memoria disponibile. Il processo non ha alcun controllo su come gestire la cpu e la memoria fisica. Questo modello viene chiamato multiprogrammazione preemptive, ed è diffuso nei maggiori sistemi operativi per PC a partire dagli anni 90, e che permette quindi l’esecuzione contemporanea di molti processi su cpu anche con un solo core.

Nel modello preemptive un processo può assumere uno dei seguenti stati:

– nuovo

– in esecuzione

– in attesa

– pronto

– terminato

Quando un processo viene avviato, va in stato nuovo, dove viene inizializzato (codice e memoria), successivamente va in stato pronto, con una priorità presso lo schedulatore. Quando viene il suo turno, passa in stato attivo, da cui può uscire perchè ha terminato il suo quanto di tempo, oppure perchè ha generato una interruzione, in quanto in attesa di una risorsa. Una volta che la risorsa è stata ottenuta, torna in stato pronto. Se invece viene terminato, va in stato terminato ed infine rimosso.

Ogni volta che un processo viene interrotto (al termine del quanto di tempo), lo stato della CPU (ovvero i registri PC, IR, SP e di memoria) vengono salvati sul PCB dal sistema operativo e ripristinati una volta riattivato. Il sistema operativo quindi assegna la cpu al processo successivo, per il suo quanto di tempo, e così via.

Nell’immagine seguente viene mostrato cosa avviene con due processi che vengono eseguiti sullo stesso processore, in una esecuzione suddivisa in quanti di tempo.

Va osservato che in un sistema multi-processo preemptive il costo di esecuzione di più applicazioni in contemporanea è superiore della somma del costo di esecuzione delle singole applicazioni, perchè occorre tenere conto anche di un overhead dovuto a:

– tempo di esecuzione dello switch di contesto (salvataggio e ripristino registri);

– tempo di esecuzione dello scheduler;

– tempo di gestione dei PCB.

Le risorse condivise

Se è vero che la CPU e la memoria sono risorse condivise in modo trasparente, le altre risorse di I/O (pensiamo alla tastiera, allo schermo, il filesystem, ecc.) non lo sono. Il sistema operativo ha una gestione centralizzata di tutte queste risorse e offre al singolo processo delle librerie di accesso dette API (Application Program Interface) per accedervi.

Ogni risorsa deve essere quindi esplicitamente richiesta, tramite una chiamata di sistema operativo. Il sistema operativo tiene traccia degli accessi alle risorse condivise (in particolare quelle limitate). Inoltre prevede per ogni risorsa un gestore (un software detto anche driver) e un protocollo di accesso (delle regole). 

Il legame tra processo e risorsa viene effettuato attraverso il concetto di richiesta.

Essa può essere:

– singola o multipla (su una o più risorse);

– bloccante (ovvero il processo si blocca finchè non ottiene la risorsa);

Il sistema operativo risponde con una assegnazione, che può essere:

– statica: valida cioè per tutto il ciclo di vita del processo (ad esempio una finestra dello schermo);

– dinamica: la risorsa viene assegnata limitatamente nel tempo.

Le risorse si possono classificare in:

– risorse seriali (o mutuamente esclusive) ovvero risorse a cui accede un processo alla volta (ad esempio un file in scrittura) o parallele, a cui possono accedere più processi in contemporanea (es. un file in lettura)

– risorse prerilasciabili/mutuamente esclusive: risorse che il sistema operativo può sottrarre ad un processo senza danneggiarlo (es. cpu e memoria) o mutuamente esclusive.

Qui uno schema riassuntivo.

Riassumendo:

  • il processo, quando necessita di una risorsa, effettua una richiesta al sistema operativo: questa può essere singola/multipla, bloccante/non bloccante, con assegnazione statica o dinamica.
  • Il sistema operativo risponde con una assegnazione che può essere esclusiva/non esclusiva, prerilasciabile (prima di aver terminato di usarla) o non prerilasciabile (il processo non può rilasciarla finchè non ne termina l’uso).

Esempi

Vediamo alcuni esempi di questo meccanismo.

Esempio 1) Lock di risorsa

Ipotizziamo che un sistema operativo abbia 3 processi P1, P2, P3 di pari priorità e 3 risorse R1, R2, R3, tutte mutuamente esclusive e non rilasciabili. Ipotizziamo quindi un possibile scenario, in cui inizialmente i processi sono pronti ed in coda.

QuantoSoggettoRichiesta/assegnazioneAssegnazioni
1P1richiede R1 (singola/bloccante/dinamica).P1 va in waiting. R1R2R3P1   P2   P3   
2SOassegna R1 a P1 (esclusivo). P1 torna pronto. R1R2R3P1exc  P2   P3   
3P2 richiede R2 (singola/bloccante/dinamica). P2 va in waiting. R1R2R3P1exc  P2   P3   
4SOassegna R2 a P2 (esclusivo). P2 torna pronto. R1R2R3P1exc  P2 exc P3   
5P3richiede R3 (singola/bloccante/dinamica). P3 va in waiting. R1R2R3P1exc  P2 exc P3   
6SOassegna R3 a P3 (esclusivo). P3 torna pronto. R1R2R3P1exc  P2 exc P3  exc
7P1richiede R2 (singola/bloccante/dinamica). P1 va in waiting. R1R2R3P1exc  P2 exc P3  exc
8SOnon assegna R2 a P1. P1 resta waiting. R1R2R3P1exccoda P2 exc P3  exc

Con la gestione concorrente, quindi, esiste un rischio concreto che uno o più processi restino bloccati in attesa di una risorsa fino a quando questa risorsa non viene rilasciata.

Esempio 2) Lock in cascata

Ipotizziamo lo scenario precedente di partenza. 

QuantoSoggettoRichiesta/assegnazioneAssegnazioni
1P1richiede R1 (singola/bloccante/dinamica).P1 va in waiting. R1R2R3P1   P2   P3   
2SOassegna R1 a P1 (esclusivo). P1 torna pronto. R1R2R3P1exc  P2   P3   
3P2 richiede R2 (singola/bloccante/dinamica). P2 va in waiting. R1R2R3P1exc  P2   P3   
4SOassegna R2 a P2 (esclusivo). P2 torna pronto. R1R2R3P1exc  P2 exc P3   
5P3richiede R3 + R1 (multipla/bloccante/dinamica).P3 va in waiting. R1R2R3P1exc  P2 exc P3   
6SOSiccome la richiesta è multipla, P3 resta bloccato in coda per le risorse.  R1R2R3P1exc  P2 exc P3coda coda
7P1svolge il suo quanto senza fare richieste. P1 torna pronto. R1R2R3P1exc  P2 exc P3coda coda
8P2Richiede R3 (singola/bloccante/dinamica) R1R2R3P1exc  P2 exc P3coda coda
9SOCosa fa il sistema operativo? 

In questo scenario il sistema operativo ha due possibilità:

1) Concedere R3 a P2, perchè tanto P3 (che pure aveva richiesto prima quella risorsa) è in coda per R1.

2) Mettere in coda P2, ed attendere che P1 sblocchi R1, in modo da concedere R1+R3 a P3. 

Nel primo caso viene favorito un uso più completo di risorse non concorrenti, ma con l’ovvio svantaggio di avere magari processi in coda che quando fanno richieste multiple rischiano di restare sempre bloccati.

Nel secondo caso viene garantita la priorità delle code dei processi, ma con l’ovvio svantaggio di creare una catena di dipendenza di uno più processi determinata dal rilascio di risorse. 

In questo caso aumenta quindi la probabilità di avere processi bloccati che occupano memoria pur non riuscendo mai ad ottenere l’accesso alle risorse. 

In questo caso è il sistema operativo che deve decidere una strategia. 

In sistemi operativi che privilegiano l’accesso mono utente (es. desktop-mobile) si preferisce adottare la soluzione 1, in quanto si presuppone che un processo che chiede una risorsa è in genere il processo usato dall’utente, con probabilità quindi più alta che questo sia il desiderio dell’utente. Inoltre nei sistemi operativi mobili si tende a terminare processi che restano in coda troppo a lungo. 

Nei sistemi operativi che privilegiano l’accesso di più utenti in contemporanea (es. server) si preferisce adottare la soluzione 2, in quanto si presuppone di non dover far preferenze tra processi, cioè utenti, diversi. Si privilegiano poi eventualmente meccanismi, lato server, per rendere le risorse prerilasciabili, in modo da non incappare comunque in situazioni di stallo.

Esempio 3) Deadlock

In questo scenario abbiamo due processi P1, P2 e due risorse R1, R2.

QuantoSoggettoRichiesta/assegnazioneAssegnazioni
1P1richiede R1 (singola/bloccante/dinamica).P1 va in waiting. R1R2P1  P2  
2SOassegna R1 a P1 (esclusivo). P1 torna pronto. R1R2P1exc P2  
3P2 richiede R2 (singola/bloccante/dinamica). P2 va in waiting. R1R2P1exc P2  
4SOassegna R2 a P2 (esclusivo). P2 torna pronto. R1R2P1exc P2 exc
5P1richiede R2 (singola/bloccante/dinamica). P1 va in waiting. R1R2P1exc P2 exc
6SOnon concede R2 in quanto usata da P2. P1 resta in waiting. R1R2P1exccodaP2 exc
7P2richiede R1 (singola/bloccante/dinamica). P2 va in waiting. R1R2P1exccodaP2 exc
8SOnon concede R1 in quanto usata da P1. P2 resta in waiting. R1R2P1exccodaP2codaexc

Questa situazione provoca una situazione di stallo.  Tutti i processi sono fermi in attesa di una risorsa che non può essere loro concessa.  Il sistema operativo può solamente terminare i processi in stallo dopo un certo lasso di tempo. 

Spetta alle applicazioni prevenire i deadlock verificando prima di fare richieste bloccanti che queste risorse siano disponibili.