Newsgroup: it.comp.lang.c
Titolo: Processi concorr. e problemi con select

Salve a tutti,
non riesco a spiegarmi il comportamento del programma di cui al fondo
di questo post troverete il codice.
Sistema operativo: Linux
Vi spiego cosa intendo realizzare:
un primo processo (processo_1) invia dei dati  ad un processo che "gli
sta a valle" chiamato processo_2.
Quest'ultimo segnalerà tramite  una signal (su un semaforo
implementato con una pipe) che è disponibile a ricevere il dato per
elaborarlo.
Nel contempo il processo_2  deve interagire anche con la tastiera e
cioè a seconda del comando che l'utente inserisce premendo un tasto
deve fare una cosa piuttosto che un'altra.
PROBLEMA:terminato l'invio dei dati da processo_1 a processo_2
quest'ultimo dovrebbe rimaner bloccato sulla select e "superarla" solo
quando l'utente invia un comando da tastiera, elaborarlo e ribloccarsi
sulla select.
In realtà la select viene comunque superata e il processo si pianta.
Perchè? Quale evento fa superare la select se il processo_1 non invia
più dati e se non si invia alcun comando?
Ciao a tutti, grazie
                                           Openyourmind
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <stdio.h>
#include <stdlib.h>
void gestore(int );
void sem_init (int* );
void sem_signal (int* );
void sem_wait (int* );
int casuale(float );
int casuale2(int, float );
int pipe2[2];
int sem1[2];
int sem2[2];
int main (int argc, char *argv[]) {
	int pid, i, status;
	pipe(pipe2);
	sem_init(sem1);
	sem_init(sem2);
	if(fork() == 0) {
		processo_1();
		exit(EXIT_SUCCESS);
	}
	if(fork() == 0) {
		processo_2();
		exit(EXIT_SUCCESS);
	}
	for(i=0; i<2; i++)
		pid = wait(&status);i, status;
	exit(EXIT_SUCCESS);
}
int processo_1(void) {
// Per 3 volte:
// Genera un valore casuale val e dopo sleep(val)
// passa tale valore al processo_2 (se questo gli da l'ok)
	int i=1,val=0;
	close(pipe2[0]);
	while(1) {
		if ( i<4 ){
			val = casuale2(3, 5);
			buf=val;
			sleep(val);
			sem_wait(sem2);
			//quando il processo a valle segnala che è disponibile
			//gli invio val, cioè il dato da elaborare
			write(pipe2[1], &buf, sizeof(int));
			i++;
		}
	}//non muore mai
}
//-----------------------------------------
int processo_2 (void) {
	int elab_time_2 = 0, buf = 0, num_dati_ric = 0;
	int dato_ricev;
	fd_set rfds;
	FD_ZERO(&rfds);
	FD_SET (pipe2[0], &rfds);
	FD_SET (STDIN_FILENO, &rfds);
	sem_signal(sem2);//ok x ricevere dati da processo_1
	while(1) {
		signal(SIGALRM, gestore);
		select (pipe2[0]+1, &rfds, NULL, NULL, NULL);
		printf("\n\tSELECT DONE\n");
		if (FD_ISSET(pipe2[0], &rfds)) {
			//select oltrepassata perchè processo_1
			//ha inviato dati
			read(pipe2[0], &dato_ricev, sizeof(int));
			elab_time_2 = casuale2(1, 4);
			num_dati_ric++;
			printf("Ricevuto da processo_1 il %d° dato: -%d-
\n",num_dati_ric,dato_ricev);
			alarm(elab_time_2);
			// avvia un time out al termine del quale  verra inviato un segnale
che "azionerà"
			// un gestore che avviserà processo_1 che processo_2 può ricevere
altri dati
			// simula sleep(elab_time);
			//  sem_signal(sem2);
			// ma non è bloccante e allora può ricevere dati anche dallo
stdinput ed elaborarli
			FD_SET (STDIN_FILENO, &rfds);
		}
		else{
                                //select oltrepassata perchè inserito
comando da tastiera
				read(STDIN_FILENO, &buf, 2);
				if (buf != 2680){ //x + INVIO da tastiera
					printf ("---Ricevuto da tastiera %d da tastiera\n",buf);
					//return(0);
				}
				else{
					printf("---Ricevuto da tastiera \"x+INVIO\"\n");
					printf("");
				}
				FD_SET(pipe2[0], &rfds);
			}
	}//non muore mai
}
//-------Definizione delle funzioni--------
void sem_signal (int* s){
	write (s[1], "X", 1);
}
//-----------------------------------------
void sem_wait (int* s){
	char b; read (s[0], &b, 1);
}
//-----------------------------------------
void sem_init (int *s){
	pipe (s);
}
//-----------------------------------------
void gestore(int signum){
	sem_signal(sem2);
	printf("Ho fatto la signal\n");
        //segnala a processo_1 che processo_2 è disponibile a ricevere
dati
}
//-----------------------------------------
int casuale(float max){
	struct timeb tb;
	ftime (&tb);
	srand (tb.millitm);
	return (int) (max*rand()/(RAND_MAX+1.0));
}
//-----------------------------------------
int casuale2(int min, float max){
	int x;
	struct timeb tb;
	max = max+1;
	ftime (&tb);
	srand (tb.millitm);
	x = max*rand()/(RAND_MAX+1.0);
	while (x < min)
			x = max*rand()/(RAND_MAX+1.0);
	return (x);
}

Devi spostare il blocco:
FD_ZERO(&rfds);
        FD_SET (pipe2[0], &rfds);
        FD_SET (STDIN_FILENO, &rfds);
All'interno del while loop, proprio prima di select().
select() purtroppo non garantisce egual comportamento in caso di
segnali su tutte le piattaforme.
Comunque questo non e' un problema di C, e' un problema di
programmazione Posix (comp.unix.programmer sarebbe un posto piu'
adatto alla tua richiesta).

Non è cambiato nulla.
Grazie  comunque ;-)


Sei sicuro di questo?


Si, sicuro.
Ovviamente, per come son stati scritti,  i processi rimarranno sempre
"vivi" continuando a ciclare sul while ( 1 ){...}  presente
all'interno delle funzioni processo_1( ) e processo_2( ), come
indicato dal commento.
La cosa è voluta, e d'altro canto mi è necessaria  per il processo_2,
perchè vorrei mantenere l'interazione con la tastiera anche dopo che
l'elaborazione di processo_2 è conclusa.
Ad esempio immagina che processo_2 elabori i dati e li conservi in una
qualche forma per visualizzare su richiesta da tastiera dell'utente
delle statistiche, dei report, etc... Vorrei che avvenisse questo sia
mentre i dati arrivano da processo_1 (ed effettivamente avviene) sia
quando i dati son son stati tutti elaborati (stavolta
processo _2 si pianta)


Ho provato il tuo codice.
Ho alcuni commenti da fare, piu' la soluzione in fondo: ma non
scrollare perche' altrimenti i commenti non servono a nulla.
Quando posti il codice, mi raccomando di:
a) pulirlo (sem1 non viene mai usato)
b) scriverlo in C standard, almeno ISO C90 (i prototipi ci vogliono
tutti)
c) verificare che almeno si compili (buf alla linea 47 non e'
dichiarato; ho dovuto aggiungere io int buf)
d) non usare i commenti C99/C++ : quando si fa il taglia incolla, non
mantengono l'effetto desiderato: e' meglio usare /* */
Soluzione:
Ti sei dimenticato di controllare i valori di ritorno delle funzioni
di sistema, un errore imperdonabile.
Se l'avessi fatto, avresti scoperto che la select ritorna -1 a causa
della chiamata alarm() che tu spedisci.
Le funzioni di sistema, quando interrotte, impostano errno al valore
EINTR.
Il che significa che intorno alla select devi mettere un bel loop:
int sret;
...
do {
    sret = select (pipe2[0]+1, &rfds, NULL, NULL, NULL);
} while (sret == -1 && errno == EINTR);
Ovvio che e' meglio evitare di usare la select con un timer infinito.
Comunque devi pulirlo un po' il codice. Ma almeno hai la soluzione al
mistero della select!
Buona fortuna!

Sei un grande!
Gli errori imperdonabil hanno però un pregio: diventano
indimenticabili ;-)
E' stata un'ottima lezione.....
Ciao, grazie