Sistemi di Calcolo

Corso di Laurea in Ingegneria Informatica e Automatica

Home | Avvisi | Diario Lezioni | Esercitazioni | Esami | Materiale Didattico | Valutazioni Studenti | Lezioni di Camil Demetrescu |

[T07] Esercitazione del 12 aprile 2019

Istruzioni per l’esercitazione:

Per maggiori informazioni fate riferimento al regolamento delle esercitazioni.

Esercizio 1 (Parsing di una linea da stdin in C)

Questo è un esercizio di mera conoscenza del linguaggio C. Il canale stdin (di tipo FILE*) modella in C la sorgente di caratteri ASCII che proviene come input, salvo diversamente specificato, dal terminale. Si chiede di scrivere nel file E1-get-cmd-line/e1.c una funzione void get_cmd_line(char* argv[]); che legge la prossima linea di testo da stdin, estrae ciascun token (sequenza consecutiva di caratteri, esclusi spazi, tab \t e ritorni a capo \n) e produce un array di al più n<=MAX_TOKENS=64 stringhe come segue:

Assumere che la linea di testo letta da stdin contenga al più 1024 caratteri compreso il ritorno a capo \n.

Esempio: se la linea letta da stdin è rm -f .DS_store, la chiamata get_cmd_line(argv) restituisce in argv l’array di stringhe {"rm", "-f". ".DS_Store", NULL}.

Suggerimenti. Basandosi sul comando man (oppure la documentazione online cercando funzione opengroup) usare:

L’array di stringhe prodotto deve essere deallocabile con la semplice funzione:

void free_args(char* argv[]) {
    while (*argv) free(argv++);
}

Usare il main di prova nella directory di lavoro E1-parse-line compilando con `gcc e1_main.c e1.c -o e1´.

Esercizio 2 (Scrittura di una semplice shell dei comandi Linux)

[Svolgere questo esercizio solo dopo aver svolto l’Esercizio 1]

Una shell è un programma che chiede all’utente di eseguire altri programmmi sotto forma di nuovi processi, passandogli eventuali argomenti specificati dall’utente. Una shell fornisce normalmente un prompt, vale a dire un breve testo (es. $, >, ecc.) che segnala all’utente che la shell è in attesa di ricevere comandi.

Si chiede di scrivere nel file E2-shell/e2.c una semplice shell sotto forma di una funzione int do_shell(const char* prompt); che prende come parametro la stringa di prompt e si comporta come segue;

  1. stampa il prompt;
  2. attende che l’utente inserisca in stdin un comando seguito dai suoi eventuali argomenti (es: ls -l, dove ls è il comando e -l è il suo unico argomento). Per ottenere comando e argomenti da stdin usare il risultato dell’esercizio 2;
  3. se il comando è vuoto (NULL) tornare al punto 1;
  4. se il comando è quit terminare con successo la shell;
  5. creare con fork un nuovo processo che esegua il comando con gli argomenti dati usando execvp;
  6. se il comando si riferisce a un programma inesistente riportare l’errore unknown command seguito dal nome del comando e tornare al punto 1;
  7. attendere con wait la terminazione del processo e tornare al punto 1.

Per valutare il corretto funzionamento della shell, effettuare i seguenti quattro test:

  1. inserire ls -l e verificare che listi la directory corrente
  2. inserire echo hello e verificare che venga stampato hello
  3. inserire sergente hartman e verificare che venga stampato un messaggio di errore
  4. inserire quite verificare la terminazione della shell

Il risultatao di ogni system call deve essere controllato e in caso di errore segnalato con perror e terminazione EXIT_FAILURE.

Esercizio 3 (Domande)

Rispondi alle seguenti domande, tenendo conto che una risposta corretta vale 1 punti, mentre una risposta errata vale 0 punti.

Domanda 1 Quale tra le seguenti affermazioni è FALSA?

Domanda 2 Quale tra le seguenti affermazioni è VERA?

Domanda 3 Quale tra le seguenti affermazioni è VERA?

Domanda 4 Quale tra le seguenti affermazioni è VERA?

Domanda 5 Quale tra le seguenti affermazioni è VERA?

Domanda 6 Relativamente al seguente codice C (supponendo che la fork non generi un errore), quale tra le seguenti affermazioni è VERA?

Domanda 6

Domanda 7 Relativamente al seguente codice C (supponendo che la fork non generi un errore), qual è l’output atteso?

Domanda 7

Domanda 8 Relativamente al seguente codice C (supponendo che la execv non generi un errore), qual è l’output atteso?

Domanda 8

Soluzioni

Esercizio 1 (Parsing di una linea da stdin in C)

e1.c

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "e1.h"

char* dup_string(const char* in) {
    size_t n = strlen(in);
    char* out = malloc(n + 1);
    strcpy(out, in);
    return out;
}

void get_cmd_line(char* argv[MAX_TOKENS]) {
    int argc = 0;
    char line[MAX_LINE];
    fgets(line, MAX_LINE, stdin);
    char* token = strtok(line, " \t\n");
    argc = 0;
    while (argc < MAX_TOKENS && token != NULL) {
        argv[argc++] = dup_string(token);
        token = strtok(NULL, " \t\n");
    }
    argv[argc] = NULL;
}
Esercizio 2 (Scrittura di una semplice shell dei comandi Linux)

e2.c

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include "../e2.h"

#define MAX_LINE    1024
#define MAX_TOKENS  64

void do_cmd(char* argv[MAX_TOKENS]) {
    int res;
    pid_t pid = fork();

    if (pid == -1) {
        perror("fork error");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        res = execvp(argv[0], argv);
        if (res == -1) {
            printf("unkwnown command %s\n", argv[0]);
            _exit(EXIT_FAILURE);
        }
    }

    res = wait(NULL);
    if (pid == -1) {
        perror("wait error");
        exit(EXIT_FAILURE);
    }
}

char* dup_string(const char* in) {
    size_t n = strlen(in);
    char* out = malloc(n + 1);
    strcpy(out, in);
    return out;
}

void get_cmd_line(char* argv[MAX_TOKENS]) {
    int argc = 0;
    char line[MAX_LINE];
    fgets(line, MAX_LINE, stdin);
    char* token = strtok(line, " \t\n");
    argc = 0;
    while (argc < MAX_TOKENS && token != NULL) {
        argv[argc++] = dup_string(token);
        token = strtok(NULL, " \t\n");
    }
    argv[argc] = NULL;
}

int do_shell(const char* prompt){
    for (;;) {
        printf("%s", prompt);
        char* argv[MAX_TOKENS];
        get_cmd_line(argv);
        if (argv[0] == NULL) continue;
        if (strcmp(argv[0], "quit") == 0) break;
        do_cmd(argv);
    }
    return EXIT_SUCCESS;
}
Esercizio 3 (Domande)
  1. D. Funzioni wrapper di system call come read, write, fork e _exit vengono eseguite interamente in “kernel mode”
  2. E. Nessuna delle precedenti
  3. D. L’interrupt generata dal timer è asincrona
  4. B. Il valore di ritorno di una system call viene scritto nel registro EAX.
  5. B. perror stampa un messaggio di errore che dipende dal valore contenuto nella variabile errno
  6. C. Viene stampato “Figlio: 1” e poi “Padre: 1
  7. A. Nell’ordine (una per linea): “Figlio 1: x=10”, “Figlio 2: x=20”, “Figlio 3: x=30”, “Padre: x=40
  8. C. Nell’ordine (una per linea): “--- started ---”, “Hello