Perl:当子进程/管道的文件句柄有别名时,关闭子进程失败



我已经阅读了所有关于open、close和IPC的perl文档,并阅读了许多相关的论坛线程,但仍然无法找出我做错了什么。首先,我创建了这个简单的分叉过程来证明我正确地进行了开放处理:

#!/usr/bin/perl
use IO::Handle;
my $pid = open(CHILD, "|-");
if ($pid == 0) {
# child:
while (<STDIN>) { # keeps reading until parent closes it's end of the pipe
chomp $_;
print "child: $_n";
}
}
else {
# parent:
for my $line (1..3) {
print CHILD "$linen";
}
my $success = close CHILD;
print "parent: success: $success, $!: $!, $?: $?n";
}

正如预期的那样,输出是:

child: 1
child: 2
child: 3
parent: success: 1, $!: Illegal seek, $?: 0

成功的价值是真实的;所以我忽略了"非法搜索",因为它不相关。然而,当我将STDOUT别名为管道(以避免需要显式打印为CHILD(,然后稍后将STDOUT恢复为其原始值时,如下所示,关闭的CHILD不再工作(即使STDOUT不再指向它(:

#!/usr/bin/perl
use IO::Handle;
my $pid = open(CHILD, "|-");
if ($pid == 0) {
# child:
while (<STDIN>) { # keeps reading until parent closes it's end of the pipe
chomp $_;
print "child: $_n";
}
}
else {
# parent:
open(ORIG_STDOUT, ">&STDOUT") or die "Unable to dup STDOUT: $!";
open(STDOUT, ">&=CHILD")      or die "Unable to alias CHILD: $!";
for my $line (1..3) {
print "$linen";
}
open(STDOUT, ">&=ORIG_STDOUT") or die "Unable to alias ORIG_STDOUT: $!";
my $success = close CHILD;
print "parent: success: $success, $!: $!, $?: $?n";
}

输出是这样的:

child: 1
child: 2
child: 3
parent: success: , $!: Illegal seek, $?: -1

success值不再为true,这意味着关闭管道时出错,或者CHILD返回了一个非零值。美元的-1值?指示等待系统调用由于某种原因而失败;非法查找";意思是,但事实上$!不为零,进一步表明不仅是CHILD进程返回了非零代码,导致关闭CHILD返回错误,而且关闭过程中存在某种错误。我在这里错过了什么?

至少有两个问题。首先,对open的调用实际上在两个程序中都失败了,或者更确切地说,如果在第一次调用之后附加check子句,就会失败。

#!/usr/bin/perl
use IO::Handle;
my $pid = open(CHILD, "|-") or die("A descriptive message");

程序在执行时立即崩溃。然而,由于开放模式参数中的管道意味着对fork的调用,这意味着应该有两个单独的进程,相当于总输出。可以理解的是,您认为调用成功了,因为在此点之后的代码正在执行。

然而,调用open失败的原因是多方面的,也是有趣的。您传入的是一个无效的文件描述符,但如果子进程能够存活足够长的时间来尝试复制文件描述符,就会发生一些非常有趣的事情。在对open的调用中使用">&="而不是简单的">&"将对内核的底层调用从dup更改为fdopen;第一个返回文件描述符,第二个返回文件句柄。(在C:intFILE*中(

TL;DR:您没有复制文件描述符,因为您说过不想复制文件描述符。而查找错误是试图读取1 + n字节超过从调用open收到的空句柄的0总字节的逻辑结果。

最新更新