PHP не будет открывать fifo для записи

#php #pipe #posix #qfile

#php #канал #posix #qfile

Вопрос:

Я пишу небольшую оболочку (на PHP 7.0.33) вокруг сложного двоичного файла, который принимает входные данные из именованного файла. Поскольку это будет обработка секретов, я не хочу передавать данные в файловую систему, следовательно, используя FIFO, а не обычный файл. Двоичный файл с радостью считывает свои данные из FIFO (проверено с использованием 2 сеансов оболочки — в одном я создал 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 openedn";
        while (!feof($fh)) {
                $in.=fgets($fh);
        }
} else {
        print "failed to open filen";
}

file_put_contents("output", $in);
 

но когда я пишу в FIFO из PHP-кода….

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

Комментарии:

1. FIFO будет блокироваться до тех пор, пока не появится хотя бы один reader и writer. Причина, по которой open не блокируется, w заключается в том, что у вас есть reader и writer (один и тот же процесс). Помимо этого я не совсем понимаю, о чем остальная часть вашего вопроса.

2. Это, безусловно, то, что я описал выше — моя точка зрения заключается в том, следует ли этого ожидать при отсутствии записи.

Ответ №1:

По умолчанию открытие FIFO будет блокироваться до тех пор, пока не будет хотя бы одного устройства чтения и записи. Причина этого заключается в том, что ядру негде хранить данные канала, если нет процесса, который мог бы их использовать. справочная страница для fifo:

… специальный файл FIFO не имеет содержимого в файловой системе, запись файловой системы служит просто точкой отсчета, чтобы процессы могли получить доступ к каналу, используя имя в файловой системе.

Ядро поддерживает ровно один объект канала для каждого специального файла FIFO, который открывается хотя бы одним процессом. FIFO должен быть открыт на обоих концах (чтение и запись), прежде чем данные могут быть переданы. Обычно открытие блоков FIFO происходит до тех пор, пока не будет открыт и другой конец.

Однако вы можете обойти это поведение. Один из способов похож на то, что вы сделали, — самостоятельно открыв конец чтения и записи. Другой способ — установить O_NONBLOCK флаг при открытии файла (впоследствии вы можете установить его обратно на блокировку). AFAIK, вы не можете этого сделать с помощью fopen. Пример с dio библиотекой:

 <?php
echo "Openingn";
$writer = dio_open("myfifo", O_CREAT | O_WRONLY | O_NONBLOCK) or die("Could not create FIFOn");
echo "Open. Writingn";
dio_write($writer, "DATA");
echo "Donen";
 

Предостережение при этом заключается в том, что если нет средства чтения, описанный выше процесс запишет данные, затем немедленно завершит работу, а затем данные будут потеряны навсегда.

Комментарии:

1. Кажется, что O_NONBLOCK доступен только после создания потока (подразумевающего открытие файла). Я избежал проблемы, запустив pcntl_fork(); и отделив средство записи от вызова процесса.

Ответ №2:

В интересах всех, кто находит этот вопрос в Google и ищет более подробное решение, чем комментарий к ответу semisecure:

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

(но вы также можете добавить SIGALRM в writer на случай, если «двоичный файл» не выполняет / не очищает канал).