#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 на случай, если «двоичный файл» не выполняет / не очищает канал).