1. Descrivere il meccanismo di realizzazione di una Chiamata al Supervisore (SVC)
evidenziando la differenza che esiste tra il salvataggio dei registri del processore nello stack di
sistema e quello nel descrittore di un processo.
Una chiamata a supervisore (SVC), che è semplicemente la richiesta da parte di un programma utente di
accedere ad una funzione del kernel (implementata tipicamente con una primitiva, cioè con una
procedura non interrompibile), non si può realizzare attraverso una semplice chiamata a
sottoprogramma, in quanto prevede la variazione della modalità di esecuzione, dallo stato utente a
quello supervisore (che consente l’uso di istruzioni privilegiate). Una SVC si effettua allora attraverso
apposite procedure di interfaccia, messe a disposizione dalle librerie runtime dei linguaggi di
programmazione che generano un’interruzione con un’apposita istruzione del processore detta
Software Interrupt (SWI), questa deve essere in coppia con l’Interrupt Return (IRET) la quale
ripristinerà il contenuto dei registri prelevandoli dallo stack e riabilita la possibilità di interruzione; o in
alternativa, con un trap, causata da un errore. In questo caso sarà poi la ISR (interrupt service routine) a
dover riconoscere gli errori fortuiti da quelli provocati volontariamente per accedere al kernel. È
possibile distinguere le due situazioni attraverso il contenuto di un registro particolare. Le interruzioni
così ottenute, producono i seguenti effetti:
1. il processo in esecuzione viene interrotto e il suo stato puntuale viene salvato nello stack di
sistema;
2. Nel PC viene immesso un elemento del vettore delle interruzioni, che di fatto è l’entry della
particolare ISR preposta alla gestione delle interruzioni provocate;
3. L’esecuzione passa da modalità utente a modalità supervisore, in modo che la ISR possa
sfruttare istruzioni privilegiate. In kernel mode sono disabilitate le interruzioni;
Lo stesso meccanismo permette anche ad un livello del kernel di chiamare una funzione di livello
inferiore.
Una SVC non comporta necessariamente un context switch, cioè il passaggio del processo in
esecuzione nello stato waiting, seguito dalla schedulazione di un altro processo, per cui anche il
salvataggio dei registri del processore nel PCB del processo corrente non avviene sempre ma solo
quando al termine dell’esecuzione dell’ISR, non si torna subito al programma chiamante a causa del
cambiamento di contesto. Indispensabile è invece il salvataggio nello stack di sistema del contesto.
Quindi la memorizzazione dei registri del processo nel PCB viene effettuata quando un dato processo
deve essere swappato dalla RAM (le sue informazioni verranno memorizzate nel suo descrittore sul
disco, per un suo eventuale riutilizzo).
2. Descrivere la struttura di un descrittore di processo e la struttura dati di un S.O. di cui fa
parte.
Ogni volta che un processo viene creato, si definisce un descrittore di processo o Process Control
Block (PCB), ossia un record che tiene traccia dell’evoluzione del processo.
Il PCB contiene tutte le informazioni relative a: stato globale (new, running, waiting, ready, terminated),
program counter (indica l’indirizzo della successiva istruzione da eseguire per tale processo), risorse
possedute (memoria centrale, unità di I/O assegnate staticamente, file aperti, ecc.), valori correnti dei
registri del processore all’atto dell’uscita dallo stato running, parametri di schedulazione per
l’assegnazione di alcune risorse, posizione dell’area di swap su disco se privo della risorsa memoria
centrale, informazioni di account, ecc. Il PCB viene assegnato ad un processo quando viene posto nello
stato new e viene rilasciato quando esce dallo stato terminated e ne viene persa traccia. Tipicamente i
PCB sono disposti in code costituite da liste a puntatori. Appartengono ad una stessa coda PCB di
processi caratterizzati dallo stesso stato globale (ad esempio coda dei processi ready ad uguale priorità
di schedulazione, coda dei processi waiting in attesa di avviare una operazione di accesso ad un disco,
coda dei processi waiting in attesa di sincronizzarsi con uno stesso processo di servizio, ecc.)
L’abbandono del processore da parte di un processo provoca il cosiddetto cambiamento di contesto
(context switch) nel senso che cambia il contesto a cui appartengono le informazioni contenute nei
registri del processore. Il contenuto dei registri appartenente al processo uscente viene salvato nel suo
Quelli di informatica www.quellidiinformatica.tk - Scaricato da www.cplusplus.it
descrittore, e, effettuata la schedulazione del processo entrante, il contenuto dei registri del processore
viene ripristinato con le informazioni prelevate dal descrittore di quest’ultimo
Il cambiamento di contesto è quindi il risultato dell’esecuzione di tre procedure distinte del S.O. .
Procedure Context_Switch
begin Salvataggio_stato, Scheduling_CPU, Ripristino_stato
end
Nel caso di UNIX, per consentire una migliore gestione della memoria, le informazioni contenute nel
descrittore di processo sono distribuite su tre strutture dati: USER STRUCTURE (può essere trasferita
su memoria di massa), PROCESS STRUCTURE e TEXT STRUCTURE (sempre residenti in
memoria).
UNIX mette a disposizione un array di process structure la cui lunghezza è definita nella fase di
inizializzazione del sistema e che fissa il livello di multiprogrammazione del sistema.
Nella process structure sono contenuti: parametri per lo scheduler(priorità, tempo trascorso in stato
bloccato, tempo di CPU usato), informazioni sulla memoria, segnali,. Varie(stato del processo, process
identifier PID, parent process identifier PPID, user id UID, group id GID, puntatore a user structure).
Nella user structure sono contenuti: registri macchina (SP e PC), parametri system call, tabella dei file
aperti (dal processo in questione), informazione sull’accounting (ovvero sull’uso delle risorse), stack del
kernel.
La differenza tra process e user structure è che la prima definisce quando e come reperire le
informazioni di esecuzione di processo, mentre la seconda è il processo vero e proprio.
3. Descrivere i concetti di stato puntuale e di stato globale di un processo, i possibili stati
globali in cui può trovarsi e per ciascuno stato gli eventi che danno luogo a transizioni.
Stato puntuale di un processo:
Un processo, in quanto sequenza di eventi prodotti dall’esecuzione di un programma, è caratterizzato
dal possedere uno stato che è definito dal valore del contatore di programma e dalla n-pla dei valori
memorizzati nei registri che contengono le sue istruzioni e i suoi dati. Concorrono quindi a definire lo
stato di un processo i valori delle sue variabili globali, dello stack dei registri del processore accessibili
da programma.
Stato globale di un processo:
Definisce la condizione in cui si trova un processo in relazione alla sua possibilità di evolvere, ossia di
eseguire istruzioni su di un processore. Può essere uno dei seguenti:
running: le sue istruzioni sono eseguite da un processore
ready: il processo è in attesa che le sue istruzioni vengano eseguite da un processore
waiting: il processo è in attesa di un evento dovendosi sincronizzare, implicitamente o esplicitamente,
con un altro processo
Nello stato running lo stato puntuale di un processo cambia mentre è bloccato negli stati ready e
waiting. Lo stato ready è anche detto di attesa passiva di un processore e manca se si fa l’ipotesi che il
sistema possiede tanti processori quanti sono i processi. Oltre che negli stati precedenti, un processo
può essere negli stati di:
new: in cui attende che il S.O. gli assegni le risorse necessarie per poterne iniziare l’esecuzione
terminated: in cui attende che il S.O. recuperi tutte le risorse che possiede al termine dell’esecuzione
delle sue istruzioni
Transizioni tra gli stati:
Æ
new ready: il S.O. ha assegnato al processo la necessaria memoria centrale e la ha inizializzata, per
cui si passa all’esecuzione su di un processore della sua prima istruzione
Æ
ready running: lo scheduler della CPU ha assegnato un processore al processo.
Æ
running ready: il processo ha perso forzatamente l’uso del processore (e.g. per eccesso di utilizzo
continuativo)
Quelli di informatica www.quellidiinformatica.tk - Scaricato da www.cplusplus.it
Æ
running waiting: il processo ha eseguito una primitiva del S.O.
• per sincronizzarsi implicitamente o esplicitamente con altri processi e deve attendere
• per l’uso di risorse gestite dal S.O. e la risorsa non è disponibile o è stata concessa e trattandosi
di risorsa di I/O l’operazione è stata lanciata e deve attenderne la terminazione
Æ
waiting ready: la risorsa gestita dal sistema operativo è disponibile e viene concessa o l’operazione
di I/O in precedenza lanciata è terminata
Æ
running terminated: il processo ha eseguito la primitiva del S.O. di terminazione e attende che le
risorse di cui ancora dispone siano recuperate dal S.O., oppure ha commesso un errore fatale per il
quale il S.O. lo ha mandato in ABORT.
4. Descrivere la differenza concettuale che esiste tra l’implementazione di una funzionalità
del S.O. con una procedura del kernel e quella della stessa funzionalità come una proce
dura eseguibile nel contesto di un processo del S.O.
Il S.O. non è altro che un insieme di programmi, per cui si può pensare che esso sia assimilabile ad un
processo. Come tale esso ha una sua evoluzione. Secondo lo standard di progettazione tradizionale le
procedure del kernel vengono eseguite al di fuori di qualsiasi processo: quando il processo in
esecuzione è interrotto o effettua una SVC, il suo contesto viene salvato e il controllo passa al kernel.
Quest’ultimo ha una sua regione di memoria e un suo stack di sistemare, dopo aver eseguito la
procedura richiesta, può ripristinare il processo interrotto prima o schedulare un altro.
Il concetto di processo non si può, in questo caso, applicare al S.O. perché esso viene visto come
un’entità separata operante in modalità privilegiata. Un’altra possibile scelta è quella di eseguire gran
parte del codice del kernel nel contesto dei processi utenti, le cui immagini, oltre ad area codice, area
dati, stack, heap, PCB, comprenderanno anche uno stack di sistema (del kernel) per la gestione delle
chiamate e dei ritorni dalle primitive del kernel, mentre il codice e i dati appartenenti ad esso si
troveranno in uno spazio di indirizzamene condiviso da tutti i processi. Quando si verifica
un’interruzione, o una SVC, la modalità di esecuzione passa da utente a supervisore e il controllo passa
al kernel che esegue, però, il proprio codice nel contesto dello stesso processo utente. Il principale
vantaggio di questa soluzione è che l’esecuzione di una primitiva del S.O. non comporta un cambio di
contesto ma solo un cambio di modalità di esecuzione e la stessa cosa vale per il ritorno al processo
chiamante, a meno che al suo posto non sia stato schedulato un altro. Si evince, da quanto detto, che
all’interno di un solo processo utente si possono eseguire sia programmi utente, sia routine del S.O.
La sicurezza di questi metodo è garantita dal cambio di modalità di esecuzione (user mode a kernel
mode). Un terzo modo è quello di “concepire” le procedure del kernel come processi separati, eseguiti
ovviamente in kernel mode. Questo approccio incoraggio a strutturare il S.O. in moduli, rende efficace
l’implementazione di routines e porta ottime prestazioni in ambiente multiprocessing, in cui una parte
del S.O. si può eseguire su CPU dedicate.
5. Descrivere il meccanismo di creazione e di terminazione di un processo in generale e in
Unix. Evidenziare inoltre la differenza concettuale che esiste tra condivisione o meno
dell’immagine in memoria dei processi padre e figlio.
La creazione di un processo avviene in maniera dinamica ad opera di altri processi per mezzo di
meccanismi messi a disposizione del S.O. In particolare, un processo viene “eseguito” dall’interprete di
JCL (Job Control Language) al momento del lancio di un programma e da un altro processo utente
durante l’esecuzione di un programma concorrente. Anche la terminazione viene realizzata da
meccanismi del S.O. e può essere volontaria (in tal caso il processo esegue una apposita primitiva di
terminazione, exit() ) o forzata (tipicamente per un errore irrecuperabile). Al momento della creazione
di un processo, gli vengono assegnati un identificatore unico (ID) e delle risorse, costituite
principalmente da uno spazio di memoria, in cui vi sono un’area codice, un’area dati e uno stack. Ad
ogni processo viene, inoltre, dato un PCB contenente tutte le informazioni necessarie al S.O. per gestire
Quelli di informatica www.quellidiinformatica.tk - Scaricato da www.cplusplus.it
il processo stesso. Alla terminazione, un processo rilascia tutte le sue risorse che vengono recuperate dal
S.O. I meccanismi fondamentali che consentono ad un processo di generare un altro processo sono le
primitive di fork e join. In particolare la fork consente ad un processo padre di creare un processo figlio
che può condividere o meno lo spazio di memoria del padre ma che in ogni caso eseguirà istruzioni
diverse. Se i due processi condividono area codice, area dati e stack, pur procedendo su flussi di
controllo diversi, si hanno in realtà due thread, cioè due sequenze dinamiche all’interno dello stesso
processo e quindi il passaggio dal padre al figlio è poco oneroso, non comportando un cambio di
contesto. Se, viceversa, non c’è condivisione dell’immagine in memoria, padre e figlio sono due processi
concorrenti e ad ogni passaggio dell’uno all’altro si avrà un cambiamento di contesto. La fork esiste in
due versioni: quella originale non ha parametri di uscita mentre quella ideata in seguito restituisce il PID
del figlio, che viene poi accettato in ingresso dalla join, in modo da permettere al padre di sincronizzarsi
con la terminazione di un determinato figlio (realizzata con la primitiva exit). Un’altra versione della
join ha come parametro di ingresso un intero che è il numero di figli generati e permette al padre di
attendere la terminazione di tutti i figli per proseguire. Anche in UNIX un processo ne genera un altro
con una fork, ma stavolta padre e figlio condividono l’area codice e non l’area dati, perché le loro
istruzioni possono modificare in maniera concorrente gli stessi dati. La sequenza dinamica percorsa dai
due processi si differenzia all’interno della comune sequenza statica grazie al parametro di uscita della
fork, che assume il valore zero per il figlio ed è uguale al PID del figlio per il padre. Il figlio può, poi,
differenziarsi ulteriormente dal padre con una system call exec, che gli permette di eseguire un nuovo
programma, ricopiandone il codice nella propria area testo. Il padre, invece, esegue una wait per
sincronizzarsi con la terminazione del figlio, che può essere volontaria o forzata (questa info è
contenuta nel parametro di ingresso della wait). Il figlio, per terminare, esegue una exit che restituisce
zero alla wait del padre in caso di esito positivo (-1 in caso di esito negativo) e gli permette di
proseguire. La exit causa anche la chiusura dei file aperti dal processo chiamante e la terminazione di
tutti i processi del gruppo di cui era capostipite, mentre i suoi eventuali figli vengono imparentati col
processo 1 (init) che avvia il S.O. Un figlio terminato prima che il padre possa eseguire la wait diventa
<Zombie>, in quanto rilascia tutte le risorse possedute ma non il PCB, in attesa che il padre chieda di
ricongiungersi con lui.
6. Descrivere la natura dei thread e i vantaggi-svantaggi di una programmazione
concorrente che fa uso di thread in luogo di processi cooperanti.
In generale, un processo è definito dalle risorse utilizzate e dalla locazione di memoria nella quale è in
esecuzione. Risorse e dati sono assegnate ad un solo processo per volta. E’ utile però, ammettere la
condivisione delle risorse, consentendo l’accesso in concorrenza. Alcuni sistemi operativi di nuova
concezione hanno provveduto a fornire questo meccanismo attraverso l’utilizzo dei thread.
Un thread è l’unità di base dell’utilizzo della CPU e consiste in un program counter, un insieme di
registri ed uno stack. Esso condivide con i thread ad esso associati le sezioni codice e dati, oltre alle
risorse fornite dal S.O. .
L’insieme dei thread e dell’ambiente da loro condiviso prende il nome di task. L’alto grado di
condivisione rende la creazione dei thread e il passaggio da un thread all’altro in uno stesso task
operazioni meno costose del context switch tra processi. Un primo svantaggio, legato all’utilizzo dei
thread, è il problema del controllo della concorrenza che richiede l’uso delle sezioni critiche o dei lock.
Un modo per rendere più veloce il passaggio da un thread all’altro è quello di implementare i thread a
livello utente, evitando chiamate al S.O. che produrrebbero interrupt. Altro svantaggio legato ai thread
si ha nel caso in cui il kernel sia composto da un singolo thread: in questo caso ogni thread al livello
utente che esegua una system call finirà per sospendere l’esecuzione dell’intero task fino al
completamento della system call stessa. Per capire l’efficacia dei thread basta confrontare la gestione del
controllo con thread multipli con il modo di operare di più processi. Nel caso di processi multipli,
ciascuno opera in maniera indipendente e possiede i propri registri e i propri dati. Invece nel caso di
thread multipli, è possibile la condivisione di registri e dati con un evidente risparmio di risorse e
maggiore velocità di esecuzione.
Quelli di informatica www.quellidiinformatica.tk - Scaricato da www.cplusplus.it
Anche i thread, in maniera analoga ai processi possono trovarsi ne
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Domande Teoria Sistemi operativi
-
Appunti Sistemi Operativi - Seminari sistemi UAV , sistemi operativi NuttX-ROS
-
Domande Sistemi operativi
-
Soluzione teoria Sistemi operativi