Nome e cognome | |
---|---|
Mattia Paternoster | mattia.paternoster@studenti.unitn.it |
Francesco Previdi | francesco.previdi@studenti.unitn.it |
Tommaso Manzana | tommaso.manzana@studenti.unitn.it |
Daniele Soprani | daniele.soprani@studenti.unitn.it |
NB. C'è anche una versione pdf di questo di file nella cartella principale del progetto.
L'albero dei processi è stato creato seguendo la gerarchia rappresentata nello schema seguente:
All'avvio del programma M il processo M si preoccuperà di creare 4 figli (S1,S2,S3,S4) i quali a loro volta creeranno 2 figli che rappresenteranno il led e l'interruttore/pulsante. Il primo processo della coda dei processi P sarà figlio di M e sarà creato anch'esso all'avvio del programma. Il processo M è formato da 4 thread che hanno i seguenti compiti:
- Thread 1: gestione input utente
- Thread 2: legge i messaggi ricevuti dai processi S1,S2,S3,S4
- Thread 3: legge i messaggi ricevuti dai processi P
- Thread 4: legge i messaggi ricevuti da Q
Il programma Q è indipendente da tutti gli altri e non ha alcuna relazione di parentela con gli altri processi.
La comunicazione tra i vari processi e programmi è rappresentata in questo schema:
La comunicazione tra M e S avviene utilizzando una pipe anonima in scrittura per ogni processo S, in quanto è necessario un canale di comunicazione esclusivo per ogni S. In lettura invece è necessaria un'unica pipe in quanto il processo M è l'unico reader. all'invio di un comando, quindi, M si occuperà di inoltrarlo al corrispondente processo S. Quest'ultimo, invece, avrà il compito di elaborare il comando e inviare il risultato dell'operazione ad M; inoltre si occuperà di informarlo in caso di variazione dello stato di led ed interruttori.
Per la comunicazione tra S, T e L si utilizzano esclusivamente i segnali.
Una volta ricevuto il comando "premi" da M, S invierà un segnale SIGURS 1
al relativo pulsante/interruttore T, il quale cambierà il suo stato e risponderà con un segnale SIGURS 1
di conferma. Ricevuta la conferma S invierà un ulteriore segnale SIGURS 1
al led L il quale cambierà lo stato e risponderà con un segnale SIGURS 2
di conferma.
La comunicazione tra M e P avviene attraverso due pipe anonime (scrittura e lettura) e un segnale SIGURS 1
.
Quando è necessario creare un nuovo processo P, M scrive sulla pipe l'id del nuovo processo e successivamente invia un segnale SIGURS 1
all'ultimo P della coda. P legge dalla pipe l'id comunicatogli ed effettua il fork, scrivendo sulla pipe l'id del nuovo processo creato.
M mantiene la coda dei processi P memorizzandone id e pid.
In caso di eliminazione di un processo della coda, verranno inviati dei segnali SIGTERM
partendo dall'ultimo discendente, risalendo fino al processo selezionato; Successivamente verrà ricreata la coda dei processi.
La comunicazione tra Q e M avviene attraverso due FIFO, una in scrittura e una in lettura.
La comunicazione tra Q e Pi avviene attraverso un segnale SIGURS 2
(verso Pi) ed una FIFO in lettura.
Sulla base del comando inviato su Q, questo invia una richiesta diversa ad M:
-
comando
pos <x>
: Q chiede ad M di inviare il pid corrispondente al processo P in posizionex
all'interno della coda. M risponde con il pid richiesto. Q invia quindi un segnaleSIGURS 2
al P corrispondente al pid ottenuto in precedenza, il quale scrive nella FIFO il proprio id. -
comando
id <y>
: Q chiede ad M il numero di processi P in coda in modo da effettuare una ricerca binaria sull'id. L'algoritmo è spiegato di seguito:- Q parte chiedendo il pid del processo in posizione centrale nella coda;
- M risponde con il pid corrispondente;
- Q invia quindi un segnale
SIGURS 2
al P corrispondente al pid ottenuto in precedenza, il quale scrive nella FIFO il proprio id; - Q confronta l'id con
y
:- se l'id corrisponde, viene restituita la posizione del processo P ricercato;
- altrimenti prosegue la ricerca binaria. Dovesse non trovare tale id verrà stampato un errore;
- Per mancanza di tempo non tutti i possibili casi di errore sono stati gestiti. Alcuni esempi:
- Nel caso venga killato dall'utente, tramite terminale, un processo utile al funzionamento del progetto non è detto che il programma termini correttamente, gestisca l'errore e non è garantito che continui a funzionare correttamente.
- Non è stata implementata l'eventualità che vi siano più processi M. Nel caso vi siano più processi M ci sarà un problema di comunicazione tra Q e M_1/M_2.
Nella cartella è presente un Dockerfile con il quale è possibile creare automaticamente l'ambiente per il progetto. Il Dockerfile si occuperà di "costruire" un container Docker nel quale verrà scaricato ed installato Ubuntu 18.04.04, le build-essentials (compilatore gcc e make) e l'editor nano. Una volta costruito l'ambiente, lo script si occuperà di copiare i file necessari all'interno del container. È possibile testare ed eseguire il progetto anche non utilizzando il file docker. Seguono alcune indicazioni sull'utilizzo.
- Aprite un terminale nella cartella del progetto e digitate il seguente comando
docker build --pull --rm -f "Dockerfile" -t labso:latest "."
- Successivamente per avviare ed entrare nel container creato utilizzare
docker run --rm -it labso:latest
- Per aprire più terminali collegati allo stesso container
- Aprire un nuovo terminale
- Ottenete il container_id utilizzando
docker ps
- Successivamente aprite un nuovo terminale con
docker exec -it <container_id> bash
- Semplicemente copiate la cartella
src
, ilmakefile
e volendo lo script di comodointerruttore.sh
all'interno del vostro container docker o macchina virtuale Ubuntu 18.04.
- Entrare nella cartella del progetto e digitare
make build
. - Utilizzare
./M
per eseguire il programma M./Q
per eseguire il programma Q
- Per rimuovere gli eseguibili e i file temporanei è possibile utilizzare
make clean
. - Utilizzando
make help
verranno mostrate a video alcune indicazioni utili all'utilizzo del progetto.
- Appena avviato il programma sarà richiesto all'utente di specificare il tipo dei 4 dispositivi di input tra interruttore o pulsante.
- Successivamente si sarà liberi di eseguire qualsiasi comando tra quelli proposti.
- Scrivendo
help
in qualsiasi momento durante l'esecuzione di M verranno mostrati a video i comandi disponibili con una breve descrizione. Con essi sarà possibile manipolare gli interruttori/pulsanti, i led, e la coda dei processi P. - Ogni volta che un led e/o interruttore-pulsante cambia stato sarà fornito un feedback a video. Inoltre utilizzando il comando
status
sarà possibile conoscere in qualsiasi momento lo stato di tutti gli interruttori-pulsanti e led. - La coda dei processi P verrà manipolata (se possibile) ogni qualvolta venga premuto un pulsante-interruttore e sarà fornito un feedback a video anche in caso di fallimento. Inoltre tramite il comando
list
sarà possibile visualizzare a video la coda dei processi P in qualsiasi momento. - Sono disponibili alcune funzionalità extra:
- Tutti i processi hanno un nome diverso e quindi aprendo un altro terminale e utilizzando il comando
ps -a
(il programma M dovrà essere aperto nel terminale principale) sarà possibile visualizzare la situazione corrente dei processi attivi. Utilizzandops -a -l | grep P
è possibile visualizzare tutti i processi facenti parte della coda dei processi P. - Inviando un
SIGURS1
ad un interruttore è possibile cambiarne lo stato. (es. il pid del processoT1_I
è 81; posso aprire un nuovo terminare ed eseguire il comandokill -10 81
per cambiare lo stato dell'interruttore. Si può facilmente notare che questa è un alternativa all'utilizzo del comandopremi 1
). Utilizzando lo script bashinterruttore.sh <numero_interruttore>
è possibile inviare unSIGURS1
ad uno dei quattro interruttori in maniera più semplice e veloce.
- Tutti i processi hanno un nome diverso e quindi aprendo un altro terminale e utilizzando il comando
- Appena avviato il programma verrà mostrato all'utente il menù dei comandi.
- Dopo aver eseguito i comandi
id
opos
, nel caso in cui M non sia aperto il programma Q resterà in attesa fino a che M non verrà aperto (c'è la possibilità di chiuderlo utilizzandoCTRL+C
).