Newsgroup: it.comp.os.linux.development
Titolo: Rallentamento in ricezione dalla seriale (i.e. perdita di dati)

Devo leggere e scrivere dei dati da /dev/ttyS0 a 2400 bps, 8n1, senza
nessun tipo di sincronizzazione o controllo di flusso (il collegamento
e` costituito solo da TxD, RxD e la massa). Il dispositivo collegato
alla seriale spedisce dei pacchetti composti da due caratteri, e fra i
pacchetti c'e` un intervallo minimo di circa 100 ms.
Con qualche problema (il serial programming howto e` stato scritto
digitando lettere a caso sulla tastiera) sono riuscito a impostare la
ricezione/trasmissione come volevo io. Se spedisco un pacchetto "ogni
tanto", esso viene ricevuto sempre e senza eccezioni; se pero` spedisco
i pacchetti alla frequenza suddetta (quindi attorno a 10 al secondo, per
uno sbalorditivo transfer rate di 20 cps) apparentemente molti
pacchetti, almeno due su tre, vengono persi.
Ho notato anche che se elimino la chiamata alla read() (la ricezione e`
asincrona, uso i segnali per "notificare" i byte in arrivo), le
notifiche arrivano tutte. Quindi sembra che sia la read(), per motivi
che mi sfuggono, a perdere del tempo prima o dopo che ha ricevuto i due
byte.
Avete qualche idea? Riporto degli spezzoni salienti di codice (non mi
chiedete perche` ho usato la ricezione asincrona e inoltre ho creato un
processo separato, e` una lunga storia; comunque vi assicuro che ho
provato qualsiasi combinazione con i medesimi risultati):
/* Per inizializzare la seriale... */
if( (fd = open( serport, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
{
...
}
bzero( &tio, sizeof( tio));
tio.c_cflag = B2400 | CS8 | CLOCAL | CREAD;
tio.c_cc[VTIME] = 0;
tio.c_cc[VMIN] = 1;
tcflush( fd, TCIFLUSH);
tcsetattr( fd, TCSANOW, &tio);
/* La funzione che gestisce le notifiche... */
void signal_handler_IO( int status)
{
    wait_flag = FALSE;
}
/* Il processo che riceve i byte... */
void RXthread( int fdser, int fdpipe)
{
    char buf[2];
    int res;
    signal( SIGIO, signal_handler_IO);
    fcntl( fdser, F_SETOWN, getpid());
    fcntl( fdser, F_SETFL, O_ASYNC);
    while( TRUE)
    {
        if( !wait_flag)
        {
            if( (res = read( fdser, buf, 2)) == 2)
                write( fdpipe, buf, res);
            wait_flag = TRUE;
        }
        else
            usleep( 100);
    }
}
---
Bye! Lorenzo Lutti
http://www.geocities.com/SiliconValley/Peaks/6258/

Se credi puoi provare a cambiare il loop di lettura con questo:
if (!wait_flag) {
	errno = 0;
again:
	while ((res = read(fdser, buf, 2)) > 0)
		write(fdpipe, buf, res);
	if (res == -1)
		 if (errno == EINTR) goto again;
		/* else if gestisci un generico read error */
	wait_flag = TRUE;
}
else usleep(100);
_dovrebbe_ essere più robusto, se non mi sfugge qualcosa, perchè l'ho
fatto al momento...
Se ti da fastidio il goto, ovviamente lo puoi rimpiazzare con un
do..while invertendo i tests. Personalmente trovo molto leggibile questo
impiego del goto.
Vorrei inoltre consigliarti cfmakeraw invece di bzero ed il resto,
perchè mi pare più leggibile. Se invece hai bisogno della massima
portabilità, comunque invece di bzero è meglio settare gli attributi.
ciao.

In realtà il seguente è ancora più affidabile, anche se è meno conciso. 
if (!wait_flag) {
        errno = 0;
	do {
        	while ((res = read(fdser, buf, 2)) > 0) {
again:              
			if (write(fdpipe, buf, res) < 0)
				if (errno == EINTR) goto again;
				else perror("in write(): ");
		}
	} while ((res == -1) && (errno == EINTR));
        if (res == -1) perror("in read(): ");
        wait_flag = TRUE;
}
else usleep(100);
Per non mettere 2 goto annidati, ho cambiato il ciclo esterno in un
do...while. Ho aggiunto un minimo di error-handling, perchè se non fa il
suo dovere forse così intrappoli qualcosa.
Attenzione, per via dell'impiego dei segnali, anzi di SIGIO in
particolare, portare questo codice su altri Un*x potrebbe dare
grattacapi.