[pipe] Somma parallelizzata

Lo scopo della verifica è implementare un software per parallelizzare il calcolo della somma di una serie di numeri interi non negativi. Nell’archivio zip vengono fornite tre versioni di un programma che:

  1. avvia un numero casuale di istanze del programma somma, che dovete implementare e posizionare nella stessa cartella;
  2. invia a ciascuna istanza, tramite una pipe, una sequenza di lunghezza casuale di numeri interi (non negativi);
  3. legge da un’altra pipe il risultato della somma degli interi inviati.

Il programma somma deve leggere dalla pipe la sequenza di numeri e scrivere la somma. I path delle due pipe sono passati al vostro programma tramite riga di comando. In particolare, argv[1] conterrà il nome della pipe da cui leggere gli interi, mentre in argv[2] trovate il nome della pipe in cui scrivere il risultato. I numeri da sommare vengono scritti nella pipe come stringhe di caratteri separati da virgola. Il risultato dovrà essere scritto nella pipe anch’esso come stringa.

In base all’architettura della macchina che state usando, utilizzate il binario:

  • sum-x86: Linux, 32 bit;
  • sum-x86-64: Linux, 64 bit;
  • sum-macOS: macOS.

Il programma sum supporta la flag -d (debug mode) che può essere utilizzata per visualizzare quali sono i numeri che sono stati inviati ad ogni processo. Quando usate questa modalità, il numero di istanze avviate è pari a 2 e il numero di interi inviati ad ogni istanza è sufficientemente basso da produrre un risultato leggibile.

Ecco un esempio di esecuzione in modalità debug:

>>> ./sum -d
Al processo con PID 17197 sono stati inviati i seguenti numeri:
 214
 246
 107
 122
 40
Al processo con PID 17198 sono stati inviati i seguenti numeri:
 9
 16
 162
Processo 17197 - Ricevuta somma 729
Processo 17198 - Ricevuta somma 187
La somma totale e' 916

In questo esempio, il processo 17917 ha letto dalla pipe la stringa 214,246,107,122,40, (non viene inviato il terminatore '\0') e ha restituito la stringa 729. Analogamente, il processo 17198 ha letto la stringa 9,16,162, e ha scritto nella pipe la stringa 187.

Segue ora un esempio di esecuzione in modalità normale:

>>> ./sum 
Processo 17271 - Ricevuta somma 852081
Processo 17272 - Ricevuta somma 121682
Processo 17273 - Ricevuta somma 796582
Processo 17274 - Ricevuta somma 1222255
Processo 17275 - Ricevuta somma 942321
La somma totale e' 3934921
Visualizza la soluzione

Soluzione

Segue una possibile soluzione commentata della verifica:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

/*
 * L'obiettivo della verifica è la parallelizzazione della somma di una sequenza
 * di numeri interi. Viene fornito un programma 'sum' che esegue un numero
 * casuale di istanze del programma 'somma' che noi dobbiamo implementare.
 * Ciascuna istanza riceve su una pipe una stringa contenente una sequenza di
 * numeri interi non negativi separati da virgola e deve scrivere la somma di
 * questi numeri (sempre come stringa) all'interno di una seconda pipe.
 * I path delle due pipe da utilizzare sono passati al nostro programma quando
 * viene eseguito da 'sum'.
 */
int main(int argc, char *argv[]) {
    int fd_in, fd_out, sum, n;
    char c, *str_sum;
    size_t str_size;

    /* Controllo che i path delle pipe siano stati forniti al programma. */
    if (argc != 3) {
        fprintf(stderr, "Utilizzo: ./somma <pipe_in> <pipe_out>");
        exit(EXIT_FAILURE);
    }

    /* Apro le pipe. Termina il programma in caso di errore. */
    fd_in = open(argv[1], O_RDONLY);
    if (fd_in < 0) {
        fprintf(stderr, "Non riesco ad aprire '%s' in lettura.\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    fd_out = open(argv[2], O_WRONLY);
    if (fd_out < 0) {
        fprintf(stderr, "Non riesco ad aprire '%s' in scrittura.\n", argv[2]);
        exit(EXIT_FAILURE);
    }

    /* Leggi dalla pipe un carattere per volta. Se il carattere letto è:
     * - una cifra, costruisci il numero corrente in maniera incrementale;
     * - una virgola, il numero è stato letto e può essere aggiunto alla somma
     *   parziale;
     * - un altro carattere, si tratta di un errore.
     * Il ciclo termina la funzione read ritorna zero, ovvero quando la pipe
     * è vuota ed è stata chiusa in lettura dal programma 'sum', quindi la
     * sequenza di numeri da leggere è terminata. */
    sum = n = 0;
    while (read(fd_in, &c, sizeof(char))) {
        if (c >= '0' && c <= '9') {
            n = n * 10 + (c - '0');
        } else if (c == ',') {
            sum += n;
            n = 0;
        } else {
            fprintf(stderr, "Carattere non atteso: %c\n", c);
            exit(EXIT_FAILURE);
        }
    }
    close(fd_in);

    /* Alloca una stringa di dimensione sufficiente per rappresentare il
     * risultato ed invialo sulla pipe. Chiudi la pipe una volta effettuata
     * la scrittura. */
    str_size = snprintf(NULL, 0, "%d", sum) + 1;
    str_sum = malloc(sizeof(char) * str_size);
    snprintf(str_sum, str_size, "%d", sum);
    write(fd_out, str_sum, str_size);
    free(str_sum);
    close(fd_out);

    return EXIT_SUCCESS;
}