AngoLinux

Un'altra esperienza in Quarta

- A cura del Prof. Stefano Salvi -


Sommario

  1. Introduzione
  2. L'esercizio proposto
  3. Le funzioni per la gestione delle mailbox
  4. Le strutture, le Costanti e le Variabili Globali
  5. La Funzione main ()
  6. La Funzione guardia () che Gestisce la Guardia
  7. La Funzione ladro () che Gestisce il Ladro
  8. La Funzione campo () che Gestisce il Campo
  9. Il sorgente Completo

Introduzione

Nel precedente esercizio abbiamo visto come creare più processi e come farli interagire, utilizzando le pipe.

Un altro metodo di comunicazione tra processi è quello delle caselle postali o mailbox. Con questo metodo un processo può mettere un messaggio in una coda che un altro processo preleverà.

In Linux questo metodo è tra quelli previsti dalla System V IPC vale a dire i metodi di Inter Process Communication previsti dallo standard UNIX System V (la quinta versione dello standard di UNIX).

L'esercizio Proposto

L'esercizio è lo stesso dell'esempio precedente, nel quale si devono creare tre processi, uno che impersoni un ladro, uno che faccia da guardia ed uno che gestisca il campo di gioco.

Questa volta però utilizzeremo, al posto delle pipe, delle mailbox.

Le funzioni per la gestione delle mailbox

In questo esercizio sostituiremo le quattro pipe con quattro mailbox. Potremmo anche usarne una sola, utilizzando tipi di messaggio diverso a seconda del destinatario che si vuole selezionare.

Per poter utilizzare una mailbox occorrerà per prima cosa crearla. Per farlo dovremo utilizzare la seguente funzione:

int msgget ( key_t key, int msgflg )
Crea una mailbox il cui nome è key e con le opzioni msgflg. Il tipo key_t è un riferimento (un handle) ad un nome, che si può utilizzare per creare delle mailbox utilizzate da programmi diversi. Nel nostro caso, visto che le pipe saranno usate da diversi processi generati dalla stesso programma, potremo usare una mailbox senza nome, creata con l'handle standard IPC_PRIVATE . Le opzioni per la creazione, indicate in msgflg, sono a grandi linee quelle che si usano per i file. Nel nostro caso, visto che dovremo utilizzare le mailbox per scrivere e leggere, utilizzeremo le costanti S_IREAD ed S_IWRITE. La funzione ritorna un riferimento da usare per utilizzare la mailbox, oppure EOF in caso di errore.

Una volta create le nostre mailbox, potremo introdurre messaggi o prelevare messaggi con le seguenti funzioni:
int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz, int msgflg )
Con questa funzione si può inviare un messaggio, ponendolo nella mailbox. Se la mailbox è piena, il processo si ferma ed attende. Il parametro msqid conterrà l'handle della mailbox, ottenuto con la msgget. Il parametro msgp sarà un puntatore ad una struct che inizierà con un long, che corrisponde al tipo di messaggio, seguito dai dati utili del messaggio. La definizione del tipo è la seguente:

     struct msgbuf {
        long mtype;      
        char mtext[1];
     }
In realtà il campo mtext sarà sostituito dalla nostra struttura dati. Il parametro msgsz conterrà la dimensione dei dati ultili. Attenzione che msgsz non deve contenere la dimensione dell'intera struttura passata per parametro, ma dei soli dati utili, escluso il tipo di messaggio. Per finire msgflg conterrà i flags per la trasmissione, che in questo caso non ci servono. Questa funzione ritorna il numero di byte utili scritti, o -1 in caso di errore.
int msgrcv ( int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg )
Con questa funzione si può ricevere un messaggio, prelevandolo dalla mailbox. Se la mailbox è vuota, il processo si ferma ed attende. Il parametro msqid conterrà l'handle della mailbox, ottenuto con la msgget. Il parametro msgp sarà un puntatore ad una struct che inizierà con un long, che corrisponde al tipo di messaggio, seguito dallo spazio per i dati utili del messaggio, come nella funzione precedente. Il parametro msgsz conterrà lo spazio disponibile per i dati ultili. Attenzione che msgsz non deve contenere la dimensione dell'intera struttura passata per parametro, ma dei soli dati utili, escluso il tipo di messaggio. Il parametro msgtyp serve per selezionare i messaggi da prelevare dalla mailbox. Se ha un valore positivo, verranno prelevati dalla mailbox i soli messaggi che hanno quel numero come tipo. Se vale zero, preleveremo tutti i messaggio, mentre se sarà minore di zero preleveremo i messaggi il cui tipo va da 1 al valore assoluto del parametro (0 non è un valore di tipo valido). Per finire msgflg conterrà i flags per la trasmissione, che in questo caso non ci servono. Questa funzione ritorna il numero di byte utili letti, o -1 in caso di errore.

Una volta finito l'uso della mailbox noi dovremo liberare le risorse utilizzate. Per questo useremo la seguente funzione:


int msgctl (int msqid, int cmd, struct msqid_ds *buf);
Eseguirà un comando cmd sulla mailbox msqid, eventualmente utilizzando i dati in buf. I possibili comandi sono: IPC_STAT, IPC_SET, IPC_RMID. Il comando che a noi serve è IPC_RMID che serve per eliminare la mailbox. Questo comando non prevede dei dati in buf, quindi potremo passare il valore NULL in questo parametro. Il valore di ritorno sarà zero se tutto andrà bene, EOF in caso di errore.

Per poter usare queste funzioni occorre includere i file <sys/types.h> <sys/ipc.h> <sys/msg.h>. Per usare le costanti per definire l'accesso alla mailbox (S_IREAD ed S_IWRITE) si dovrà includere il file <sys/stat.h>.

Le strutture, le Costanti e le Variabili Globali

Per prima cosa indicheremo i file header da utilizzare per le varie funzioni e costanti utilizzate nel programma:

/* game.c
* Giochino scemo scritto usando 'vgalib'
* e i costrutti del multitasking
* - fork per 'figliare' i processi
* - 'pipe' o 'messaggi' per farli comunicare
*/

// #define	TEXTONLY

// #define	USESPIPES
#define	USEMESSAGES

#include <stdlib.h>
#include <stdio.h>
#include <vga.h>
#include <time.h>
#include <errno.h>
#include <signal.h>

#ifdef	USEMESSAGES
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define	MYMSG	((long) 1)
#endif


#define BORDER  40      /* Bordo da rispettare in pixel */
#define ERR     7       /* Codice di errore */
#define GCOLOR  3       /* Colore della guardia */
#define LCOLOR  4       /* Colore del ladro */
#define PASSO   8       /* Ogni quanto il ladro cambia direzione */

int XMax;
int YMax;
int CMax;
int VGAMODE;

void arrow (int x,int y, int dir, int color);
void guardia (int outfile,int infile);
void ladro (int outfile,int infile);
int campo (int ginfile,int linfile, int goutfile, int loutfile);

/* Indici delle direzioni */
#define         O       0
#define         SO      1
#define         S       2
#define         SE      3
#define         E       4
#define         NE      5
#define         N       6
#define         NO      7

int arrows [8][4][2] = {
 { { 0 , 10 } , { 20 , 10 } , { 10 , 0 } , { 10 , 20 } },     /* Sinistra */
 { { 0 , 20 } , { 20 , 0 } , { 0 , 0 } , { 20 , 20 } },       /* Sinistra,giu */
 { { 10 , 20 } , { 10 , 0 } , { 0 , 10 } , { 20 , 10 } },     /* Giu */
 { { 20 , 20 } , { 0 , 0 } , { 0 , 20 } , { 20 , 0 } },       /* Destra,giu */
 { { 20 , 10 } , { 0 , 10 } , { 10 , 0 } , { 10 , 20 } },     /* Destra */
 { { 20 , 0 } , { 0 , 20 } , { 0 , 0 } , { 20 , 20 } },       /* Destra,su */
 { { 10 , 0 } , { 10 , 20 } , { 0 , 10 } , { 20 , 10 } },     /* Su */
 { { 0 , 0 } , { 20 , 20 } , { 20 , 0 } , { 0 , 20 } },       /* Sinistra,su */
};

#define DELTA   1       /* Spostamneto per 'frame' */

#define LDELTA  5       /* Spostamento ladro per frame */
#define GDELTA  3       /* Spostamento guardia per frame */

int move [8][2] = {
 { -DELTA, 0 },       /* Sinistra */
 { -DELTA, DELTA },   /* Sinistra,giu */
 { 0, DELTA },        /* Giu */
 { DELTA, DELTA },    /* Destra,giu */
 { DELTA, 0 },        /* Destra */
 { DELTA, -DELTA },   /* Destra,su */
 { 0, -DELTA },       /* Su */
 { -DELTA, -DELTA },  /* Sinistra,su */
};

typedef struct {
#ifdef	USEMESSAGES
  long type;	// Tipo di messaggio - sara' MYMSG
#endif
 int x,y,dir;
} point;

La Funzione main ()

La funzione main è la funzione principale di ogni programma C.

Il programma non necessita di parametri e non ha bisogno di ritornare un codice di ritorno, quindi scegliamo il prototipo void main (void) senza ne parametri ne valore di ritorno.

Di seguito alla graffa di apertura della funzione troviamo le variabili. I commenti descrivono il loro uso.

void main(void)
{
#ifdef	USESPIPES
int g_c [2];    /* pipe guardia-campo */
int l_c [2];    /* pipe ladro-campo */
int c_g [2];    /* pipe campo-guardia */
int c_l [2];    /* pipe campo-ladro */
#endif
#ifdef	USEMESSAGES
int g_c;    	/* coda guardia-campo */
int l_c;    	/* coda ladro-campo */
int c_g;    	/* coda campo-guardia */
int c_l;    	/* coda campo-ladro */
#endif

int gpid,lpid;  /* PID della guardia e del ladro */
int gret,lret;  /* valore di ritorno della guardia e del ladro */
int r;

#ifdef	USESPIPES
  if (pipe (g_c))
  {
      printf ("Non riesco a creare il canale verso la guardia\n");
      return;
  }

  if (pipe (l_c))
  {
      printf ("Non riesco a creare il canale verso il ladro\n");
      return;
  }

  if (pipe (c_g))
  {
      printf ("Non riesco a creare il canale dalla guardia\n");
      return;
  }

  if (pipe (c_l))
  {
      printf ("Non riesco a creare il canale dal ladro\n");
      return;
  }
#endif
#ifdef	USEMESSAGES
  g_c = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (g_c == EOF)
  {
      perror ("Non riesco a creare il canale verso la guardia\n");
      return;
  }

  l_c = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (l_c == EOF)
  {
      msgctl (g_c, IPC_RMID, NULL);
      perror ("Non riesco a creare il canale verso il ladro\n");
      return;
  }

  c_g = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (c_g == EOF)
  {
      msgctl (g_c, IPC_RMID, NULL);
      msgctl (l_c, IPC_RMID, NULL);
      perror ("Non riesco a creare il canale dalla guardia\n");
      return;
  }

  c_l = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (c_l == EOF)
  {
      msgctl (g_c, IPC_RMID, NULL);
      msgctl (l_c, IPC_RMID, NULL);
      msgctl (c_g, IPC_RMID, NULL);
      perror ("Non riesco a creare il canale dal ladro\n");
      return;
  }
#endif

#ifdef	TEXTONLY
  XMax = 640;
  YMax = 480;
  CMax = 16;
#else
  vga_init();
  VGAMODE = vga_getdefaultmode();
  if (VGAMODE == -1)
      VGAMODE = G640x480x16;  /* Default mode. */

  if (!vga_hasmode(VGAMODE))
  {
      printf("Modalita' grafica non disponibile.\n");
      exit(-1);
  }

  vga_setmode(VGAMODE);

  XMax = vga_getxdim ();
  YMax = vga_getydim ();
  CMax = vga_getcolors ();
#endif

  srand (time ((time_t *) 0));

  gpid = fork ();     /* crea il processo 'guardia' */
  if (!gpid)
  {   /* se e' il processo figlio, esegue la 'guardia' */
#ifdef	USESPIPES
      guardia ( g_c [1] , c_g [0] );
#endif
#ifdef	USEMESSAGES
      guardia ( g_c , c_g );
#endif
      return;
  }
  else if (gpid < 0)
  {   /* Errore !!! */
      printf ("Non riesco a generare la guardia\n");
      return;
  }

  lpid = fork ();     /* crea il processo 'ladro' */
  if (!lpid)
  {   /* se e' il processo figlio, esegue il ladro */
#ifdef	USESPIPES
      ladro (l_c [1],c_l [0]);
#endif
#ifdef	USEMESSAGES
      ladro (l_c ,c_l );
#endif
      return;
  }
  else if (lpid < 0)
  {   /* Errore !!! */
      printf ("Non riesco a generare il ladro\n");
      return;
  }

#ifdef	USESPIPES
  r = campo (g_c [0], l_c [0], c_g [1],c_l [1]); /* Il padre fa il 'campo' */
#endif
#ifdef	USEMESSAGES
  r = campo (g_c , l_c , c_g ,c_l ); /* Il padre fa il 'campo' */
#endif


  getchar();

  vga_setmode(TEXT);

  if (r)
  {
      perror ("Errore in 'campo'");
  }

  waitpid (gpid,&gret,0);
  waitpid (lpid,&lret,0);

#ifdef	USEMESSAGES
  msgctl (g_c, IPC_RMID, NULL);
  msgctl (l_c, IPC_RMID, NULL);
  msgctl (c_g, IPC_RMID, NULL);
  msgctl (c_l, IPC_RMID, NULL);
#endif

  exit(0);
}

La Funzione guardia () che Gestisce la Guardia

La funzione guardia è la funzione che svolge tutto il lavoro del processo figlio della guardia.

Questa funzione viene chiamata immediatamente dopo la creazione del processo. Il processo termina immediatamente dopo il termine di questa funzione.

I parametri passati a questa funzione sono gli handle delle due pipe che serviranno per comunicare con il campo.

void guardia (int outfile,int infile)
{
point l,g;      /* Le strutture per il ladro e la guardia */
int dx,dy;      /* Lo spostamento da fare */
double d;       /* La tangente della direzione */
int e;

 g.x = XMax - BORDER;
 g.y = YMax - BORDER;
 g.dir = O;

 do {
#ifdef	USESPIPES
    read (infile,&l,sizeof (l));
#endif
#ifdef	USEMESSAGES
    e = msgrcv (infile, (struct msgbuf *) &l , sizeof (l) - sizeof (long) , 0,  0 );
#ifdef TEXTONLY
    if (e < 0)
    {
      perror ("Guardia riceve");
    }
#endif
#endif


    g.x += move [g.dir][0] * GDELTA;
    g.y += move [g.dir][1] * GDELTA;


    if (g.x < BORDER || g.x > XMax - BORDER ||
       g.y < BORDER || g.y > YMax - BORDER)
          g.dir = (g.dir + 4) % 8;
    else
    {
        dx = g.x - l.x;   // < 0 => O; > 0 => E
        dy = l.y - g.y;   // < 0 => N; > 0 => S
                          // l'asse y ha direzione opposta (+ = basso)

        if (!dx)
        {  // Verticale
          if (dy > 0)
             g.dir = N;
          else g.dir = S;
        }

        d = (double) dy / (double) dx;

        if (d > 2 || d < -2)
        {
          if (dy > 0)
             g.dir = S;
          else g.dir = N;
        }
        else if (d > -0.5 && d < 0.5)
        {
          if (dx > 0)
             g.dir = O;
          else g.dir = E;
        }
        else if (d > 0)
        {  /* tra .5 e 2 */
          if (dx > 0)
             g.dir = SO;
          else g.dir = NE;
        }
        else
        {  /* tra -.5 e -2 */
          if (dx > 0)
             g.dir = NO;
          else g.dir = SE;
        }
    }

#ifdef	USESPIPES
    write (outfile,&g,sizeof (g));
#endif
#ifdef	USEMESSAGES
    g.type = MYMSG;
    msgsnd (outfile, (struct msgbuf *) &g , sizeof (g) - sizeof (long) , 0 );
#endif
 } while (l.dir >= 0);
}

La Funzione ladro () che Gestisce il Ladro

Passiamo a descrivere la funzione che gestisce il ladro.

Il ladro deve scappare in maniera casuale. Ogni PASSO spostamenti esso cambierà direzione. Per evitare che il movimento sia troppo caotico, la direzione potrà variare al massimo di più o meno quarantacinque gradi.

La funzione, come quella della guardia, riceverà come parametri gli handle del lato opportuno delle due pipe che collegano il ladro al campo.

La pipe verso il campo servirà, come per la guardia, per comunicare la posizione del ladro al campo.

La pipe nella direzione opposta invece servirà solamente per trasportare un flag (una variabile booleana) che indicherà la fine del gioco.

Vediamo quindi la definizione:

void ladro (int outfile,int infile)
{
point p;
int i;
#ifdef	USESPIPES
int j;
#endif
#ifdef	USEMESSAGES
struct {
   long type;
   int r;
} j;
#endif

 p.x = BORDER;
 p.y = BORDER;
 p.dir = E;

 for (i = 0;i < 1000;i ++)
 {
#ifdef	USESPIPES
    if (write (outfile,&p,sizeof (p)) != sizeof (p))
       return;
#endif
#ifdef	USEMESSAGES
    p.type = MYMSG;
    if (msgsnd (outfile, (struct msgbuf *) &p , sizeof (p) - sizeof (long) , 0 ) < 0)
    {
      return;
    }
#endif

    p.x += move [p.dir][0] * LDELTA;
    p.y += move [p.dir][1] * LDELTA;
    if (i % PASSO == PASSO - 1)
    {
        if (p.x < BORDER)
        {  /* 0 -> sinistra (O) */
          if (p.dir < E)
             p.dir ++;
          else
             p.dir --;
        }
        else if (p.x > XMax - BORDER)
        {  /* 4 -> Destra (E) */
          if (p.dir < E)
             p.dir --;
          else
             p.dir ++;
        }
        else if (p.y < BORDER)
        { /* 6 -> Su (N) */
          if (p.dir > S && p.dir <= N)
             p.dir --;
          else
             p.dir ++;
        }
        else if (p.y > YMax - BORDER)
        {  /* 2 -> Giu (S) */
          if (p.dir >= S && p.dir < N)
             p.dir ++;
          else
             p.dir --;
        }
        else p.dir += (random () % 3) - 1;
        p.dir &= 7;   /* se uso il 'modulo', richio un indice negativo */
    }

#ifdef	USESPIPES
    if (read (infile,&j,sizeof (j)) < sizeof (j))
      return;
#endif
#ifdef	USEMESSAGES
    if (msgrcv (infile, (struct msgbuf *) &j , sizeof (j) - sizeof (long) , 0, 0 ) == EOF)
    {
#ifdef TEXTONLY
      perror ("Ladro riceve");
#endif
       return;
    }
#endif

    if (j.r)
       break;
 }
#ifdef TEXTONLY
    printf ("Ladro Fine Ciclo -> %d\n", i );
#endif
 p.dir = -i;
#ifdef	USESPIPES
 write (outfile,&p,sizeof (p));
#endif
#ifdef	USEMESSAGES
 p.type = MYMSG;
 msgsnd (outfile, (struct msgbuf *) &p , sizeof (p) - sizeof (long) , 0 );
#endif
}

La Funzione campo () che Gestisce il Campo

La funzione campo ha il compito di mettere in comunicazione guardia e ladro, di disegnare le frecce e di verificare la condizione di "ladro catturato".

Dovendo gestire le comunicazioni, il campo riceve come parametro i lati opportuni di tutte le pipe.

Cominciamo a vedere la funzione:

int campo (int ginfile,int linfile, int goutfile, int loutfile)
{
int j;
#ifdef	USESPIPES
int r;
#endif
#ifdef	USEMESSAGES
struct {
   long type;
   int r;
} r;
#endif
point l,g;

  l.dir = -1; // inizia senza freccia
  g.dir = -1;

  do {
    for (j = 0;j < 600000; j ++);     /* Ritardo */

#ifndef	TEXTONLY
    if (l.dir >= 0) arrow (l.x,l.y,l.dir,0);  /* Cancella freccia */
#endif
#ifdef	USESPIPES
    if (read (linfile,&l,sizeof (l)) != sizeof (l))
    {
       return -1;
    }
#endif
#ifdef	USEMESSAGES
    if (msgrcv (linfile, (struct msgbuf *) &l , sizeof (l) - sizeof (long) , 0 , 0 ) < 0)
    {
       return -1;
    }
#endif
#ifdef	TEXTONLY
    printf ("Ladro in %d %d - Dir %d ---- ",l.x,l.y,l.dir);
#else
    if (l.dir >= 0) arrow (l.x,l.y,l.dir,LCOLOR);     /* Disegna freccia */
#endif

#ifdef	USESPIPES
    if (write (goutfile,&l,sizeof (l)) != sizeof (l))
    {
      return -1;
    }
#endif
#ifdef	USEMESSAGES
    l.type = MYMSG;
    msgsnd (goutfile, (struct msgbuf *) &l , sizeof (l) - sizeof (long) , 0 );
#endif

#ifndef	TEXTONLY
    if (g.dir >= 0) arrow (g.x,g.y,g.dir,0);  /* Cancella freccia */
#endif
#ifdef	USESPIPES
    if (read (ginfile,&g,sizeof (g)) != sizeof (g))
    {
       return -1;
    }
#endif
#ifdef	USEMESSAGES
    if (msgrcv (ginfile, (struct msgbuf *) &g , sizeof (g) - sizeof (long) , 0, 0 )<0)
    {
#ifdef	TEXTONLY
      perror ("Campo legge da guardia");
#endif    
      return -1;
    }
#endif
#ifdef	TEXTONLY
    printf ("Guardia in %d %d - Dir %d\n",g.x,g.y, g.dir);
#else
    if (g.dir >= 0) arrow (g.x,g.y,g.dir,GCOLOR);     /* Disegna freccia */
#endif
#ifdef	USESPIPES
    r = (g.x > l.x - ERR && g.x < l.x + ERR &&
       g.y > l.y - ERR && g.y < l.y + ERR);
    write (loutfile,&r,sizeof (r));
#endif
#ifdef	USEMESSAGES
    r.r = (g.x > l.x - ERR && g.x < l.x + ERR &&
       g.y > l.y - ERR && g.y < l.y + ERR);
    r.type = MYMSG;
    msgsnd (loutfile, (struct msgbuf *) &r , sizeof (r) - sizeof (long) , 0 );
#endif
  } while (l.dir >= 0);
  printf ("Gara conclusa in %d mosse\n", - l.dir);
  return 0;
}

Il Sorgente Completo

Abbiamo visto, una per una, le righe del nostro programma nel precedente esercizio e le differenze in questo. Riporto qui il sorgente completo, tutto di seguito.

In questo sorgente sono presenti sia le righe relative alle pipe, sia quelle relative alle mailbox, abilitate tramite compilazione condizionale.

Sono inoltre presenti delle rghe con delle stempe di prova, abolitate tramite la #define TEXTONLY, che disablilita anche l'interfaccia grafica, semplificando il debug.

Se volete provare il programma, potete selezionare il sorgente qui di seguito, "copiarlo" usando il menù edit o modifica del browser, quindi incollarlo in un qualunque editor e salvarlo con il nome game.c in una sottodirectory della vostra home directory.

In alternativa potete salvare l'intero documento, selezionando file di testo come formato del documento, poi cancellare dal file la relazione e tenere solo questo sorgente.
/* game.c
* Giochino scemo scritto usando 'vgalib'
* e i costrutti del multitasking
* - fork per 'figliare' i processi
* - 'pipe' o 'messaggi' per farli comunicare
*/

// #define	TEXTONLY

// #define	USESPIPES
#define	USEMESSAGES

#include <stdlib.h>
#include <stdio.h>
#include <vga.h>
#include <time.h>
#include <errno.h>
#include <signal.h>

#ifdef	USEMESSAGES
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define	MYMSG	((long) 1)
#endif


#define BORDER  40      /* Bordo da rispettare in pixel */
#define ERR     7       /* Codice di errore */
#define GCOLOR  3       /* Colore della guardia */
#define LCOLOR  4       /* Colore del ladro */
#define PASSO   8       /* Ogni quanto il ladro cambia direzione */

int XMax;
int YMax;
int CMax;
int VGAMODE;

void arrow (int x,int y, int dir, int color);
void guardia (int outfile,int infile);
void ladro (int outfile,int infile);
int campo (int ginfile,int linfile, int goutfile, int loutfile);

/* Indici delle direzioni */
#define         O       0
#define         SO      1
#define         S       2
#define         SE      3
#define         E       4
#define         NE      5
#define         N       6
#define         NO      7

int arrows [8][4][2] = {
 { { 0 , 10 } , { 20 , 10 } , { 10 , 0 } , { 10 , 20 } },     /* Sinistra */
 { { 0 , 20 } , { 20 , 0 } , { 0 , 0 } , { 20 , 20 } },       /* Sinistra,giu */
 { { 10 , 20 } , { 10 , 0 } , { 0 , 10 } , { 20 , 10 } },     /* Giu */
 { { 20 , 20 } , { 0 , 0 } , { 0 , 20 } , { 20 , 0 } },       /* Destra,giu */
 { { 20 , 10 } , { 0 , 10 } , { 10 , 0 } , { 10 , 20 } },     /* Destra */
 { { 20 , 0 } , { 0 , 20 } , { 0 , 0 } , { 20 , 20 } },       /* Destra,su */
 { { 10 , 0 } , { 10 , 20 } , { 0 , 10 } , { 20 , 10 } },     /* Su */
 { { 0 , 0 } , { 20 , 20 } , { 20 , 0 } , { 0 , 20 } },       /* Sinistra,su */
};

#define DELTA   1       /* Spostamneto per 'frame' */

#define LDELTA  5       /* Spostamento ladro per frame */
#define GDELTA  3       /* Spostamento guardia per frame */

int move [8][2] = {
 { -DELTA, 0 },       /* Sinistra */
 { -DELTA, DELTA },   /* Sinistra,giu */
 { 0, DELTA },        /* Giu */
 { DELTA, DELTA },    /* Destra,giu */
 { DELTA, 0 },        /* Destra */
 { DELTA, -DELTA },   /* Destra,su */
 { 0, -DELTA },       /* Su */
 { -DELTA, -DELTA },  /* Sinistra,su */
};

typedef struct {
#ifdef	USEMESSAGES
  long type;	// Tipo di messaggio - sara' MYMSG
#endif
 int x,y,dir;
} point;

void main(void)
{
#ifdef	USESPIPES
int g_c [2];    /* pipe guardia-campo */
int l_c [2];    /* pipe ladro-campo */
int c_g [2];    /* pipe campo-guardia */
int c_l [2];    /* pipe campo-ladro */
#endif
#ifdef	USEMESSAGES
int g_c;    	/* coda guardia-campo */
int l_c;    	/* coda ladro-campo */
int c_g;    	/* coda campo-guardia */
int c_l;    	/* coda campo-ladro */
#endif

int gpid,lpid;  /* PID della guardia e del ladro */
int gret,lret;  /* valore di ritorno della guardia e del ladro */
int r;

#ifdef	USESPIPES
  if (pipe (g_c))
  {
      printf ("Non riesco a creare il canale verso la guardia\n");
      return;
  }

  if (pipe (l_c))
  {
      printf ("Non riesco a creare il canale verso il ladro\n");
      return;
  }

  if (pipe (c_g))
  {
      printf ("Non riesco a creare il canale dalla guardia\n");
      return;
  }

  if (pipe (c_l))
  {
      printf ("Non riesco a creare il canale dal ladro\n");
      return;
  }
#endif
#ifdef	USEMESSAGES
  g_c = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (g_c == EOF)
  {
      perror ("Non riesco a creare il canale verso la guardia\n");
      return;
  }

  l_c = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (l_c == EOF)
  {
      msgctl (g_c, IPC_RMID, NULL);
      perror ("Non riesco a creare il canale verso il ladro\n");
      return;
  }

  c_g = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (c_g == EOF)
  {
      msgctl (g_c, IPC_RMID, NULL);
      msgctl (l_c, IPC_RMID, NULL);
      perror ("Non riesco a creare il canale dalla guardia\n");
      return;
  }

  c_l = msgget (IPC_PRIVATE,S_IREAD | S_IWRITE);
  if (c_l == EOF)
  {
      msgctl (g_c, IPC_RMID, NULL);
      msgctl (l_c, IPC_RMID, NULL);
      msgctl (c_g, IPC_RMID, NULL);
      perror ("Non riesco a creare il canale dal ladro\n");
      return;
  }
#endif

#ifdef	TEXTONLY
  XMax = 640;
  YMax = 480;
  CMax = 16;
#else
  vga_init();
  VGAMODE = vga_getdefaultmode();
  if (VGAMODE == -1)
      VGAMODE = G640x480x16;  /* Default mode. */

  if (!vga_hasmode(VGAMODE))
  {
      printf("Modalita' grafica non disponibile.\n");
      exit(-1);
  }

  vga_setmode(VGAMODE);

  XMax = vga_getxdim ();
  YMax = vga_getydim ();
  CMax = vga_getcolors ();
#endif

  srand (time ((time_t *) 0));

  gpid = fork ();     /* crea il processo 'guardia' */
  if (!gpid)
  {   /* se e' il processo figlio, esegue la 'guardia' */
#ifdef	USESPIPES
      guardia ( g_c [1] , c_g [0] );
#endif
#ifdef	USEMESSAGES
      guardia ( g_c , c_g );
#endif
      return;
  }
  else if (gpid < 0)
  {   /* Errore !!! */
      printf ("Non riesco a generare la guardia\n");
      return;
  }

  lpid = fork ();     /* crea il processo 'ladro' */
  if (!lpid)
  {   /* se e' il processo figlio, esegue il ladro */
#ifdef	USESPIPES
      ladro (l_c [1],c_l [0]);
#endif
#ifdef	USEMESSAGES
      ladro (l_c ,c_l );
#endif
      return;
  }
  else if (lpid < 0)
  {   /* Errore !!! */
      printf ("Non riesco a generare il ladro\n");
      return;
  }

#ifdef	USESPIPES
  r = campo (g_c [0], l_c [0], c_g [1],c_l [1]); /* Il padre fa il 'campo' */
#endif
#ifdef	USEMESSAGES
  r = campo (g_c , l_c , c_g ,c_l ); /* Il padre fa il 'campo' */
#endif


  getchar();

  vga_setmode(TEXT);

  if (r)
  {
      perror ("Errore in 'campo'");
  }

  waitpid (gpid,&gret,0);
  waitpid (lpid,&lret,0);

#ifdef	USEMESSAGES
  msgctl (g_c, IPC_RMID, NULL);
  msgctl (l_c, IPC_RMID, NULL);
  msgctl (c_g, IPC_RMID, NULL);
  msgctl (c_l, IPC_RMID, NULL);
#endif

  exit(0);
}

void arrow (int x,int y, int dir, int color)
{
 vga_setcolor (color);
 vga_drawline (arrows [dir][0][0] + x, arrows [dir][0][1] + y,
    arrows [dir][1][0] + x, arrows [dir][1][1] + y);
 vga_drawline (arrows [dir][0][0] + x, arrows [dir][0][1] + y,
    arrows [dir][2][0] + x, arrows [dir][2][1] + y);
 vga_drawline (arrows [dir][0][0] + x, arrows [dir][0][1] + y,
    arrows [dir][3][0] + x, arrows [dir][3][1] + y);
}

void guardia (int outfile,int infile)
{
point l,g;      /* Le strutture per il ladro e la guardia */
int dx,dy;      /* Lo spostamento da fare */
double d;       /* La tangente della direzione */
int e;

 g.x = XMax - BORDER;
 g.y = YMax - BORDER;
 g.dir = O;

 do {
#ifdef	USESPIPES
    read (infile,&l,sizeof (l));
#endif
#ifdef	USEMESSAGES
    e = msgrcv (infile, (struct msgbuf *) &l , sizeof (l) - sizeof (long) , 0,  0 );
#ifdef TEXTONLY
    if (e < 0)
    {
      perror ("Guardia riceve");
    }
#endif
#endif


    g.x += move [g.dir][0] * GDELTA;
    g.y += move [g.dir][1] * GDELTA;


    if (g.x < BORDER || g.x > XMax - BORDER ||
       g.y < BORDER || g.y > YMax - BORDER)
          g.dir = (g.dir + 4) % 8;
    else
    {
        dx = g.x - l.x;   // < 0 => O; > 0 => E
        dy = l.y - g.y;   // < 0 => N; > 0 => S
                          // l'asse y ha direzione opposta (+ = basso)

        if (!dx)
        {  // Verticale
          if (dy > 0)
             g.dir = N;
          else g.dir = S;
        }

        d = (double) dy / (double) dx;

        if (d > 2 || d < -2)
        {
          if (dy > 0)
             g.dir = S;
          else g.dir = N;
        }
        else if (d > -0.5 && d < 0.5)
        {
          if (dx > 0)
             g.dir = O;
          else g.dir = E;
        }
        else if (d > 0)
        {  /* tra .5 e 2 */
          if (dx > 0)
             g.dir = SO;
          else g.dir = NE;
        }
        else
        {  /* tra -.5 e -2 */
          if (dx > 0)
             g.dir = NO;
          else g.dir = SE;
        }
    }

#ifdef	USESPIPES
    write (outfile,&g,sizeof (g));
#endif
#ifdef	USEMESSAGES
    g.type = MYMSG;
    msgsnd (outfile, (struct msgbuf *) &g , sizeof (g) - sizeof (long) , 0 );
#endif
 } while (l.dir >= 0);
}

void ladro (int outfile,int infile)
{
point p;
int i;
#ifdef	USESPIPES
int j;
#endif
#ifdef	USEMESSAGES
struct {
   long type;
   int r;
} j;
#endif

 p.x = BORDER;
 p.y = BORDER;
 p.dir = E;

 for (i = 0;i < 1000;i ++)
 {
#ifdef	USESPIPES
    if (write (outfile,&p,sizeof (p)) != sizeof (p))
       return;
#endif
#ifdef	USEMESSAGES
    p.type = MYMSG;
    if (msgsnd (outfile, (struct msgbuf *) &p , sizeof (p) - sizeof (long) , 0 ) < 0)
    {
      return;
    }
#endif

    p.x += move [p.dir][0] * LDELTA;
    p.y += move [p.dir][1] * LDELTA;
    if (i % PASSO == PASSO - 1)
    {
        if (p.x < BORDER)
        {  /* 0 -> sinistra (O) */
          if (p.dir < E)
             p.dir ++;
          else
             p.dir --;
        }
        else if (p.x > XMax - BORDER)
        {  /* 4 -> Destra (E) */
          if (p.dir < E)
             p.dir --;
          else
             p.dir ++;
        }
        else if (p.y < BORDER)
        { /* 6 -> Su (N) */
          if (p.dir > S && p.dir <= N)
             p.dir --;
          else
             p.dir ++;
        }
        else if (p.y > YMax - BORDER)
        {  /* 2 -> Giu (S) */
          if (p.dir >= S && p.dir < N)
             p.dir ++;
          else
             p.dir --;
        }
        else p.dir += (random () % 3) - 1;
        p.dir &= 7;   /* se uso il 'modulo', richio un indice negativo */
    }

#ifdef	USESPIPES
    if (read (infile,&j,sizeof (j)) < sizeof (j))
      return;
#endif
#ifdef	USEMESSAGES
    if (msgrcv (infile, (struct msgbuf *) &j , sizeof (j) - sizeof (long) , 0, 0 ) == EOF)
    {
#ifdef TEXTONLY
      perror ("Ladro riceve");
#endif
       return;
    }
#endif

    if (j.r)
       break;
 }
#ifdef TEXTONLY
    printf ("Ladro Fine Ciclo -> %d\n", i );
#endif
 p.dir = -i;
#ifdef	USESPIPES
 write (outfile,&p,sizeof (p));
#endif
#ifdef	USEMESSAGES
 p.type = MYMSG;
 msgsnd (outfile, (struct msgbuf *) &p , sizeof (p) - sizeof (long) , 0 );
#endif
}

int campo (int ginfile,int linfile, int goutfile, int loutfile)
{
int j;
#ifdef	USESPIPES
int r;
#endif
#ifdef	USEMESSAGES
struct {
   long type;
   int r;
} r;
#endif
point l,g;

  l.dir = -1; // inizia senza freccia
  g.dir = -1;

  do {
    for (j = 0;j < 600000; j ++);     /* Ritardo */

#ifndef	TEXTONLY
    if (l.dir >= 0) arrow (l.x,l.y,l.dir,0);  /* Cancella freccia */
#endif
#ifdef	USESPIPES
    if (read (linfile,&l,sizeof (l)) != sizeof (l))
    {
       return -1;
    }
#endif
#ifdef	USEMESSAGES
    if (msgrcv (linfile, (struct msgbuf *) &l , sizeof (l) - sizeof (long) , 0 , 0 ) < 0)
    {
       return -1;
    }
#endif
#ifdef	TEXTONLY
    printf ("Ladro in %d %d - Dir %d ---- ",l.x,l.y,l.dir);
#else
    if (l.dir >= 0) arrow (l.x,l.y,l.dir,LCOLOR);     /* Disegna freccia */
#endif

#ifdef	USESPIPES
    if (write (goutfile,&l,sizeof (l)) != sizeof (l))
    {
      return -1;
    }
#endif
#ifdef	USEMESSAGES
    l.type = MYMSG;
    msgsnd (goutfile, (struct msgbuf *) &l , sizeof (l) - sizeof (long) , 0 );
#endif

#ifndef	TEXTONLY
    if (g.dir >= 0) arrow (g.x,g.y,g.dir,0);  /* Cancella freccia */
#endif
#ifdef	USESPIPES
    if (read (ginfile,&g,sizeof (g)) != sizeof (g))
    {
       return -1;
    }
#endif
#ifdef	USEMESSAGES
    if (msgrcv (ginfile, (struct msgbuf *) &g , sizeof (g) - sizeof (long) , 0, 0 )<0)
    {
#ifdef	TEXTONLY
      perror ("Campo legge da guardia");
#endif    
      return -1;
    }
#endif
#ifdef	TEXTONLY
    printf ("Guardia in %d %d - Dir %d\n",g.x,g.y, g.dir);
#else
    if (g.dir >= 0) arrow (g.x,g.y,g.dir,GCOLOR);     /* Disegna freccia */
#endif
#ifdef	USESPIPES
    r = (g.x > l.x - ERR && g.x < l.x + ERR &&
       g.y > l.y - ERR && g.y < l.y + ERR);
    write (loutfile,&r,sizeof (r));
#endif
#ifdef	USEMESSAGES
    r.r = (g.x > l.x - ERR && g.x < l.x + ERR &&
       g.y > l.y - ERR && g.y < l.y + ERR);
    r.type = MYMSG;
    msgsnd (loutfile, (struct msgbuf *) &r , sizeof (r) - sizeof (long) , 0 );
#endif
  } while (l.dir >= 0);
  printf ("Gara conclusa in %d mosse\n", - l.dir);
  return 0;
}


[Home Page dell'ITIS "Fermi"] [Indice Angolinux] [Precedente] [Successivo]