我正在尝试使用管道(单向)将数据从我的perl脚本传递到我的c程序。 我需要找到一种方法来做到这一点,而不会弄乱子程序STDIN或STDOUT,所以我尝试创建一个新句柄并传递fd。
我创建了2个IO :: Handles并创建了一个管道。我写到管道的一端,并尝试将管道另一端的文件描述符传递给正在执行的子程序。我通过设置ENV变量来传递文件描述符。为什么这不起作用? (它没有打印出“你好世界”)。据我所知,文件描述符和管道在执行时由子进行继承。
Perl脚本:
#!/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程序,app.c(编译为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);
}
输出:
fd: 3
message:
消息永远不会通过管道传递给C程序。有什么建议吗?
答案 0 :(得分:2)
您的管道文件描述符设置为FD_CLOEXEC,因此在exec()时关闭。
Perl的$^F
控制此行为。尝试这样的事情,之前你调用IO :: Pipe-&gt; new:
$^F = 10; # Assumes we don't already have a zillion FDs open
或者,您可以在创建管道后自己Fcntl清除FD_CLOEXEC标志。
答案 1 :(得分:1)
我找到了解决方案。有些人说exec是不可能的,它不会看到管道或文件描述符,但这不正确。
原来perl关闭/无效所有fd&gt;除非你另有说明,否则自动2。
将以下标志添加到FD修复此问题(其中READ是此处的句柄,NOT STDIN):
my $flags = fcntl(READ, F_GETFD, 0);
fcntl(READ, F_SETFD, $flags & ~FD_CLOEXEC);
答案 2 :(得分:0)
您的计划失败,因为exec
calls another program and never returns。它不是为与其他进程的通信而设计的。
您可能基于IO::Pipe
documentation编写了上述代码,其中说“ARGS传递给exec”。但这并不意味着什么。 IO::Pipe
用于在Perl脚本中的两个进程之间进行通信,这两个进程由fork
创建。它们意味着执行新流程,而不是在您自己的代码中调用exec
。
修改,all you need is open
with a pipe:
open my $prog, '|-', './child' or die "can't run program: $!";
print {$prog} "Hello, world!";
答案 3 :(得分:0)
罗德里戈,我可以告诉你,当你执行c应用程序时,你的文件描述符不再有效 请注意,我只是说它是INVALID,但它仍然存在于环境变量中。 FD = 3将继续存在,直到整个过程结束 你可以通过fcntl查看fd。代码列在下面
#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);
}
你可以看到MY_FD = 3总是在ENV中,因为进程不会破坏它自己,所以你可以得到fd为3.但是,这个文件描述符无效。所以fcntl(fd,F_GETFD)的结果为-1,从fd读取的长度为0。
这就是为什么你永远不会看到“你好世界”的句子。
还有一件事,@ dan1111是对的,但您不需要打开新管道,因为您已经这样做了。
您只需设置MY_FD = 0,就像
$ENV{'MY_FD'} = 0;
STDIN / OUT是另一个始终存在的独立进程,因此当你的perl应用程序执行到c app时管道不会崩溃。这就是为什么你可以阅读你在app中输入的内容。
如果您的要求是从另一个文件hanle写入,请尝试使该文件处理一个独立的进程并始终存在,就像STDIN一样。