簡體   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