IPC::Open3 在 Apache 下运行失败

Posted

技术标签:

【中文标题】IPC::Open3 在 Apache 下运行失败【英文标题】:IPC::Open3 Fails Running Under Apache 【发布时间】:2011-01-07 00:32:48 【问题描述】:

我有一个模块使用 IPC::Open3(或 IPC::Open2,都存在这个问题)来调用外部二进制文件(在这种情况下为 bogofilter)并通过子输入文件句柄为其提供一些输入,然后读取子输出句柄的结果。该代码在大多数环境中运行时都可以正常工作。但是,此模块的主要用途是在 Apache 2.2.6 下运行的 Web 服务中。在那种环境下,我得到了错误:

无法 fdopen STDOUT:参数无效

这只发生在代码在 Apache 下运行时。以前,该代码构建了一个极其复杂的命令,其中包括一个用于输入的 here-document,并使用反引号运行它。这行得通,但速度很慢,而且容易以独特而令人费解的方式中断。我不想必须恢复到旧版本,但我无法破解。

【问题讨论】:

【参考方案1】:

会不会是因为 mod_perl 2 关闭了 STDOUT?我刚刚发现并发布了它:

http://marc.info/?l=apache-modperl&m=126296015910250&w=2

我认为这是一个令人讨厌的错误,但到目前为止似乎没有人关心它。如果您的问题与您的问题相关并且您希望它引起注意,请在 mod_perl 列表上发布后续信息。

乔恩

【讨论】:

【参考方案2】:

Bogofilter 针对垃圾邮件/非垃圾邮件返回不同的退出代码。

您可以通过将 stdout 重定向到 /dev/null 来“修复”此问题

system("bogofilter < $input > /dev/null") >> 8;

将返回 0 表示垃圾邮件,1 表示非垃圾邮件,2 表示未知(>> 8 是因为 perl 有助于更正退出代码,这可以修复损坏)。

注意:缺少环境也可能会阻止 bogofilter 找到它的词表,所以也要明确地传递它:

system("bogofilter -d /path/to/.bogofilter/ < $input > /dev/null") >> 8;

(其中 /path/to/.bogofilter 包含 wordlist.db)

您无法检索 bogofilter 以这种方式给出的实际评级,但它确实为您带来了一些东西。

【讨论】:

我认为这不是直接的问题——如果需要专门使用 Open3。【参考方案3】:

如果您的代码只打算在 Linux/Unix 系统上运行,那么很容易编写一个不会失败的 open3 替换,因为 STDOUT 不是真正的文件句柄:

sub my_open3 
    # untested!
    pipe my($inr), my($inw) or die;
    pipe my($outr), my($outw) or die;
    pipe my($errr), my($errw) or die;
    my $pid = fork;
    unless ($pid) 
        defined $pid or die;
        POSIX::dup2($inr, 0);
        POSIX::dup2($outw, 1);
        POSIX::dup2($errw, 2);
        exec @_;
        POSIX::_exit(1);
    
    return ($inw, $outr, $errr);


my ($in, $out, $err) = my_open3('ls /etc/');

【讨论】:

【参考方案4】:

Caveat Emptor:我不是 perl 向导。

正如@JonathanSwartz 建议的那样,我认为问题在于 apache2 mod_perl 关闭了 STDIN 和 STDOUT。这应该与 IPC::Open3 正在做的事情无关,但它有一个错误,described here。

总而言之(这是我不太清楚的部分),open3 尝试将子进程 STDIN/OUT/ERR 与您的进程匹配,或者如果需要,则复制它。由于 open('>&=X') 的一些未记录的工作方式,它通常工作正常,除非在 STDIN/OUT/ERR 关闭的情况下。

Another link 深入细节。

一种解决方案是修复 IPC::Open3,如这两个链接中所述。另一个对我有用的是在你的 mod_perl 代码中临时打开 STDIN/OUT,然后再关闭它:

my ($save_stdin,$save_stdout);
open $save_stdin, '>&STDIN';
open $save_stdout, '>&STDOUT';
open STDIN, '>&=0';
open STDOUT, '>&=1';

#make your normal IPC::Open3::open3 call here

close(STDIN);
close(STDOUT);
open STDIN, '>&', $save_stdin;
open STDOUT, '>&', $save_stdout;

另外,我注意到网上有很多关于 IPC::Run3 遇到同样问题的投诉,所以如果有人遇到同样的问题,我怀疑同样的解决方案会起作用。

【讨论】:

以上是关于IPC::Open3 在 Apache 下运行失败的主要内容,如果未能解决你的问题,请参考以下文章

我们在 C 或 C++ 中是不是有类似于 IPC::Open3 的 perl

IPC::Open3 转换字符编码

IPC::Open3 的更多麻烦 [重复]

perl IPC:Open3 最小通过 perlcritic?

IPC::Open3 并确定孩子是不是正在等待输入

如何检查使用 IPC::open3 执行的命令是不是挂起?