Newsgroup: it.comp.lang.perl
Titolo: Implementazione pipe

Ho visto che in Perl e' molto semplice usare le pipe con altri
programmi.
Sto cercando di implementare lo sto stesso procedimento in C ma trovo
molte difficolta', a quanto pare e' l'unico (almeno che io sappia)
linguaggio dove si puo' sfruttare la pipe in read/write in modo
continuato.
Infatti se ad es. volessimo interagire con un programma con dati di
input ed output comincia ad essere molto difficile. Ammettiamo di avere
il un programma che fa la somma di due numeri e lo stampa in output,
dopo dare un'altra volta due numeri e da' in output la moltiplicazione.
In perl si fa cosi':
my $pipe;
open $pipe, "|./programma";
print $pipe "2\n";
print $pipe "3\n";
print <$pipe>;
print $pipe "4\n";
print $pipe "5\n";
print <$pipe>;
close $pipe;
Uscira' stampato prima 5 e poi 20. Ora questo in C e' una cavolata farlo
fino al primo output, poiche' do i primi due dati, dopodiche' chiudo
l'stdin del figlio e lui mi da l'output, poiche' finquando non chiudo
non da' output.
Il problema e' che dopo chiuso il fd, non puo' prendere gli altri dati
di input...
mi sapete dire come sono riusciti ad implementare una cosa simile?
Dovrei girare nel codice sorgente del perl ma se mi sapete rispondere mi
evitate molti grattacapi :P
Grazie.
-- 
www.italianpug.org
 * Italian Python User Group Founder

 non da' output.
Uh?
Per questo tipo di compito il linguaggio C, da solo, non ha strumenti
sufficienti. In ambiente POSIX, per implementare quello che dici, si usa
fork+exec e pipe o socketpair. 
Max


E infatti faccio cosi', pero' rimane il problema...


 
Uhm, vabbé, tanto questa cosa è OT in questa sede. Ti suggerirei di 
riproporla in it.comp.os.linux.developement, allegando anche del codice 
compilabile.
Max


Ho provato IPC::Open2, pero' cio' che ho detto non si puo' fare
comunque.
Ecco perche':
use IPC::Open2;
local (*Rd, *Wr);
my $pid = open2(\*Rd, \*Wr, "./a.py");
print Wr "2\n3\n";
print <Rd>;
print Wr "3\n4\n";
print <Rd>;
close Wr;
close Rd;
Siccome il programma vuole comunque chiuso l'stdin, vuole prima tutti i
dati di input altrimenti non da output. Dunque si ferma al primo print.
Se tolgo il primo print e gli do' tutti i dati in input prima, lui mi
restituisce l'output.
Ma quindi qui c'e' qualcosa che non va nell'aggiornamento del suo
stdout? Eppure un flush non cambia niente, ho provato anche con diverse
opzioni in fcntl.


 opzioni in fcntl.
Da quello che ho capito non e' di open2 che hai bisogno, cioe' non devi
ricevere l' output del processo eseguito con popen() nel processo padre,
piuttosto il tuo problema e' di bufferizzazione come puoi leggere in
perldoc perlipc poco sotto la presentazione di open2.
"
       The problem with this is that Unix buffering is really going to ruin
       your day.  Even though your "Writer" filehandle is auto-flushed, and
       the process on the other end will get your data in a timely manner,
you
       can't usually do anything to force it to give it back to you in a
simi-
       larly quick fashion.
"
e ancora poco piu' in basso c' e' una soluzione che esclude open2 e usa
solo
pipe() con questo eloquente accorgimento:
use IO::Handle;     # thousands of lines just for autoflush :-(
appunto! perche' danno un simile consiglio?? Per usare sempre e solo
print??
Io su pipe, socket ecc . uso solo syswrite del perl che utilizza la write()
del C, chiamata di sistema NON BUFFERIZZATA!
se nel primo esempio che hai postato sostituisci le print con syswrite
vedrai che l' output arriva subito A CONDIZIONE CHE IL COMANDO DALL' ALTRA
PARTE DELLA PIPE LEGGA IN MODO NON BUFFERIZZATO (come cat -u ad esempio
o se e' uno script perl deve usare sysread e non <STDIN>). Se non e' cosi'
non ci puoi fare niente: devi riscriverti anche l' altro processo, a meno
che non supporto l' opzione -u per IO non bufferizzato.
ciao
Roberto.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 che non supporto l' opzione -u per IO non bufferizzato.
Ho tralasciato una cosa importante pero': usando syswride devi sempre
controllare il valore di ritorno per sapere quanti caratteri sono stati
effettivamente scritti, cosi' che la cosa non e' cosi' semplice come
l' ho descritta. E' necessario scrivere una funzione appropriata.
Comunque il tuo problema e' conseguente la bufferizzazione dell' IO.



Io l'ho fatto anche in C usando la write e non funziona, c'e' bisogno di
qualche flag? Non uso popen cosi' ho il pieno controllo dell'altro
processo, pero' devi anche capire che con exec il processo viene
sostituito con il nuovo e non e' piu' in mano mia.


 sostituito con il nuovo e non e' piu' in mano mia.
Anche io sia in C che in PERL se sono autore di entrambi i processi tutto
funziona, ma se l' altro processo legge e scrive in modo bufferizzato non
ci puoi fare niente (escludendo wrapper alle libc e cose complicatissime
e sporche)
ciao
Roberto



Capito, e come si puo' fare per rendere la scrittura e la lettura non
bufferizzata?
I due programmi li ho fatti io, i due processi li avvio io manualmente
utilizzando pipe, fork e dup2. Se serve il codice lo posto...


 utilizzando pipe, fork e dup2. Se serve il codice lo posto...
Io quando scrivo su in pipe uso syswrite, e per leggere sysread.
Tuttavia si puo' agire sulla var $|
con $| = 1 tutte le print o write sono non bufferizzate, ma a dispetto
del gran usa che se ne fa io ho delle perplessita'.
Per esempio facendo solo questo rendi le scritture verso STDOUT non
bufferizzate e questo puo' essere la soluzione da adottare nel processo
figlio, quello dall' altra parte del file anche per non stravolgere tutto
il codice, ma nel processo principale DEVI PRIMA SELEZIONARE IL FLUSSO
DI DEFAULT e poi renderlo non bufferizzato con $| = 1;
Per selezionare il flusso di default si usa select MA NELLE PRIMA FORMA
CHE APPARE IN perldoc -f select!!!
Attenzione che select di solito e' una syscall molto utile per multiplexare
IO, ma in perl anziche dare due nomi a due funzioni con scopi completamente
diversi hanno preferito riconoscerle dagli args passati (per me e' una
porcata!)
Il problema e' che poi tutte le print SENZA descrittore di file vanno sul
nuovo flusso di default e non a STDOUT. Un vero schifo!
Io preferisco syswrite nel primo processo. Pero' in perdoc perlipc vi e'
l' esempio che cito ancora che consiste in:
use IO::Handle;     # thousands of lines just for autoflush :-(
pipe(PARENT_RDR, CHILD_WTR);                # XXX: failure?
pipe(CHILD_RDR,  PARENT_WTR);               # XXX: failure?
CHILD_WTR->autoflush(1);
PARENT_WTR->autoflush(1);
Come vedi anche a chi ha scritto perlipc non piace importare il modulo
solo per avere PARENT_WTR->autoflush(1); ma forse e' la soluzione piu'
pulita perche come gia' detto suswrite POTREBBE NON SCRIVERE TUTTI I BYTES!
La soluzione e' in perldoc perlipc.
Ciao
Roberto.


Roberto C wrote:

Ci provero' grazie mille :)
Ora in C pero' questo come si traduce? Se puoi riesci a farmi un esempio
pratico? Scusa ancora il disturbo e grazie della pazienza.
