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 |

[T10] Esercitazione 10

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-get-cmd-line compilando con `gcc e1_main.c e1.c -o e1´.

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

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 1;
  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 quit e verificare la terminazione della shell

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

Domande

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

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

Domanda 6

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

Domanda 7

Domanda 3 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)
#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)
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.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);
    }
}

void deallocate_cmd(char* argv[MAX_TOKENS]) {
	while (*argv != NULL)
		free(*argv++);
}

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);
	deallocate_cmd(argv);
    }
    return EXIT_SUCCESS;
}

Domande
  1. C. Viene stampato “Figlio: 1” e poi “Padre: 1
  2. A. Nell’ordine (una per linea): “Figlio 1: x=10”, “Figlio 2: x=20”, “Figlio 3: x=30”, “Padre: x=40
  3. C. Nell’ordine (una per linea): “--- started ---”, “Hello