2013-04-18 4 views
5

Sto provando a passare i dati dal mio script perl al mio programma c usando una pipe (unidirezionale). Ho bisogno di trovare un modo per farlo senza fare scherzi con i programmi figlio STDIN o STDOUT, quindi provo a creare un nuovo handle e passando il file fd.Perl, come posso creare una pipe per il mio figlio exec'd?

Creo 2 IO :: Maniglie e creo un tubo. Scrivo a un'estremità della pipe e provo a passare il descrittore di file dell'altra estremità della pipe al mio programma figlio che viene eseguito. Trasmetto il descrittore di file impostando una variabile ENV. Perché non funziona? (Non stampa 'ciao mondo'). Per quanto ne so, i descrittori di file e pipe sono ereditati dal bambino quando vengono eseguiti.

Perl script:

#!/opt/local/bin/perl 
use IO::Pipe; 
use IO::Handle; 

my $reader = IO::Handle->new(); 
my $writer = IO::Handle->new(); 
$reader->autoflush(1); 
$writer->autoflush(1); 
my $pipe = IO::Pipe->new($reader, $writer); 
print $writer "hello world"; 
my $fh = $reader->fileno; 
$ENV{'MY_FD'} = $fh; 
exec('./child') or print "error opening app\n"; 
# No more code after this since exec replaces the current process 

C Programma, app.c (compilato con gcc app.c -o child):

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 

int main(int argc, char ** argv) { 
    int fd = atoi(getenv("MY_FD")); 
    char buf[12]; 
    read(fd, buf, 11); 
    buf[11] = '\0'; 
    printf("fd: %d\n", fd); 
    printf("message: %s\n", buf); 
} 

uscita:

fd: 3 
message: 

Il messaggio non è mai passato attraverso il tubo al programma C. Eventuali suggerimenti?

risposta

3

i descrittori di file pipe sono impostati FD_CLOEXEC, e così chiuse, exec().

Perl's $^F controlla questo comportamento. Provare qualcosa di simile, prima di si chiama IO :: palina> nuovo:

$^F = 10; # Assumes we don't already have a zillion FDs open 

In alternativa, è possibile con Fcntl chiara la bandiera FD_CLOEXEC da soli dopo aver creato il tubo.

0

Il programma non funziona perché execcalls another program and never returns. Non è progettato per la comunicazione con un altro processo.

Probabilmente avete scritto il codice sopra basato sul IO::Pipe documentation, che dice "ARGS sono passati ad exec". Questo non è ciò che significa, però. IO::Pipe è per la comunicazione tra due processi all'interno dello script Perl, che sono creati da fork. Significano l'esecuzione del nuovo processo, piuttosto che una chiamata a exec nel proprio codice.

Edit: comunicazione per un-direzionale, all you need is open with a pipe:

open my $prog, '|-', './child' or die "can't run program: $!"; 

print {$prog} "Hello, world!"; 
+1

Non è il mio caso 1-direzionale? Ho solo bisogno di inviare dati al processo exec'd, non i dati allo script perl, questo significa che non ho bisogno di exec per restituire qualcosa. –

+1

@RodrigoSalazar, oops Ho pensato che stavi tentando una comunicazione bidirezionale perché il tuo script Perl stava scrivendo sia su una pipe che leggendo da una pipe. Tutto ciò che serve per una comunicazione direzionale è 'open' con una pipe. –

+0

Se uso open con pipe non significa che i dati che invio a questo handle appariranno come STDIN al processo exec'd? (Non sono sicuro) –

0

Rodrigo, posso dirvi che il descrittore di file non è più valido quando si Exec nel c app.
Si prega di essere consapevoli che dico solo che è non valido, ma esiste ancora nelle variabili di ambiente. L'FD = 3 continuerà a esistere fino alla fine dell'intero processo.
È possibile controllare il file fd mediante fcntl. Il codice è l'elenco qui sotto

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <fcntl.h> 

int main(int argc, char ** argv) { 
    int fd = atoi(getenv("MY_FD")); 
    char buf[12]; 
    read(fd, buf, 11); 
    buf[11] = '\0'; 
    printf("fd: %d, if fd still valid: %d\n", fd, fcntl(fd, F_GETFD)); 
    printf("strlen %d\n", (int)strlen(buf)); 
    printf("message: %s\n", buf); 
} 

Si può vedere che MY_FD = 3 sarà sempre nella ENV come il processo non distruggere se stessa, in modo da poter ottenere fd come 3. Ma, questo descrittore di file è stato valido. quindi il risultato di fcntl (fd, F_GETFD) sarà -1, e la lunghezza che leggerete da fd sarà 0.
Ecco perché non vedrete mai la frase "ciao mondo".

Un'altra cosa, @ dan1111 è giusta, ma non è necessario aprire una nuova pipa, come già fatto.
Tutto quello che dovete è solo impostato MY_FD = 0, come

$ENV{'MY_FD'} = 0; 

Lo STDIN/OUT è un altro processo indipendente che esiste sempre, in modo che il tubo non sarà ripartito quando il perl applicazione exec in c app. Ecco perché puoi leggere da ciò che inserisci nell'app.
Se la tua richiesta è scritta da un altro file, prova a fare in modo che quel file gestisca un processo indipendente ed esista sempre, proprio come STDIN.

0

Ho trovato la soluzione. Alcune persone hanno detto che non era possibile con exec, che non avrebbe visto pipe o descrittori di file, ma non era corretto.

Si scopre che perl chiude/annulla automaticamente tutti i fd> 2 a meno che non si dica diversamente.

aggiungendo le seguenti bandiere al FD risolve questo problema (dove LEGGI è il manico qui, NON STDIN):

my $flags = fcntl(READ, F_GETFD, 0); 
fcntl(READ, F_SETFD, $flags & ~FD_CLOEXEC);