View Full Version : [C] Open e read su una pipe con nome
Ed_Bunker
26-07-2004, 01:09
Ciao, ho un "astioso" problema che da qualche giorno mi affligge... Un processo server legge richieste da una pipe con nome la quale, prima di iniziare ad effettuare le letture, viene aperta in sola lettura. Siccome la open su una pipe e' bloccante, quando essa ritorna c'e' sicuramente un client che ha aperto la pipe stessa in scrittura e, quindi, quando il server effettuera' la read, essa:
_ o ritornera' il numero di bytes letti
_ oppure risultera' a sua volta bloccante fintanto che non c'e' qualche byte da leggere.
E fin qui e' proprio il comportamento che vorrei fosse tenuto dal server.
L'inconveniente e':
se c'e' un solo client con la pipe aperta in scrittura, quando esso, una volta ricevuta la risposta dal server, chiudera' la pipe in scrittura il server rimarra' (Non si sa quanto) con una pipe aperta in lettura senza che nessuno ce l'abbia aperta in scrittura.
A questo punto ogni read che il server effettuera' ritornera' 0 senza che si abbia lo "sperato" comportamento bloccante. Siccome vorrei evitare, ovviamente, attesa attiva come potrei fare per fare in modo che, anche quando il server rimane con la pipe aperta in sola lettura (Senza che nessuno l'abbia aperta in scrittura), possa effettuare delle read bloccanti evitando attesa attiva ?!? C'e' qualche "scappatoia" o qualche soluzione logica... alla quale la mia mente bacata non ha pensato ?!? :D
P.S.: avevo pensato di far aprire al server la pipe in modalita' scrittura/lettura in modo che l'effetto bloccante sia sempre dato dalla read (Mentre la open in scrittura/lettura sara' no bloccante). Ma non so se sia "eticamente" corretto...
thks ;)
ilsensine
26-07-2004, 10:24
Una poll prima della read?
Ed_Bunker
26-07-2004, 10:38
Originariamente inviato da ilsensine
Una poll prima della read?
Ok sembra quello che fa per me . :D
Altra domanda: avevo provato ad aprire la pipe prima in modalita' O_RDONLY (Bloccante sino a che il capo in scrittura non viene aperto da qualcuno) e subito dopo anche in modalita' O_WRONLY (Senza utilizzare il descrittore restituito ma solo per evitare che future read potessero restituire 0 come valore di ritorno). La prima lettura viene eseguita correttamente e la richiesta viene soddisfatta mentre, quando il server legge la seconda volta dalla pipe, la read restituisce -1 e la variabile errno mi urla in faccia: "Interrupted System Call".
A cosa potrebbe essere dovuto !? Ad un segnale non catturato o mal gestito o a cos'altro ?!
thks ;)
ilsensine
26-07-2004, 10:56
Dovresti essere sempre preparato a un EINTR. Se ti capita, devi semplicemente rieseguire la read.
EINTR viene generato ogni volta che arriva un segnale; non è un errore vero e proprio, ma una notifica.
Ed_Bunker
26-07-2004, 12:14
Originariamente inviato da ilsensine
Dovresti essere sempre preparato a un EINTR. Se ti capita, devi semplicemente rieseguire la read.
EINTR viene generato ogni volta che arriva un segnale; non è un errore vero e proprio, ma una notifica.
Si, infatti ottenfo un EINTR. Il fatto e' che anche nelle successive read (alla seconda) continuo ad ottenere degli EINTR all'infinito. E non so proprio perche'... :confused:
ilsensine
26-07-2004, 12:40
Strano...prova a definire un signalhandler per SIGPIPE
Oppure, ancora meglio: chiudi la pipe e riaprila...credo che le pipe abbiano un comportamento un pò "particolare".
Ed_Bunker
26-07-2004, 12:58
Originariamente inviato da ilsensine
Strano...prova a definire un signalhandler per SIGPIPE
Oppure, ancora meglio: chiudi la pipe e riaprila...credo che le pipe abbiano un comportamento un pò "particolare".
Trovato l'errore: derivava da uno sbaglio sulla sigaction per gestire il SIGCHLD...
Aprendo prima in lettura e poi in scrittura adesso funziona. Poi ho provato con la poll ma mi da qualche problema. In particolar modo quando la pipe rimane aperta in sola lettura dal server (Poiche' il primo client l'ha chiusa in scrittura) al ciclo successivo la poll non ha effetto bloccante (Perche?!?) e stessa cosa nelle chiamate successive anche se nessun client apre e chiude la pipe in scrittura. In questo modo ogni volta viene effettuata una read che ritorna (Come atteso) 0.
:confused:
ilsensine
26-07-2004, 13:10
Sto dando una occhiata; stai usando dei file di tipo pipe aperti con open() oppure la syscall pipe() ?
Ed_Bunker
26-07-2004, 14:03
Originariamente inviato da ilsensine
Sto dando una occhiata; stai usando dei file di tipo pipe aperti con open() oppure la syscall pipe() ?
Utilizzo la 'normale' open
ilsensine
26-07-2004, 14:10
Bene, bene...ho appena visto che la "normale" open su un pipe è _bloccante_ (nota: la open stessa, non solo la read!)
Una open con O_NONBLOCK invece non è bloccante.
Inoltre sono riuscito a riprodurre il tuo problema: la poll che ritorna "1" ma la read che ritorna "0", dopo la chiusura del client. Se vai a vedere però cosa c'è nel campo "revents" della struttura della poll relativa al pipe, non c'è POLLIN, ma...16, che corrisponde a POLLHUP!
In sintesi: credo che quando l'altro capo della pipe viene chiuso, devi chiudere e riaprire il file pipe.
ilsensine
26-07-2004, 14:22
Ad esempio questo funziona...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static void __attribute__((__noreturn__)) fail(const char *s) {
perror(s);
exit(-1);
}
int main(void) {
int fd;
int ret;
char buf[4096];
struct pollfd pfd;
reopen:
fd = open("pipe_name", O_RDONLY|O_NONBLOCK);
if(fd<0) fail("open");
pfd.fd = fd;
pfd.events = POLLIN;
fprintf(stderr, "pipe aperto sul fd %d...\n", fd);
retry:
ret = poll(&pfd, 1, -1);
fprintf(stderr, "poll=%d revents=%d\n", ret, pfd.revents);
if(pfd.revents&POLLIN) {
ret = read(fd, buf, 4095);
fprintf(stderr, "read=%d\n", ret);
if(ret<=0) goto retry;
buf[ret] = '\0';
fprintf(stderr, "recv %s\n", buf);
goto retry;
} else if(pfd.revents&POLLHUP) {
fprintf(stderr, "Hangup!\n");
close(fd);
goto reopen;
} else {
fprintf(stderr, "!?\n");
goto retry;
}
return 0;
}
Ed_Bunker
26-07-2004, 14:40
Originariamente inviato da ilsensine
Ad esempio questo funziona...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static void __attribute__((__noreturn__)) fail(const char *s) {
perror(s);
exit(-1);
}
int main(void) {
int fd;
int ret;
char buf[4096];
struct pollfd pfd;
reopen:
fd = open("pipe_name", O_RDONLY|O_NONBLOCK);
if(fd<0) fail("open");
pfd.fd = fd;
pfd.events = POLLIN;
fprintf(stderr, "pipe aperto sul fd %d...\n", fd);
retry:
ret = poll(&pfd, 1, -1);
fprintf(stderr, "poll=%d revents=%d\n", ret, pfd.revents);
if(pfd.revents&POLLIN) {
ret = read(fd, buf, 4095);
fprintf(stderr, "read=%d\n", ret);
if(ret<=0) goto retry;
buf[ret] = '\0';
fprintf(stderr, "recv %s\n", buf);
goto retry;
} else if(pfd.revents&POLLHUP) {
fprintf(stderr, "Hangup!\n");
close(fd);
goto reopen;
} else {
fprintf(stderr, "!?\n");
goto retry;
}
return 0;
}
Pero' in questo modo potresti fare attesa attiva senza "accorgertene". O no ?!? Tu controlli il valore di ritorno della poll ed in tutti i casi o fai una poll successiva oppure passi a fare la read (Nel caso ret valga 1). Ma il fatto che ret valga 0 non dovrebbe essere un errore, visto che tu non hai imposto un timeout alla poll (-1 potrebbe invece verificarsi in caso d'errore) ?!? Ora riprovo...
ilsensine
26-07-2004, 14:51
Originariamente inviato da Ed_Bunker
Pero' in questo modo potresti fare attesa attiva senza "accorgertene". O no ?!?
Cosa vuol dire "attesa attiva"?
Tu controlli il valore di ritorno della poll
Non ho controllato il valore di ritorno della poll in quanto nel mio esempio può solo essere 1. Nel tuo caso, dove gestisci dei segnali, può generare un EINTR.
Quello che controllo è "revents", ovvero lo stato del pipe.
Ma il fatto che ret valga 0 non dovrebbe essere un errore
L'unico ret che controllo è quello della read; il controllo comunque è pura paranoia, in quanto non può che essere >0 nel mio esempio.
Nel tuo caso stai anche qui attento a EINTR.
Ed_Bunker
26-07-2004, 15:07
Per attesa attiva indico la situazione nella quale si e' in attesa del verificarsi di un evento e, invece, di attendere che l'evento venga in qualche modo segnalato si continuano a fare delle chiamate (Che sprecano risorse) per capire se l'evento si e' verificato.
Tu controlli il valore di ritorno della read (Mi ero sbagliato nel dire che poll al posto di read :cry: ). Siccome la read viene effettuata dopo una poll essa dovrebbe restituire:
_-1 in caso d'errore (Ed errno settata)
_>0 altrimenti
Il fatto che venga fatta dopo una poll "impedisce" che la read restituisca un valore = 0. Il comportamento a quel punto dovrebbe essere bloccante. Mentre a me succede proprio questo ovvero la read mi restituisce 0 pur essendo chiamata dopo una poll.
ilsensine
26-07-2004, 15:27
Originariamente inviato da Ed_Bunker
Per attesa attiva indico la situazione nella quale si e' in attesa del verificarsi di un evento e, invece, di attendere che l'evento venga in qualche modo segnalato si continuano a fare delle chiamate (Che sprecano risorse) per capire se l'evento si e' verificato.
E' proprio quello che fa la poll: mette a dormire il programma finché non si verifica un certo evento...
Utilizzo di risorse zero
Ed_Bunker
26-07-2004, 15:39
Originariamente inviato da ilsensine
E' proprio quello che fa la poll: mette a dormire il programma finché non si verifica un certo evento...
Utilizzo di risorse zero
Perfetto: proprio quello che "desideravo". Il fatto e' che le read dopo la poll mi ritornano 0 bytes letti (Mentre essendo chiamate dopo la poll dovrebbero ritornare un valore maggiore di 0 oppure -1 nel caso di errore). E' come se la poll rilevasse che ci sono bytes da leggere anche se in realta' non e' cosi'...
Boh...
ilsensine
26-07-2004, 15:43
Originariamente inviato da Ed_Bunker
Perfetto: proprio quello che "desideravo". Il fatto e' che le read dopo la poll mi ritornano 0 bytes letti (Mentre essendo chiamate dopo la poll dovrebbero ritornare un valore maggiore di 0 oppure -1 nel caso di errore). E' come se la poll rilevasse che ci sono bytes da leggere anche se in realta' non e' cosi'...
Non è possibile se la poll restituisce l'evento POLLIN. Controlli revents?
Ed_Bunker
26-07-2004, 16:30
Originariamente inviato da ilsensine
Non è possibile se la poll restituisce l'evento POLLIN. Controlli revents?
No, perche' avendo messo events = POLLIN la poll dovrebbe tornare solo in caso d'errore o in caso di nuovi bytes da leggere sulla pipe. A meno che non ritorni perche' viene sempre rilevato un errore...
Return Value:
On success, a positive number is returned, where the number returned is
the number of structures which have non-zero revents fields (in other
words, those descriptors with events or errors reported). A value of 0
indicates that the call timed out and no file descriptors have been
selected. On error, -1 is returned, and errno is set appropriately.
ilsensine
26-07-2004, 16:35
Originariamente inviato da Ed_Bunker
No, perche' avendo messo events = POLLIN la poll dovrebbe tornare solo in caso d'errore o in caso di nuovi bytes da leggere sulla pipe.
Suggerisco una lettura approfondita della manpage di poll ;)
Ed_Bunker
26-07-2004, 16:50
Originariamente inviato da ilsensine
Suggerisco una lettura approfondita della manpage di poll ;)
Ecco cosi' mi sono ribeccato un "cazziatone"... :D
Comunque ora e' ok. Penso il comportamento anomalo derivasse ancora dall'errore che avevo commesso nelle sigaction.
ilsensine
26-07-2004, 16:53
Ti avevo postato pure un esempio pronto per il copia & incolla :D
pfd.events = POLLIN;
(...)
if(pfd.revents&POLLIN) {
(...)
} else if(pfd.revents&POLLHUP) {
nb nel signal handler ricorda di salvare e ripristinare errno ;)
Ed_Bunker
26-07-2004, 17:31
Originariamente inviato da ilsensine
Ti avevo postato pure un esempio pronto per il copia & incolla :D
nb nel signal handler ricorda di salvare e ripristinare errno ;)
Ehm.... falso "allarme". Continua a non funzionarmi. Prima andava perche' aprivo la pipe anche in scrittura oltre che in lettura.
:cry: :cry:
Alla signal handler di quale segnale fai riferimento ?! Gli unici segnali che gestisco diversamente dal comportamento di default sono il SIGCHLD, SIGINT e SIGALRM.
Ed_Bunker
26-07-2004, 18:55
La poll mi ritorna sempre con un POLLHUP. Quale e' la causa che da luogo a questo tipo di errore ?!?
ilsensine
28-07-2004, 07:45
Che l'altra parte ha chiuso il pipe. Nota che se il pipe viene chiuso prima che leggi i dati, la poll ritorna POLLHUP|POLLIN e puoi comunque leggere i dati inviati.
Ed_Bunker
28-07-2004, 12:38
Mi ci sto rincoglionendo... Il fatto di resettare la variabile errno per quale motivo viene fatto ?!? E poi la dovrei resettare ogni volta che rilevo un errore su una chiamata di sistema (Come quando avviene una interrupted system call) ?!
thks :)
ilsensine
28-07-2004, 12:41
No non mi hai capito:
void sighandler(...) {
int old_errno = errno
(...)
errno = old_errno
}
Comunque non è legato al tuo problema.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.