繁体   English   中英

Perl,如何为我的exec'd孩子创建一个管道?

[英]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程序。 有什么建议?

您的管道文件描述符设置为FD_CLOEXEC,因此在exec()时关闭。

Perl的$^F控制此行为。 调用IO :: Pipe-> new 之前尝试这样的事情:

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

或者,您可以在创建管道后使用Fcntl自行清除FD_CLOEXEC标志。

我找到了解决方案。 有些人说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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM