PHP 不会打开 fifo 进行写入

Posted

技术标签:

【中文标题】PHP 不会打开 fifo 进行写入【英文标题】:PHP won't open fifo for writing 【发布时间】:2021-01-08 00:12:24 【问题描述】:

我正在围绕一个复杂的二进制文件编写一个小包装器(在 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 将阻塞直到至少有一个读写器。 open 不会被w+ 阻塞的原因是因为你有一个读者和一个作者(相同的过程)。除此之外,我真的不明白你剩下的问题是什么。 这当然是我上面所描述的——我的意思是在没有写入的情况下是否应该这样做。 【参考方案1】:

默认情况下,打开 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";

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

【讨论】:

似乎 O_NONBLOCK 仅在创建流后可用(意味着打开文件)。我通过运行 pcntl_fork(); 避免了这个问题;并将编写者与流程调用分开。【参考方案2】:

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

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

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

【讨论】:

以上是关于PHP 不会打开 fifo 进行写入的主要内容,如果未能解决你的问题,请参考以下文章

PHP中管道的非阻塞打开

我可以在 Linux 上打开命名管道以在 Python 中进行非阻塞写入吗?

程序打开同一个命名管道并用 C 多次写入

c语言,打开fifo文件时就没有反应,也不报错,就像是被暂停了一样,请问是怎么个情况?代码如下。

如果我在同一个程序中打开一个 FIFO 进行读写会发生啥?

一种分片更新ubi卷的方式(基于ubiupdatevol,拓展fifo支持)