C e segnali

Forum dedicato alla programmazione.

Moderatore: Staff

Regole del forum
1) Citare in modo preciso il linguaggio di programmazione usato.
2) Se possibile portare un esempio del risultato atteso.
3) Leggere attentamente le risposte ricevute.
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.
Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

C e segnali

Messaggio da ulisse89 »

Ciao a tutti. Ho una domanda.
Sto facendo (per esigenze didattiche) un programma (in C) che crea due figli, ognuno dei due deve contare per lo stesso file quante righe hanno più di N caratteri. Ogni figlio si occupa di un carattere diverso. Ogni volta che un figlio trova una riga con più di N caratteri lo segnala al padre con SIGUSR1/SIGUSR2.
Il problema è che la funzione che conta, conta correttamente, ma il padre sembra ricevare al massimo 1 SIGUSR1/SIGUSR2. Cioè se un figlio non trova linee, non manda segnali, tutto ok. Se ne trova una, manda un SIGUSR1/2, tutto ok. Ma se ne trova più di una allora il padre sembra comunque rilevare un solo SIGUSR1/2.
Mi chiedo se, quando un processo manda un segnale più volte ad un altro processo, il processo destinatario entra nella routine di servizio per tutte le volte che riceve il segnale, o una volta per tutte.

Io posto il codice, ma è abbastanza lungo e non so quanto abbiate voglia di mettervi a leggerlo, quindi se potete chiaritemi solo la questione dei segnali.

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>

void print_usage(){
  printf("Usage: contaCaratteri <c1> <c2> <N> <fileIn> <fileOut>\n");
}

int N, lines1, lines2, terminated;

void conta(int fd, char c, int signum);
void lines(int signum);
void handler(int signum);
void wait_child();

int main(int argc, char ** argv){
  
  int fdOut, fdIn, i;
  int pid;
  char c1, c2;
  char msg1[50], msg2[50];

  lines1 = 0;
  lines2 = 0;
  terminated = 0;

  if(argc != 6){
    print_usage();
    exit(-1);
  }

  N = atoi(argv[3]);
  c1 = argv[1][0];
  c2 = argv[2][0];
  if((fdIn = open(argv[4], O_RDONLY)) == -1){
    printf("Il file in lettura non esiste.\n");
    exit(-1);
  }
  close(fdIn);

  if((signal(SIGUSR1, lines)) == SIG_ERR)
     perror("Prima signal non riuscita\n");
  if((signal(SIGUSR2, lines)) == SIG_ERR)
    perror("Seconda signal non riuscita\n");
  if((signal(SIGTERM, handler)) == SIG_ERR)
    perror("Terza signal non riuscita\n");

  for(i = 0; i < 2; ++i){
    pid = fork();
    if(pid == 0){
      if(i == 0){
	if((fdIn = open(argv[4], O_RDONLY)) == -1){
	    printf("Errore nell'apertura del file in lettura.\n");
	    exit(-1);
	}
	conta(fdIn, c1, SIGUSR1);
	kill(getppid(), SIGTERM);
	exit(0);
      }
      else if (i == 1){
	if((fdIn = open(argv[4], O_RDONLY)) == -1){
	    printf("Errore nell'apertura del file in lettura.\n");
	    exit(-1);
	}
	conta(fdIn, c2, SIGUSR2);
	kill(getppid(), SIGTERM);
	exit(0);
      }
    }
    else if (pid < 0)
      perror("Fork error");
  }

  while(terminated != 2);

  /* Scrittura risultato in fileOut */
  if((fdOut = open(argv[5], O_WRONLY|O_CREAT, 0755)) == -1){
     printf("Errore nell'apertura del file in scrittura.\n");
     exit(-1);
  }
  sprintf(msg1, "Il file contiene %d linee con più di %d occorrenze di %c\n", lines1, N, c1);
  sprintf(msg2, "Il file contiene %d linee con più di %d occorrenze di %c\n", lines2, N, c2);
  if((write(fdOut, msg1, strlen(msg1))) < strlen(msg1))
    printf("Errore in scrittura del file.\n");
  if((write(fdOut, msg2, strlen(msg2))) < strlen(msg2))
     printf("Errore in scrittura del file.\n");
  close(fdOut);

  wait_child();
}
  
/* Raccolta stato figli */
void wait_child(){
  int i, status, pid;
  for(i = 0; i < 2; i++){
    pid = wait(&status);
    if(WIFEXITED(status)){
      printf("Il processo figlio con pid %d è terminato volontariamente con stato %d\n", pid, WEXITSTATUS(status));
    }
    else if (WIFSIGNALED(status))
      printf("Il processo figlio con pid %d è terminato involontariamente a causa del segnale %d\n", pid, WTERMSIG(status));
  }
}

void conta(int fd, char c, int signum){
  
  char buff[1];
  int occurrance = 0;
  while(read(fd, buff, 1) > 0){
    if(buff[0] == '\n'){
      if(occurrance > N){
	kill(getppid(), signum);
      }
      occurrance = 0;
    }
    else if (buff[0] == c)
      ++occurrance;
  }
  close(fd);
  return;
}

void lines(int signum){
  if(signum == SIGUSR1)
    lines1++;
  else if(signum == SIGUSR2)
    lines2++;
  return;
}

void handler(int signum){
  terminated++;
  return;
}
Grazie.

Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

Re: C e segnali

Messaggio da ulisse89 »

A quanto pare il probema è che se arrivano nuovi segnali quando il processo sta eseguendo la routine di gestione di un precedente segnale, questi vengono persi.
Per cui mi è bastato rallentare l'invio di segnali da parte dei figli con una sleep(n).
Io pensavo che venissero accodati, e poi pian piano serviti tutti.
Qualcuno mi sa dire se effettivamente vengono gestiti così?
Le slide da cui sto studiando dicono che le possibili gestioni sono:
- innestamento delle routine
- perdita di segnali
- accodamento
Ma non dice poi quale effettivamente viene usata.

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6628
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: C e segnali

Messaggio da targzeta »

Non so su cosa studi comunque da signal(2):

Codice: Seleziona tutto

The behavior of signal() varies across Unix versions, and has also varied historically across different versions of Linux.  Avoid its use: use sigaction(2) instead.
Inoltre ti consiglio di leggere signal(7) e più precisamente la sezione "Signal Mask and Pending Signals", questo penso ti sia sufficiente a capire cosa fare e come comportarti.

Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

Re: C e segnali

Messaggio da ulisse89 »

Grazie Emanuele!
Come al solito RTFM torna sempre utile.
Purtroppo sono costretto a usare le system call che ci hanno dato perciò mi posso scordare sigaction per ora.
Dal man di signal ho letto che i segnali vengono bloccati quando si entra nella routine e risbloccati una volta usciti.
Perciò penso che l'unica soluzione possibile (per me che devo usare la signal), sia quella di rallentare l'invio di segnali.

Una cosa non ho capito bene.
Un segnale può essere bloccato, che significa che esso non verrà
recapitato fino a quando non verrà sbloccato. Un segnale viene
definito pendente nel periodo di tempo che passa tra quando è stato
generato e quando è recapitato.
Vuol dire che il segnale viene perso o solo che viene recapitato più tardi?

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6628
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: C e segnali

Messaggio da targzeta »

Riccardo, ma le slide sono disponibili in rete? Ho visto un po' il tuo programma e, a parte lo stile di programmazione che non mi ispira molto (appena posso ti posto qualcosa così magari vedi se ti convinco), questa riga mi ha proprio spiazzato:

Codice: Seleziona tutto

while(terminated != 2);
è veramente brutta non trovi?

Ora vedo se posso darti una mano, mi ha intrigrato la faccenda perchè è da un po' che non uso i segnali,
Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

Re: C e segnali

Messaggio da ulisse89 »

Le slide sono queste, relativamente ai segnali: http://lia.deis.unibo.it/Courses/sot091 ... liUnix.pdf.

La riga di cui parli è l'unico modo a cui sono arrivato per fare aspettare il padre sino a che entrambi i figli non gli inviano SIGTERM.
E' brutta perchè? Perchè fa cicli inutili?
Sarebbe meglio

Codice: Seleziona tutto

while(terminated !=2) pause();
Sicuramente farebbe meno cicli inutili.


Grazie comunque dell'interesse. Ci sto perdendo la testa perchè non riesco a capire proprio cme funziona.

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6628
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: C e segnali

Messaggio da targzeta »

Esatto, sarebbe meglio così. Non si tratta di cicli inutili in meno, ma la tua soluzione è un attesa attiva mentre pause() fa attesa passiva. In pratica con la pause non stressi la CPU. Tu non te ne accorgi perchè il programma è veloce, ma se il file da analizzare fosse molto grande vedresti la differenza, con la prima soluzione il processore sarebbe sempre al 100% di utilizzo.

Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

Re: C e segnali

Messaggio da ulisse89 »

Però devo capire perchè:
- Se tolgo la sleep(1) dalla funziona conta(), e lascio

Codice: Seleziona tutto

while(terminated != 2);
, allora non conta bene.
- Se tolgo sleep(1) e cambio il while aggiungendoci la pause() va bene.
Lasciamo perdere il programma che faresti troppa fatica a leggere e a capire, senza avere le specifiche dell'esercizio.
Vorrei capire. Io ho un padre e un figlio. Il figlio invia a ripetizione dei segnali al padre, il padre per ognuno di questi entra nella routine di gestione.
Ma se il padre sta gestendo un segnale e ne arrivano altri, i segnali che arrivano vanno persi? E soprattutto: se vengono accodati da qualche parte per recapitarli in seguito, viene salvato anche l'ordine in cui sono arrivati?
Sono un po' confuso: non mi sembra di aver fatto dei grandi errori nel mio programma, ma cambiando anche solo quella piccola cosa della pause(), cambia il risultato del programma.

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Re: C e segnali

Messaggio da Mario Vanoni »

Mi intriga questo fatto, ma non so spiegarlo:

man 2 fork
* The termination signal of the child is always SIGCHLD (see clone(2)).

Nel tuo programma non compare, solo SIGUSR[12].

Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

Re: C e segnali

Messaggio da ulisse89 »

Mario Vanoni ha scritto:Mi intriga questo fatto, ma non so spiegarlo:

man 2 fork
* The termination signal of the child is always SIGCHLD (see clone(2)).

Nel tuo programma non compare, solo SIGUSR[12].
Nel mio programma c'è la funzione wait_child(), che fa uso della wait() per raccogliere lo stato dei figli. In questo modo c'è una attesa esplicita della terminazione dei figli.
Se faccio una funzione handler che gestisca SIGCHLD, posso raccogliere lo stato di terminazione del figlio solo quando effettivamente è terminato invece di mettermi in attesa.
In ogni caso l'azione di default per SIGCHLD è ignore.

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6628
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: C e segnali

Messaggio da targzeta »

Mario quella è un altra cosa.

Comunque, da quel che ricordavi io i segnali non dovrebbero andare persi, non puoi sapere quale è stato inviato prima ma ci dovrebbe essere un contatore che si incrementa ad ogni invio e si decrementa ad ogni gestione. Però sta di fatto che questo programma sembra perdere i segnali.

L'unica cosa che vale la pena di notare è proprio quell'attesa sui segnali. Io la eliminerei, in fondo se perdi un SIGTERM (o se un figlio termina inaspettatamente) il padre non termina mai. Già esegui le wait per attendere la fine dei figli quindi credo che sia meglio eliminare proprio l'attesa dei segnali.

Posto il mio codice con qualche FIX da fare. Nota solo il minor uso di variabili globali e codice meno ridondante (comunque anche questa soluzione non mi piace molto):

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

void print_usage()
{
  printf("Usage: contaCaratteri <c1> <c2> <max> <fileIn> <fileOut>\n");
}

int lines[2] = { 0, 0 };

void conta(char *, int, char *, int *, int);
void sig_handler(int);

int main(int argc, char ** argv)
{
  FILE *fdOut, *fdIn;
  int i, pid, status, sig[2] = { SIGUSR1, SIGUSR2 } , max;
  char c[2];

  if ( argc != 6 )
    {
      print_usage();
      return -1;
    }

  max = atoi(argv[3]);
  c[0] = *(argv[1]);
  c[1] = *(argv[2]);

  /* FIX: l'esistenza del file si fa con la stat() */
  if ( (fdIn = fopen(argv[4], "r")) == NULL )
    {
      printf("Il file in lettura non esiste.\n");
      return -1;
    }
  fclose(fdIn);

  for ( i = 0; i < 2; i++ )
    if ( (signal(sig[i], sig_handler)) == SIG_ERR )
      perror("signal() non riuscita");

  /* FIX: la perror() non termina il programma. se una fork va male
     il numero di wait() dovrebbe diminuire oppure il programma aspetta
     all'infinito. Altrimenti si termina al fallimento della fork().
  */
  for ( i = 0; i < 2; i++ )
    if ( (pid = fork()) < 0 )
      perror("Fork error");
    else
      if ( pid == 0 )
        conta(argv[4], i, c, sig, max);

  for ( i = 0; i < 2; i++ )
    {
      pid = wait(&status);
      if ( WIFEXITED(status) )
        printf("Il processo figlio con pid %d è terminato volontariamente con stato %d\n", pid, WEXITSTATUS(status));
      else
        if ( WIFSIGNALED(status) )
          printf("Il processo figlio con pid %d è terminato involontariamente a causa del segnale %d\n", pid, WTERMSIG(status));
    }

  /* Scrittura risultato in fileOut */
  if ( (fdOut = fopen(argv[5], "w")) == NULL )
    {
      printf("Errore nell'apertura del file in scrittura.\n");
      exit(-1);
    }
  for ( i = 0; i < 2; i++ )
    if ( fprintf(fdOut, "Il file contiene %d linee con più di %d occorrenze di %c\n", lines[i], max, c[i]) < 0 )
      printf("Errore in scrittura del file.\n");
  fclose(fdOut);

  return 0;
}

void conta(char *path, int id, char *c, int *sig, int max)
{
  FILE *file;
  int buff, occurrance;

  occurrance = 0;
  if ( (file = fopen(path, "r")) == NULL )
    exit(-1);

  while ( (buff = fgetc(file)) != EOF )
    if ( buff == '\n' )
      {
        if ( occurrance > max )
          kill(getppid(), sig[id]);
        occurrance = 0;
      }
    else
      if ( buff == c[id] )
        ++occurrance;
  /* FIX: controllare se buff e' veramente EOF o un errore */
  fclose(file);

  exit(0);
}

void sig_handler(int signum)
{
  switch ( signum )
    {
    case SIGUSR1:
      lines[0]++;
      break;
    case SIGUSR2:
      lines[1]++;
      break;
    }

  return;
}
Se acquisisci altre informazioni sarei curioso di esserne informato,
Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

Re: C e segnali

Messaggio da ulisse89 »

Mi piace la tua soluzione. Ci sono però alcune cose che non avendo avuto il testo non potevi sapere.
1) Non posso usare fopen, fwrite, fread, ma vabbè. E' solo codice in più, non influiscono sulla riuscita o meno del programma.
2) Per forza devo segnalare la fine dell'elaborazione dei figli, ovvero mandare SIGTERM prima di terminare. Lo so che non ha senso però il testo richiede così. Di conseguenza non posso prescindere dal mettere nel padre una funzione che attende (while per l'appunto).

Grazie del chiarimento sulla fork, infatti se una fork va male converrebbe terminare forzatamente il programma perchè non potrebbe andare oltre.
Rimane il dubbio sui segnali. Se non viene memorizzato l'ordine allora potrebbe venire servito un SIGTERM prima di un SIGUSR e allora sballerebbe tutto.
Sei sicuro che sia così? Anche se le prove portano a questa conclusione, sarebbe comunque una cosa senza senso.

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6628
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: C e segnali

Messaggio da targzeta »

Oddio, sicuro è un parolone. E' molto tempo che non uso i segnali e quindi non sono sicuro di niente, anzi se tu scopri qualcosa di più fammelo sapere. Secondo me lo scopo dell'esercizio è proprio quello di sottolineare il limite dei segnali nonché della signal(2). Quando passerai alla sigaction(2) vedrai che potrai fare molte cose in più.
Immaginavo che non potevi usare le fopen() ma le sprintf() prima della write() mi sembravano brutte, così come la read di un byte (in quel caso buff[1] puoi risparmiartelo passando dichiarando 'char buff;' e passando &buff...dovrebbe funzionare).

Sui segnali aggiungo che oltre ad essere quasi sicuro che non puoi sapere quale è arrivato prima sono anche quasi sicuro che alcuni segnali vengono consegnati con una priorità maggiore.

Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Re: C e segnali

Messaggio da Mario Vanoni »

ulisse89 ha scritto:
Mario Vanoni ha scritto:Mi intriga questo fatto, ma non so spiegarlo:

man 2 fork
* The termination signal of the child is always SIGCHLD (see clone(2)).

Nel tuo programma non compare, solo SIGUSR[12].
Nel mio programma c'è la funzione wait_child(), che fa uso della wait() per raccogliere lo stato dei figli. In questo modo c'è una attesa esplicita della terminazione dei figli.
Se faccio una funzione handler che gestisca SIGCHLD, posso raccogliere lo stato di terminazione del figlio solo quando effettivamente è terminato invece di mettermi in attesa.
In ogni caso l'azione di default per SIGCHLD è ignore.
Scusa l'intrusione, ma dove trovo una documentazione su wait_child?
Cercando inutilemente ho trovato forse un'idea al tuo problema
http://book.chinaunix.net/special/ebook ... 1sec9.html
For a parent and child relationship, we often have the following scenario. After the fork, both the parent and the child have something to do. For example, the parent could update a record in a log file with the child's process ID, and the child might have to create a file for the parent. In this example, we require that each process tell the other when it has finished its initial set of operations, and that each wait for the other to complete, before heading off on its own.
Se inutile chiedo perdono.

Avatar utente
ulisse89
Packager
Packager
Messaggi: 643
Iscritto il: sab 17 gen 2009, 12:53
Nome Cognome: Riccardo
Slackware: 13.0
Kernel: 2.6.29.6
Desktop: Xfce
Località: Bologna

Re: C e segnali

Messaggio da ulisse89 »

spina ha scritto:Oddio, sicuro è un parolone. E' molto tempo che non uso i segnali e quindi non sono sicuro di niente, anzi se tu scopri qualcosa di più fammelo sapere. Secondo me lo scopo dell'esercizio è proprio quello di sottolineare il limite dei segnali nonché della signal(2). Quando passerai alla sigaction(2) vedrai che potrai fare molte cose in più.
Mi piacerebbe poterti dire che è così, ma il corso è già finito (per la cronaca si tratta di Sistemi Operativi) e si ferma alla signal() per quanto riguarda i segnali.
Sui segnali aggiungo che oltre ad essere quasi sicuro che non puoi sapere quale è arrivato prima sono anche quasi sicuro che alcuni segnali vengono consegnati con una priorità maggiore.
Guarda adesso sono sotto un altro esercizio in cui per forza devo capire come funziona. La mia soluzione non dà l'esito aspettato, quella che ci è stata data sì.
Testo: http://lia.deis.unibo.it/Courses/sot091 ... 82x%29.pdf (Esercizio 2)
Soluzione: http://lia.deis.unibo.it/Courses/sot091 ... correggi.c
Mia soluzione:

Codice: Seleziona tutto

#include <stdio.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>

void handler_segnali(int signum);
void gestione_figlio(int signum);
void wait_child();

void print_usage(){
  printf("Usage: correggi <fileIn> <fileOut>\n");
}

int correzioni, terminati, fdIn, pid[3];

int main(int argc, char ** argv){

  /* Variabili */
  int fdOut;
  int i;
  int coppia[2];
  char messaggio[50];

  correzioni = 0;
  terminati = 0;
  
  if(argc != 3){
    print_usage;
    exit(-1);
  }

  if((signal(SIGUSR1, handler_segnali)) == SIG_ERR)
     perror("Errore nella prima signal");
  if((signal(SIGTERM, handler_segnali)) == SIG_ERR)
    perror("Errore nella seconda signal");

  pid[0] = getpid();
  for(i = 1; i < 3; i++){
    pid[i] = fork();
    if(pid[i] == 0){
      if(i == 1){
	/* Imposto la gestione dei segnali */
	if((signal(SIGUSR1, gestione_figlio)) == SIG_ERR)
	  perror("Errore nella signal figlio");
	if((signal(SIGUSR2, gestione_figlio)) == SIG_ERR)
	  perror("Errore nella signal figlio");
	if((signal(SIGTERM, gestione_figlio)) == SIG_ERR)
	  perror("Errore nella signal figlio");

	if((fdIn = open(argv[2], O_RDWR|O_CREAT, 0777)) == -1)
	  perror("Errore nella open del fileOut");

	while(1)
	  pause();
	exit(0);
	
      }
      else{
	if((fdIn = open(argv[1], O_RDONLY)) == -1){
	  perror("Errore nell'apertura del file da parte di P2\n");
	  exit(-1);
	}
	sleep(2);  /* tempo di impostare signal P1 */
	while((read(fdIn, coppia, 2*sizeof(int))) > 0){
	  if(coppia[0] >= coppia[1]){
		kill(pid[1], SIGUSR1);
	  }
	  else{
	    kill(pid[1], SIGUSR2);
	  }
	  if((lseek(fdIn, sizeof(int), SEEK_CUR)) < 0)
	    perror("Errore lseek P2");
	  sleep(1);
	}
	kill(pid[1], SIGTERM);
	close(fdIn);
	exit(0);
      }	
    }
    else if (pid[i] < 0)
      perror("Fork error\n");
  }

  while(terminati == 0){
    pause();
  }
  wait_child;
  wait_child;

  /* Scrittura informazioni nel file di output */
  if((fdOut = open(argv[2], O_WRONLY)) == -1)
    perror("Errore nella open");
  sprintf(messaggio, "Sono state effettuate %d correzioni\n", correzioni);
  if((write(fdOut, messaggio, strlen(messaggio))) < strlen(messaggio))
    perror("Errore nella write");
  close(fdOut);
}

void handler_segnali(int signum){
  if(signum == SIGUSR1)
    correzioni++;
  if(signum == SIGTERM)
    terminati++;
}

void gestione_figlio(int signum){
  int tris[3];
  if(signum == SIGUSR1){
    if((read(fdIn, tris, 3*sizeof(int))) == -1)
      perror("Errore nella read");
    if(tris[0] != tris[2]){
      /* Scrivo il valore corretto */
      if((lseek(fdIn, - sizeof(int), SEEK_CUR)) < 0)
	perror("Errore nella lseek di P1");
      if((write(fdIn, tris, sizeof(int))) < sizeof(int))
	perror("Errore nella write");
      kill(pid[0], SIGUSR1);
      sleep(1);
    }
  }
  else if(signum == SIGUSR2){
     if((read(fdIn, tris, 3*sizeof(int))) == -1)
      perror("Errore nella read");
    if(tris[1] != tris[2]){
      /* Scrivo il valore corretto */
      if((lseek(fdIn, - sizeof(int), SEEK_CUR)) < 0)
	perror("Errore nella lseek di P1");
      if((write(fdIn, &(tris[1]), sizeof(int))) < sizeof(int))
	perror("Errore nella write");
      kill(pid[0], SIGUSR1);
      sleep(1);
    }
  }
  else if(signum == SIGTERM){
    close(fdIn);
    kill(pid[0], SIGTERM);
    exit(0);
  }
  else
    printf("Never happen!!\n");
}
  
/* Raccolta stato figlio */
void wait_child(){
  int status, pid;
  pid = wait(&status);
  if(WIFEXITED(status)){
    printf("Figlio con pid %d: terminato volontariamente con stato %d\n", pid, WEXITSTATUS(status));
  }
  else if (WIFSIGNALED(status))
    printf("Figlio con pid %d: terminato involontariamente a causa del segnale %d\n", pid, WTERMSIG(status));
}
Comunque ci sto guardando io. Ti ho postato tutto nel caso avessi tempo da perdere e curiosità.


@Mario
Su SIGCHLD non so. Quello che ti ho detto io, lo puoi trovare anche su wikipedia. Se vuoi approfondire non saprei proprio.
Il link che hai postato, gli ho dato un occhiata, ma non penso mi aiuti. Però guarderò meglio.
Ultima modifica di ulisse89 il dom 20 giu 2010, 13:19, modificato 1 volta in totale.

Rispondi