Sistemi di Calcolo

Corso di Laurea in Ingegneria Informatica e Automatica - A.A. 2017-2018

HomePage | Avvisi | Diario lezioni | Programma | Materiale didattico | Esami | Forum | Login

Revision [2678]

Last edited on 2017-02-13 16:48:11 by DanieleDelia
Additions:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
Deletions:
int sigaction(int signum, const struct signum *act, struct signum *oldact);


Revision [2518]

Edited on 2016-12-02 18:29:18 by EmilioCoppa
Additions:
if (res == 0 && S_ISREG(path_stat.st_mode) == 1)
return 0;
Deletions:
if (res == 0)
return S_ISREG(path_stat.st_mode);


Revision [2510]

Edited on 2016-12-02 17:30:07 by CamilDemetrescu
Additions:
[ [[SolEserc05AA1617 soluzioni]] ]


Revision [2509]

Edited on 2016-12-02 17:14:36 by EmilioCoppa
Additions:
return S_ISREG(path_stat.st_mode);
Deletions:
return S_ISREG(path_stat.st_mode


Revision [2508]

Edited on 2016-12-02 15:03:41 by CamilDemetrescu
Additions:
// ed esce dalla funzione se il canale stdin è terminato (CRTL+D durante fgets fornisce NULL)
Deletions:
// ed esce dalla funzione se la riga e' vuota


Revision [2506]

Edited on 2016-12-02 14:25:55 by CamilDemetrescu
Additions:
// argv[0] = "ls" (puntatore alla sottostringa ls in
// tok_buf[0])
// 2. per inizializzare argv[0], si suggerisce di farlo puntare alla
// sottostringa di tok_buf[0] che rappresenta il nome del file
// (esempio, ls in /bin/ls) -- si suggerisce di usare la funzione
// POSIX strrchr
make_argv(argv, tok_buf, tok_num); // crea argv per processo
Deletions:
// - argv0: [appoggio] buffer preallocato di caratteri da cui ottenere argv[0]
char argv0[MAX_LINE_SIZE],
// argv[0] = "ls" (puntatore al buffer argv0 ottenuto da
// tok_buf[0] togliendo il path)
// 2. per inizializzare argv[0], si suggerisce di copiare tok_buf[0]
// nel buffer argv0 passato e poi estrarre il puntatore al nome
// del file in argv0 usando la funzione basename (si veda man
// basename o google "opengroup basename")
char argv0[MAX_LINE_SIZE]; // buffer per contenere argv[0]
make_argv(argv, argv0, tok_buf, tok_num); // crea argv per processo


Revision [2505]

Edited on 2016-12-02 12:55:42 by CamilDemetrescu
Additions:
==Esercizio 1 - shell comandi minimale==
==Esercizio 4 - comando interno ##cat##==
==Esercizio 5 - comando interno printenv ==
In questo esercizio è richiesto di implementare il comando interno ##printenv## nella nostra shell. La sintassi del comando deve essere:
printenv
I segnali sono un meccanismo che permette ad un processo di ricevere notifiche dall’OS o da altri processi. Quando un processo ricevente può stabilire che azione eseguire in risposta ad un certo segnale, tale segnale è detto catturabile. In particolare, il processo può decidere di ignorarlo, di eseguire semplicemente l’operazione di default prevista per quel segnale, oppure di eseguire un metodo specifico (//signal handler//) indicato dal programmatore. Questo tipo di configurazione viene registrato dal programma attraverso la system call ##sigaction## che ha il seguente prototipo:
int sigaction(int signum, const struct signum *act, struct signum *oldact);
~- ##sa_handler ## va assegnato a ##SIG_DFL## per eseguire l’azione di default, a ##SIG_IGN## per ignorare l’arrivo del segnale, o altrimenti va lasciato a ##0##
~- ##sa_flags ## va lasciato a ##0## se l’azione da eseguire è il default o il segnale va ignorato, altrimenti va impostato a ##SA_SIGINFO## per usare un proprio signal handler
~1) "eliminare" ##"&"## dall'array degli argomenti, rimpiazzandolo con ##NULL##
~2) dopo l'avvenuta ##fork##, non attendere esplicitamente la terminazione del comando esterno (//suggerimento: usare un flag per distinguere se il comando va eseguito in background o in foreground//), ma stampare semplicemente il PID del processo appena creato e tornare nel loop della linea di comando
~1) (opzionale) verifichi che il segnale ricevuto sia effettivamente ##SIGCHLD##
~2) esegua la system call ##wait## per liberare le risorse rimanenti del processo figlio
~3) si assicuri che non vi siano stati errori e stampi il PID del processo figlio appena rimosso
~4) stampi l'exit status del processo figlio (si vedano le macro definite per ##wait(2)##, in particolare ##WEXITSTATUS##)
int kill(pid_t pid, int sig);
Dove il primo argomento è il PID del processo destinatario e il secondo il segnale da inviare (macro definite in ####).
L'obiettivo di questo esercizio è implementare in ASM x86 una funzione ##mykill## che prenda gli stessi argomenti come per la ##kill## C in alto ed effettui la system call corrispondente mediante l'istruzione di trapping ##INT##.
#include
#include // pid_t
#include // macro segnali (SIGKILL)
#include
if (argc != 2) {
printf("Sintassi: %s \n", argv[0]);
return 1;
pid_t pid = atoi(argv[1]);
int ret = mykill(pid, SIGKILL);
if (ret != 0) {
errno = -ret; // INT $0x80 non imposta errno letto da perror()
perror("mykill fallita");
printf("[valore di ritorno: %d]\n", ret);
return 1;
printf("Segnale KILL inviato!\n");
Deletions:
==Esercizio 1 - shell comandi minimale (no background)==
==Esercizio 4 - comando interno cat==
==Esercizio 5 - comando interno ##printenv## ==
In questo esercizio è richiesto di implementare il comando interno ##printenv## nella nostra shell. La sintassi del comando deve essere: printenv
I segnali sono un meccanismo che permette ad un processo di ricevere notifiche dall’OS o da altri processi. Quando un processo ricevente può stabilire che azione eseguire in risposta ad un certo segnale, tale segnale è detto catturabile. In particolare, il processo può decidere di ignorarlo, di eseguire semplicemente l’operazione di default prevista per quel segnale, oppure di eseguire un metodo specifico (//signal handler//) indicato dal programmatore. Questo tipo di configurazione viene registrato dal programma attraverso la system call ##sigaction## che ha il seguente prototipo: ##int sigaction(int signum, const struct signum *act, struct signum *oldact);##
~- ##sa_handler ## va assegnato a ##SIG_DFL## per eseguire l’azione di default, a ##SIG_IGN## per ignorare l’arrivo del segnale, o altrimenti va lasciato a ##0##
~- ##sa_flags ## va lasciato a ##0## se l’azione da eseguire è il default o il segnale va ignorato, altrimenti va impostato a ##SA_SIGINFO## per usare un proprio signal handler
%%(c;)
1) "eliminare" ##"&"## dall'array degli argomenti, rimpiazzandolo con ##NULL##
2) dopo l'avvenuta ##fork##, non attendere esplicitamente la terminazione del comando esterno (//suggerimento: usare un flag per distinguere se il comando va eseguito in background o in foreground//), ma stampare semplicemente il PID del processo appena creato e tornare nel loop della linea di comando
1) (opzionale) verifichi che il segnale ricevuto sia effettivamente ##SIGCHLD##
2) esegua la system call ##wait## per liberare le risorse rimanenti del processo figlio
3) si assicuri che non vi siano stati errori e stampi il PID del processo figlio appena rimosso
4) stampi l'exit status del processo figlio (si vedano le macro definite per ##wait(2)##, in particolare ##WEXITSTATUS##)
##int kill(pid_t pid, int sig);##
Dove il primo argomento è il PID del processo destinatario e il secondo il segnale da inviare (macro definite in ####).
L'obiettivo di questo esercizio è implementare in ASM x86 una funzione ##mykill## che prenda gli stessi argomenti come per la ##kill## C in alto ed effettui la system call corrispondente mediante l'istruzione di trapping ##INT##.
#include
#include // pid_t
#include // macro segnali (SIGKILL)
#include
if (argc != 2) {
printf("Sintassi: %s \n", argv[0]);
return 1;
pid_t pid = atoi(argv[1]);
int ret = mykill(pid, SIGKILL);
if (ret != 0) {
errno = -ret; // INT $0x80 non imposta errno letto da perror()
perror("mykill fallita");
printf("[valore di ritorno: %d]\n", ret);
return 1;
printf("Segnale KILL inviato!\n");


Revision [2504]

Edited on 2016-12-02 12:49:43 by CamilDemetrescu
Additions:
return -1;
Deletions:
return -1;


Revision [2503]

Edited on 2016-12-02 12:48:28 by CamilDemetrescu
Additions:
cat
==Esercizio 5 - comando interno ##printenv## ==
I segnali sono un meccanismo che permette ad un processo di ricevere notifiche dall’OS o da altri processi. Quando un processo ricevente può stabilire che azione eseguire in risposta ad un certo segnale, tale segnale è detto catturabile. In particolare, il processo può decidere di ignorarlo, di eseguire semplicemente l’operazione di default prevista per quel segnale, oppure di eseguire un metodo specifico (//signal handler//) indicato dal programmatore. Questo tipo di configurazione viene registrato dal programma attraverso la system call ##sigaction## che ha il seguente prototipo: ##int sigaction(int signum, const struct signum *act, struct signum *oldact);##
~- ##sa_handler ## va assegnato a ##SIG_DFL## per eseguire l’azione di default, a ##SIG_IGN## per ignorare l’arrivo del segnale, o altrimenti va lasciato a ##0##
~- ##sa_flags ## va lasciato a ##0## se l’azione da eseguire è il default o il segnale va ignorato, altrimenti va impostato a ##SA_SIGINFO## per usare un proprio signal handler
~- ##sa_sigaction## va impostato con l’indirizzo della funzione signal handler scritta dal programmatore, altrimenti va lasciato a ##0## (ciò accade quando ##sa_handler## è stato settato a ##SIG_IGN## o a ##SIG_DFL##)
%%(c;)
void my_handler(int sig, siginfo_t *siginfo, void *context);
Deletions:
cat path-to-file
==Esercizio 5 - comando interno printenv ==
I segnali sono un meccanismo che permette ad un processo di ricevere notifiche dall’OS o da altri processi. Quando un processo ricevente può stabilire che azione eseguire in risposta ad un certo segnale, tale segnale è detto catturabile. In particolare, il processo può decidere di ignorarlo, di eseguire semplicemente l’operazione di default prevista per quel segnale, oppure di eseguire un metodo specifico (//signal handler//) indicato dal programmatore. Questo tipo di configurazione viene registrato dal programma attraverso la system call ##sigaction## che ha il seguente prototipo:
##int sigaction(int signum, const struct signum *act, struct signum *oldact);##
- ##sa_handler ## va assegnato a ##SIG_DFL## per eseguire l’azione di default, a ##SIG_IGN## per ignorare l’arrivo del segnale, o altrimenti va lasciato a ##0##
- ##sa_flags ## va lasciato a ##0## se l’azione da eseguire è il default o il segnale va ignorato, altrimenti va impostato a ##SA_SIGINFO## per usare un proprio signal handler
- ##sa_sigaction## va impostato con l’indirizzo della funzione signal handler scritta dal programmatore, altrimenti va lasciato a ##0## (ciò accade quando ##sa_handler## è stato settato a ##SIG_IGN## o a ##SIG_DFL##)
##void my_handler(int sig, siginfo_t *siginfo, void *context);##


Revision [2502]

Edited on 2016-12-02 12:47:15 by CamilDemetrescu
Additions:
cat path-to-file
Deletions:
cat


Revision [2501]

Edited on 2016-12-02 12:46:28 by CamilDemetrescu
Additions:
In questo esercizio è richiesto di implementare il comando ##cat## come funzione built-in (comando interno) nella nostra shell. La sintassi del comando interno ##cat## deve essere la seguente:
cat
Deletions:
In questo esercizio è richiesto di implementare il comando ##cat## come funzione built-in (comando interno) nella nostra shell. La sintassi del comando interno ##cat## deve essere la seguente: cat


Revision [2500]

Edited on 2016-12-02 12:44:46 by CamilDemetrescu
Additions:
// parse_cmd_line -- [fornita]
// do_internal_cmd -- [fornita]
// main -- [fornito]
Deletions:
// parse_cmd_line
// do_internal_cmd
// main


Revision [2499]

Edited on 2016-12-02 12:43:20 by EmilioCoppa
Additions:
// la dimensione del buffer è almeno size byte
// - buffer: array di char da cui leggere i dati da scrivere
// la dimensione del buffer è almeno size byte
Deletions:
// la dimensione del buffer è almeno size byte
// - buffer: array di char su cui leggere
// la dimensione del buffer è almeno size byte


Revision [2498]

Edited on 2016-12-02 12:41:05 by EmilioCoppa
Additions:
// Parametri:
// - path: un percorso nel filesystem
// Parametri (uguali alla funzione main):
// - argc: numero di argomenti passati al comando
// - argv: argomenti passati al programma
// Parametri:
// - fd: file descriptor
// - buffer: array di char su cui scrivere i dati letti
// la dimensione del buffer è almeno size byte
// - size: numero di byte che dovrebbero essere letti
// Parametri:
// - fd: file descriptor
// - buffer: array di char su cui leggere
// la dimensione del buffer è almeno size byte
// - size: numero di byte che devono essere scritti
// Restituisce: il numero di byte effettivamente scritti
// Parametri:
// - fd: file descriptor
Deletions:
// Restisce: il numero di byte effettivamente scritti


Revision [2497]

Edited on 2016-12-02 12:33:59 by EmilioCoppa
Additions:
// get_read_descriptor
// ToDO
// read_block
// - 0 in caso di End-of-File (EOF)
// - il numero di byte effettivamente letti,
// ToDo
// write_block
// ToDo
// close_descriptor
// ToDo
// Step 1: verificare che il comando sia stato invocato
// correttamente (conforme rispetto la sintassi)
// [...]

// Step 2: ottenere il file descriptor per la lettura
// [...]
// Step 3: ottenere il file descriptor per la scrittura
// [...]
// Step 4:
// a) leggere un blocco di dati utilizzando read_block()
// b) scrivere in output i dati che sono stati letti utilizzando write_block()
// c) ripetere (a) e (b) finchè non si verifica EOF in lettura.
// [...]
// Step 5: chiudere i descrittori
// [...]
Deletions:
// get_read_descriptor
// ToDO
// read_block
// - 0 in caso di End-of-File (EOF)
// - il numero di byte effettivamente letti,
// ToDo
// write_block
// ToDo
// close_descriptor
// ToDo
// Step 1: verificare che il comando sia stato invocato
// correttamente (conforme rispetto la sintassi)
// [...]
// Step 2: ottenere il file descriptor per la lettura
// [...]
// Step 3: ottenere il file descriptor per la scrittura
// [...]
// Step 4:
// a) leggere un blocco di dati utilizzando read_block()
// b) scrivere in output i dati che sono stati letti utilizzando write_block()
// c) ripetere (a) e (b) finchè non si verifica EOF in lettura.
// [...]
// Step 5: chiudere i descrittori
// [...]


Revision [2496]

Edited on 2016-12-02 12:32:06 by CamilDemetrescu

No differences.

Revision [2495]

Edited on 2016-12-02 12:31:17 by CamilDemetrescu
Additions:
else
return -1;
// get_read_descriptor
// Funzione che ritorna il file descriptor in lettura
// In caso di errore, termina l'esecuzione del programma.
// Restituisce: il file descriptor
int get_read_descriptor(int argc, char * argv[]) {
// ToDO
// read_block
// Funzione che legge fino a size byte dal file descriptor fd.
// In caso di errore, termina l'esecuzione del programma.
// - 0 in caso di End-of-File (EOF)
// - il numero di byte effettivamente letti,
int read_block(int fd, char * buffer, size_t size) {
// ToDo
// write_block
// Funzione che scrive _esattamente_ size byte sul file descriptor fd.
// In caso di errore, termina l'esecuzione del programma.
// Gestire l'eventualità che una chiamata a write()
// possa scrivere un minor numero di byte rispetto
// a quanto richiesto. In tal caso, occorre scrivere
// su fd i dati mancati.
// Restisce: il numero di byte effettivamente scritti
int write_block(int fd, char * buffer, size_t size) {
// ToDo
// close_descriptor
void close_descriptor(int fd) {
// ToDo
}
int main(int argc, char * argv[]) {
// Step 1: verificare che il comando sia stato invocato
// correttamente (conforme rispetto la sintassi)
// [...]
// Step 2: ottenere il file descriptor per la lettura
// [...]
// Step 3: ottenere il file descriptor per la scrittura
// [...]
// Step 4:
// a) leggere un blocco di dati utilizzando read_block()
// b) scrivere in output i dati che sono stati letti utilizzando write_block()
// c) ripetere (a) e (b) finchè non si verifica EOF in lettura.
// [...]
// Step 5: chiudere i descrittori
// [...]
==Esercizio 4 - comando interno cat==
In questo esercizio è richiesto di implementare il comando ##cat## come funzione built-in (comando interno) nella nostra shell. La sintassi del comando interno ##cat## deve essere la seguente: cat
**Nota bene:** il comando interno ##cat## richiede sempre che sia indicato il file da cui occorre leggere in lettura. Non è supportata la lettura dallo standard input.
**Suggerimento**: ri-utilizzare il più possibile il codice sviluppato per l'esercizio 3.
==Esercizio 5 - comando interno printenv ==
In questo esercizio è richiesto di implementare il comando interno ##printenv## nella nostra shell. La sintassi del comando deve essere: printenv
Il comando deve stampare in standard output tutte le variabili d'ambiente (una per riga). Si ricorda che in un programma C è possibile accedere alla variabili di ambiente attraverso l'array di puntatori a stringhe:
%%(c)
extern char **environ;
Ogni stringa costituisce una variabile d'ambiente.
==Riepilogo sui segnali==
I segnali sono un meccanismo che permette ad un processo di ricevere notifiche dall’OS o da altri processi. Quando un processo ricevente può stabilire che azione eseguire in risposta ad un certo segnale, tale segnale è detto catturabile. In particolare, il processo può decidere di ignorarlo, di eseguire semplicemente l’operazione di default prevista per quel segnale, oppure di eseguire un metodo specifico (//signal handler//) indicato dal programmatore. Questo tipo di configurazione viene registrato dal programma attraverso la system call ##sigaction## che ha il seguente prototipo:
##int sigaction(int signum, const struct signum *act, struct signum *oldact);##
Quando un segnale di tipo ##signum## viene ricevuto, l’azione da eseguire sarà stata specificata all’interno della ##struct sigaction## puntata da ##act##. Il terzo argomento si può invece assumere che sia ##NULL## (serve per salvare le impostazioni precedenti in vigore per quel segnale).
Una ##struct sigaction## per poter essere usata correttamente va prima inizializzata con uno ##0## in ogni suo byte. Dovremo successivamente procedere con l'assegnare al più due dei suoi campi:
- ##sa_handler ## va assegnato a ##SIG_DFL## per eseguire l’azione di default, a ##SIG_IGN## per ignorare l’arrivo del segnale, o altrimenti va lasciato a ##0##
- ##sa_flags ## va lasciato a ##0## se l’azione da eseguire è il default o il segnale va ignorato, altrimenti va impostato a ##SA_SIGINFO## per usare un proprio signal handler
- ##sa_sigaction## va impostato con l’indirizzo della funzione signal handler scritta dal programmatore, altrimenti va lasciato a ##0## (ciò accade quando ##sa_handler## è stato settato a ##SIG_IGN## o a ##SIG_DFL##)
Una funzione che implementa un signal handler deve avere il seguente prototipo:
##void my_handler(int sig, siginfo_t *siginfo, void *context);##
Dove il primo parametro conterrà il segnale ricevuto (infatti si può utilizzare uno stesso handler per più segnali!), il secondo informazioni dettagliate sul segnale (ad esempio, il PID del processo che l’ha generato, e l’UID dell’utente che ha lanciato quel processo), mentre il terzo può essere ignorato.
Esempio di codice per ignorare ##SIGINT## (generato da CTRL-C):
%%(c)
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = SIG_IGN;
int ret = sigaction(SIGINT, &act, NULL);
if (ret == -1) […] // gestione errori
Per ulteriori informazioni si faccia riferimento alla documentazione di ##sigaction## richiamabile da terminale con ##man 2 sigaction##.
==Esercizio 6 - segnali SIGCHLD==
Quando un processo termina, il suo processo genitore riceve un segnale di tipo ##SIGCHLD##. Ciò offre la possibilità al genitore di liberare le risorse associate al processo in modo asincrono: piuttosto che eseguire ##wait## nel flusso di esecuzione principale del genitore (es. mettendosi in attesa dopo una ##fork##), si può decidere di eseguirla all'interno di un signal handler installato per ##SIGCHLD##.
Sfruttare questa informazione per implementare l'esecuzione in background di un comando esterno nella shell costruita in precedenza. In particolare, un comando andrà eseguito in background solo quando l'ultima stringa valida dell'array della linea di comando in input è uguale a ##"&"##.
Quando questa condizione è verificata, modificare il codice per l'esecuzione dei comandi esterni come segue:
1) "eliminare" ##"&"## dall'array degli argomenti, rimpiazzandolo con ##NULL##
2) dopo l'avvenuta ##fork##, non attendere esplicitamente la terminazione del comando esterno (//suggerimento: usare un flag per distinguere se il comando va eseguito in background o in foreground//), ma stampare semplicemente il PID del processo appena creato e tornare nel loop della linea di comando
Inoltre, modificare il metodo principale della shell per installare un signal handler per ##SIGCHLD## tramite la system call ##sigaction## descritta sopra, ed implementare il signal handler in modo che:
1) (opzionale) verifichi che il segnale ricevuto sia effettivamente ##SIGCHLD##
2) esegua la system call ##wait## per liberare le risorse rimanenti del processo figlio
3) si assicuri che non vi siano stati errori e stampi il PID del processo figlio appena rimosso
4) stampi l'exit status del processo figlio (si vedano le macro definite per ##wait(2)##, in particolare ##WEXITSTATUS##)
==Esercizio 7 - wrapper system call ##kill## IA-32==
La system call ##kill## permette di inviare un segnale ad un processo. In C può essere invocata mediante:
##int kill(pid_t pid, int sig);##
Dove il primo argomento è il P