Difference between revisions of "Esperimenti con message passing in C"

From Sistemi Operativi
Jump to navigation Jump to search
(Aggiunta proposta astratta del problema dei filosofi.)
 
(One intermediate revision by the same user not shown)
Line 65: Line 65:
  
 
==Dining philosophers con message passing==
 
==Dining philosophers con message passing==
--[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 11:31, 9 December 2016 (CET) Sto cercando di implementarlo senza un agente centrale che sincronizza ma con solo i filosofi che si scambiano messaggi. Riporto la soluzione con i soli passaggi logici.
+
 
 +
===Soluzione senza gestore delle bacchette===
 +
In questa soluzione non c'è un gestore delle bacchette a cui richiederne l'uso ma i filosofi si scambiano direttamente messaggi per accordarsi. <br>
 +
Essendo la libreria del message passing non attualmente thread safe ho implementato la soluzione utilizzando un processo per ogni filosofo. <br>
 +
C'è un programma chiamato ''startPhilos'' che tramite fork() e exec() esegue i filosofi. StartPhilos assegna anche le bacchette iniziali tramite parametri, io ho seguito la regola che viene assegnata la bacchetta sempre al filosofo con id minore tra i 2 filosofi che potrebbero avere quella bacchetta. In realtà qualsiasi ordine è accettabile, a parte quello in cui si impone una situazione di deadlock. <br>
 +
Ogni stick ha 3 stati: without(il filosofo ha bisogno della bacchetta), clean e dirty. Se a un filosofo manca una bacchetta la richiede al vicino.<br>
 +
Quando a un filosofo viene chiesta una bacchetta la cede solo è sporca, ma la pulisce prima di consegnarla.<br>
 +
Un filosofo mangia solo se ha 2 bacchette pulite, dopo aver mangiato le bacchette sono sporche. <br>
 +
A seguito il ciclo principale della vita di un filosofo:
 
<syntaxhighlight lang=C>
 
<syntaxhighlight lang=C>
 
+
while (1) {
#include <stdint.h>
 
#include <zconf.h>
 
#include <stdlib.h>
 
#include <pthread.h>
 
#include <msg.h>
 
#include <memory.h>
 
 
 
#define MPTYPE np
 
 
 
#define __msg_init(T, X) T ## _msg_init(X)
 
#define _msg_init(T, X) __msg_init(T,X)
 
#define msg_init(X) _msg_init(MPTYPE, X)
 
 
 
void *philo(void *arg) {
 
    //inizializza message passing
 
    //utilizza il mio indice di filosofo come nome
 
    //fai una receive in modo che il thread che gestisce la coda dei messaggi
 
    //si segni che stai aspettando messaggi dal filosofo alla tua destra e alla tua sinistra
 
    while (1) {
 
 
         //think
 
         //think
         //se mentre pensavi il filosofo alla tua destra o alla tua sinistra ti hanno chiesto le bacchette allora concedile
+
        usleep(random() % 2000000);
         //chiedi le bacchette al filosofo alla tua destra e alla tua sinistra e aspetta finché non ti rispondono tutti e due
+
        //request right stick if the philosopher doesn't have it
         //eating
+
        if (rightStick == without) {
         //guarda se ti hanno chiesto le bacchette mentre mangiavi e concedile.
+
            msg_send(rightPhilo, "request", 8);
        // Se non ti hanno ancora chiesto le bacchette aspetta finché non te le chiedono.
+
        }
 +
        //request left stick if the philosopher doesn't have it
 +
        if (leftStick == without) {
 +
            msg_send(leftPhilo, "request", 8);
 +
        }
 +
         //read messages from everyone
 +
        msg_procname philo = "";
 +
         while (msg_recv(philo, buffer, 8) > 0) {
 +
            if (strcmp(buffer, "ok") == 0) {
 +
                if (strcmp(philo, rightPhilo) == 0) {
 +
                    //a received stick is always clean
 +
                    rightStick = clean;
 +
                } else if (strcmp(philo, leftPhilo) == 0) {
 +
                    //a received stick is always clean
 +
                    leftStick = clean;
 +
                }
 +
            } else if (strcmp(buffer, "request") == 0) {
 +
                //philosophers give up sticks only if they are dirty
 +
                if ((strcmp(philo, rightPhilo) == 0) && (rightStick == dirty)) {
 +
                    rightStick = without;
 +
                    msg_send(rightPhilo, "ok", 3);
 +
                }
 +
                if ((strcmp(philo, leftPhilo) == 0) && (leftStick == dirty)) {
 +
                    leftStick = without;
 +
                    msg_send(leftPhilo, "ok", 3);
 +
                }
 +
            }
 +
        }
 +
         //if a philosopher have 2 clean stick he can start eating
 +
         if (leftStick == clean && rightStick == clean) {
 +
            //eating
 +
            usleep(random() % 2000000);
 +
            //endeat
 +
            //the sticks are dirty after eating.
 +
            leftStick = dirty;
 +
            rightStick = dirty;
 +
        }
 
     }
 
     }
}
 
 
int main(int argc, char *argv[]) {
 
    uintptr_t i;
 
    pthread_t philo_thread[5];
 
    srandom(time(NULL));
 
    for (i = 0; i <  5; i++)
 
        pthread_create(&philo_thread[i], NULL, philo, (void *) i);
 
    for (i = 0; i < 5; i++)
 
        pthread_join(philo_thread[i], NULL);
 
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
--[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 11:31, 9 December 2016 (CET) Ho il problema che dopo aver pensato un filosofo non trova mai richieste per le bacchette, quindi non le concede mai e tutti vanno in deadlock.
+
Potete trovare un archivio con i sorgenti completi e commentati e makefile [http://federico.bertani.web.cs.unibo.it/chattingPhilosophers.tar.gz qui]. <br>
 +
Questa soluzione oltre al deadlock risolve anche il problema della starvation in quanto il meccanismo bacchetta sporca/pulita obbliga un filosofo a cedere la bacchetta dopo aver mangiato e perciò consentire ai vicini di mangiare. <br>
 +
Ho utilizzato il modello completamente asincrono ma senza busy wait in quanto i filosofi tra un tentativo e l'altro di ottenere la bacchetta si mettono a pensare. <br>
 +
Questa soluzione è stata ideata in origine da [https://www.cs.utexas.edu/users/misra/scannedPdf.dir/DrinkingPhil.pdf K. Mani Chandy and J. Misra nel 1984]. <br>
 +
--[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 22:06, 10 December 2016 (CET)

Latest revision as of 09:15, 11 December 2016

Ho messo a vostra disposizione alcuni sorgenti che implementano in C le astrazioni di message passing sincrono, asincrono e completamente asincrono come le abbiamo viste (o vedremo presto) a lezione.

Ho messo il materiale in questo file.

La directory comprende:

  • un Makefile
  • suspend.[ch] è il modulo che implementa la sospensione e riattivazione dei pthread (usando il segnale SIGUSR1).
  • msg.[ch] modulo di implementazione dei paradigmi di message passing.
  • sender.c receiver.c: esempi di processi che si scambiano un messaggio (sender manda a receiver che risponde).

IL file msg.h spiega come usare i servizi forniti da questi sorgenti.

Per iniziare occorre dare il nome al processo durante l'inizializzazione del servizio:

void async_msg_init(char *procname);
void sync_msg_init(char *procname);
void nb_msg_init(char *procname);

IL tipo di servizio (sincrono, asincrono o completamente asincrono) viene deciso al momento della inizializzazione e non puo' piu' essere cambiato.

Per spedire un messaggio basta indicare il nome del destinatario, il buffer contenente il messaggio e la lunghezza in byte del messaggio.

ssize_t msg_send(char *dest, const void *buf, size_t len);

L'interfaccia e' simile alla system call "write" ma al posto del file descriptor c'e' il nome del destinatario. E' possibile inviare messaggi vuoti (di solito servono per indicare un acknowledgement. Al momento della spedizione il processo destinatario deve esistere, altrimenti la funzione ritorna errore. In caso di successo la funzione restituisce ilnumero di byte inviati, -1 un caso di errore.

Per ricevere un messaggio si usa la funzione msg_receive:

ssize_t msg_recv(char *sender, void *buf, size_t len);

Il campo sender può essere:

  • una stringa: in questo caso viene ricevuto il primo messaggio spedito dal mittente indicato
  • NULL: viene consegnato il primo messaggio ricevuto qualsiasi ne sia stato il mittente
  • una variabile di tipo msg_procname con il primo byte zero (una stringa vuota di dimensione adeguata): viene consegnato il primo messaggio ricevuto qualsiasi ne sia stato il mittente e il nome del mittente viene salvato nella variabile sender.

aggiornamento Dec. 6 2016

E' stata aggiunta una funzione per ottenere il proprio nome.

void msg_myname(char *myname);

(il parametro deve essere di tipo msg_procname per essere certi che il nome non sia più lungo del buffer).

La costante MAXLEN indica la massima lunghezza dei messaggi che la libreria gestisce.

Il makefile ha ora un parametro opzionale per la compilazione dei test/tutorial "receiver" e "sender".

make clean; make MPTYPE=async
make clean; make MPTYPE=sync
make clean; make MPTYPE=nb

generano gli eseguibili di esempio per i vari paradigmi di message passing. Lanciando da due shell diverse (e.g. due finestre terminale) prima receiver in una e poi sender nell'altra si potranno osservare le stampe dei due programmi.

aggiornamento Dec. 9 2016

Ho aggiunto questo file che contiene gli esempi di implementazione di message passing asincrono dato quello sincrono (con server) e message passing completamente asincrono dato quello sincrono.

Dining philosophers con message passing

Soluzione senza gestore delle bacchette

In questa soluzione non c'è un gestore delle bacchette a cui richiederne l'uso ma i filosofi si scambiano direttamente messaggi per accordarsi.
Essendo la libreria del message passing non attualmente thread safe ho implementato la soluzione utilizzando un processo per ogni filosofo.
C'è un programma chiamato startPhilos che tramite fork() e exec() esegue i filosofi. StartPhilos assegna anche le bacchette iniziali tramite parametri, io ho seguito la regola che viene assegnata la bacchetta sempre al filosofo con id minore tra i 2 filosofi che potrebbero avere quella bacchetta. In realtà qualsiasi ordine è accettabile, a parte quello in cui si impone una situazione di deadlock.
Ogni stick ha 3 stati: without(il filosofo ha bisogno della bacchetta), clean e dirty. Se a un filosofo manca una bacchetta la richiede al vicino.
Quando a un filosofo viene chiesta una bacchetta la cede solo è sporca, ma la pulisce prima di consegnarla.
Un filosofo mangia solo se ha 2 bacchette pulite, dopo aver mangiato le bacchette sono sporche.
A seguito il ciclo principale della vita di un filosofo:

while (1) {
        //think
        usleep(random() % 2000000);
        //request right stick if the philosopher doesn't have it
        if (rightStick == without) {
            msg_send(rightPhilo, "request", 8);
        }
        //request left stick if the philosopher doesn't have it
        if (leftStick == without) {
            msg_send(leftPhilo, "request", 8);
        }
        //read messages from everyone
        msg_procname philo = "";
        while (msg_recv(philo, buffer, 8) > 0) {
            if (strcmp(buffer, "ok") == 0) {
                if (strcmp(philo, rightPhilo) == 0) {
                    //a received stick is always clean
                    rightStick = clean;
                } else if (strcmp(philo, leftPhilo) == 0) {
                    //a received stick is always clean
                    leftStick = clean;
                }
            } else if (strcmp(buffer, "request") == 0) {
                //philosophers give up sticks only if they are dirty
                if ((strcmp(philo, rightPhilo) == 0) && (rightStick == dirty)) {
                    rightStick = without;
                    msg_send(rightPhilo, "ok", 3);
                }
                if ((strcmp(philo, leftPhilo) == 0) && (leftStick == dirty)) {
                    leftStick = without;
                    msg_send(leftPhilo, "ok", 3);
                }
            }
        }
        //if a philosopher have 2 clean stick he can start eating
        if (leftStick == clean && rightStick == clean) {
            //eating
            usleep(random() % 2000000);
            //endeat
            //the sticks are dirty after eating.
            leftStick = dirty;
            rightStick = dirty;
        }
    }

Potete trovare un archivio con i sorgenti completi e commentati e makefile qui.
Questa soluzione oltre al deadlock risolve anche il problema della starvation in quanto il meccanismo bacchetta sporca/pulita obbliga un filosofo a cedere la bacchetta dopo aver mangiato e perciò consentire ai vicini di mangiare.
Ho utilizzato il modello completamente asincrono ma senza busy wait in quanto i filosofi tra un tentativo e l'altro di ottenere la bacchetta si mettono a pensare.
Questa soluzione è stata ideata in origine da K. Mani Chandy and J. Misra nel 1984.
--FedericoB (talk) 22:06, 10 December 2016 (CET)