2013-04-18 11 views
5

Próbuję przekazać dane z mojego skryptu Perl do mojego programu c za pomocą potoku (jednokierunkowe). Potrzebuję znaleźć sposób, aby to zrobić bez zakłócania programów potomnych STDIN lub STDOUT, więc próbuję utworzyć nowy uchwyt i przekazać fd.Perl, w jaki sposób mogę utworzyć potok dla mojego egzekwowanego dziecka?

Tworzę 2 IO :: Uchwyty i utworzę rurę. Piszę na jednym końcu potoku i próbuję przekazać deskryptor pliku drugiego końca potoku do mojego programu potomnego, który jest wykonywany. Przekażę deskryptor pliku, ustawiając zmienną ENV. Dlaczego to nie działa? (Nie drukuje "Witaj świecie"). O ile mi wiadomo, deskryptory plików i potoki są dziedziczone przez dziecko po uruchomieniu.

Perl skrypt:

#!/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 Program app.c (skompilowany z 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); 
} 

wyjściowa:

fd: 3 
message: 

Komunikat nigdy nie jest przekazywany przez rurę do programu C. Jakieś sugestie?

Odpowiedz

3

Twoje deskryptory plików rury są ustawione FD_CLOEXEC, a więc są zamknięte na exec().

Kontroluje to zachowanie przez Perla za $^F. Spróbuj czegoś takiego, przed zadzwonić IO :: w rurę> nowy:

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

Alternatywnie, można z Fcntl jasnego flaga FD_CLOEXEC samemu po utworzeniu rury.

0

Twój program ulega awarii, ponieważ execcalls another program and never returns. Nie jest przeznaczony do komunikacji z innym procesem.

Prawdopodobnie napisałeś powyższy kod na podstawie IO::Pipe documentation, który mówi "ARGS są przekazywane do exec". Ale nie o to tu chodzi. IO::Pipe służy do komunikacji między dwoma procesami w twoim skrypcie Perla, które są tworzone przez fork. Chodzi o wykonanie nowego procesu, a nie wywołanie do exec w swoim własnym kodzie.

Edit: na jednokierunkowy komunikacji, all you need is open with a pipe:

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

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

Czy mój przypadek nie jest jednokierunkowy? Wystarczy wysłać dane do procesu exec'd, a nie dane do skryptu perla, to znaczy, że nie potrzebuję exec, aby zwrócić cokolwiek. –

+1

@RodrigoSalazar, oops Myślałem, że próbujesz komunikować się dwukierunkowo, ponieważ twój skrypt Perla pisząc do fajki i czytając z potoku. Wszystko czego potrzebujesz do komunikacji jednokierunkowej to 'open' z potokiem. –

+0

Jeśli używam otwartego z potokiem, nie oznacza to, że dane wysyłane do tego uchwytu będą wyświetlane jako STDIN do procesu exec'd? (Nie jestem pewien) –

0

Rodrigo, mogę powiedzieć, że deskryptor jest nieważne, jeśli exec do aplikacji c.
Należy pamiętać, że właśnie mówię, że jest NIEPRAWIDŁOWY, ale nadal istnieje w zmiennych środowiskowych. FD = 3 będzie istnieć aż do zakończenia całego procesu.
Możesz sprawdzić fd przez fcntl. Kod jest wystawianie poniżej

#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); 
} 

Widać, że MY_FD = 3 będzie zawsze w ENV jako proces nie niszczy się, dzięki czemu można uzyskać fd jako 3. Ale to deskryptor pliku jest nieprawidłowy. więc wynik fcntl (fd, F_GETFD) będzie wynosił -1, a długość odczytu z fd będzie równa 0.
Dlatego nigdy nie zobaczysz zdania "cześć świat".

Jeszcze jedno: @ dan1111 ma rację, ale nie musisz otwierać nowej rury, tak jak to już zrobiłeś.
Wszystko trzeba się po prostu ustawić MY_FD = 0, jak

$ENV{'MY_FD'} = 0; 

stdin/OUT jest inny niezależny proces, który zawsze istnieje, więc rura nie będzie podziale kiedy twój perl exec aplikacji na C aplikacji. Dlatego możesz czytać z tego, co wpisujesz w aplikacji.
Jeśli twoje wymaganie jest pisane z innego pliku hanle, spróbuj uczynić ten plik niezależnym procesem i zawsze istnieje, tak jak STDIN.

0

Znalazłem rozwiązanie. Niektórzy twierdzili, że nie było możliwe z exec, że nie zobaczył on potoków lub deskryptorów plików, ale to nie było poprawne.

Okazuje się, że perl zamyka/unieważnia wszystkie fd> 2 automatycznie, chyba że powiesz inaczej.

Dodawanie następujące flagi do FD rozwiązuje ten problem (gdzie ZOBACZ jest uchwytem tutaj, NIE stdin):

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