PHP 不会打开 fifo 进行写入

问题描述

我正在围绕一个复杂的二进制文件编写一个简单的包装器(在 PHP 7.0.33 中),它从一个命名文件获取输入。由于这将是处理机密,我不想将数据提交到文件系统,因此使用 FIFO 而不是传统文件。二进制文件会很高兴地从 FIFO 中读取数据(使用 2 个 shell 会话进行测试 - 在一个会话中我创建了 fifo 并启动了二进制文件,在第二个中我将一个文件放入了 fifo)。

但是在 PHP调用 fopen() 会阻塞,无论我指定 w、a 还是 c

  if (posix_mkfifo("myfifo",0600)) {
     $writer=fopen("myfifo",'w'); // this blocks
     `binary myfifo`;
     fputs($writer,$mydata);
  }

虽然我希望在没有读取数据的情况下写入会阻塞,但我没想到 fopen() 会阻塞。

使用“w+”似乎可以正常工作(继续执行,并启动二进制文件),但是二进制文件失败了

 qiodevice::read (QFile,"filename"): device not open

为了进一步调查,我为二进制文件编写了一个简单的替代品。当我将文件放入 FIFO 时,这再次起作用:

$in='';
$fh=fopen($argv[1],'r');
if (is_resource($fh)) {
        print "File opened\n";
        while (!feof($fh)) {
                $in.=fgets($fh);
        }
} else {
        print "Failed to open file\n";
}

file_put_contents("output",$in);

但是当我从 PHP 代码写入 FIFO 时....

fopen(import): Failed to open stream: No such file or directory in ...

解决方法

默认情况下,打开 FIFO 将阻塞,直到至少有一个读取器和写入器。这样做的基本原理是,如果没有进程可以使用它,内核就没有地方存储管道数据。 fifo 的手册页:

... FIFO 特殊文件在文件系统上没有内容,文件系统条目仅用作参考点,以便进程可以使用文件系统中的名称访问管道。

内核为至少一个进程打开的每个 FIFO 特殊文件维护一个管道对象。 FIFO 必须在两端(读和写)都打开,然后才能传递数据。通常,打开 FIFO 会阻塞,直到另一端也打开。

不过,您可以绕过此行为。一种方法就像您已经完成的那样 - 通过自己打开读写端。另一种是在打开文件时设置 O_NONBLOCK 标志(您可以在之后将其设置回阻止)。 AFAIK 你不能用 fopen 做到这一点。 dio 库示例:

<?php
echo "Opening\n";
$writer = dio_open("myfifo",O_CREAT | O_WRONLY | O_NONBLOCK) or die("Could not create FIFO\n");
echo "Open. Writing\n";
dio_write($writer,"DATA");
echo "Done\n";

这样做的警告是,如果没有读取器,上面的过程将写入数据,然后立即退出,然后数据将永远丢失。

,

为了在 Google 中发现此问题并寻求比对 semisecure 的答案发表评论更详细的解决方案的任何人的利益:

if (pcntl_fork()) {
    `binary myfifo`;
} else {
    $fh=fopen('myfifo','w');
    fputs($fh,$data);
    fclose($fh);
}

(但您可能还想在编写器上添加一个 SIGALRM,以防“二进制”不执行/刷新管道)。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...