[英]Perl, how do I create a pipe to my exec'd child?
我试图使用管道(单向)将数据从我的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程序。 有什么建议?
我找到了解决方案。 有些人说exec是不可能的,它不会看到管道或文件描述符,但这是不正确的。
事实证明perl会自动关闭/无效所有fd> 2,除非你另有说明。
将以下标志添加到FD修复此问题(其中READ是此处的句柄,NOT STDIN):
my $flags = fcntl(READ, F_GETFD, 0);
fcntl(READ, F_SETFD, $flags & ~FD_CLOEXEC);
您的程序失败,因为exec
调用另一个程序并且永远不会返回 。 它不是为与其他进程的通信而设计的。
您可能基于IO::Pipe
文档编写了上述代码,其中说“ARGS传递给exec”。 但这并不意味着什么。 IO::Pipe
用于Perl脚本中的两个进程之间的通信,这两个进程由fork
创建。 它们意味着执行新进程,而不是在您自己的代码中调用exec
。
编辑:对于单向通信, 您只需open
一个管道 :
open my $prog, '|-', './child' or die "can't run program: $!";
print {$prog} "Hello, world!";
罗德里戈,我可以告诉你,当你执行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时管道不会崩溃。 这就是为什么你可以阅读你在应用程序中输入的内容。
如果您的要求是从另一个文件写入,请尝试使该文件处理一个独立的进程并始终存在,就像STDIN一样。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.