LINUX.ORG.RU

Запись в файловый дескриптор дочернего процесса

 ,


0

1

Разбираюсь с пайпами и файловыми дескрипторами. Возник вопрос в конкретной ситуации. Имеется php-скрипт read.php:

$step = 4 * 1024;

echo "php started\n";
while (!\feof(\STDIN)) {
    \fwrite(\STDOUT, 'read stdin part: '.\fread(\STDIN, $step)."\n");
}
echo "php finished\n";

Хочу запустить его из баш-скрипта + отдельной строкой передать ему что-то в standard input следующим образом:

php read.php &
procId=$(ps axw -o pid,command | grep 'read' | head -1 | sed -r 's|^\s*([0-9]+)[^0-9]+.*$|\1|g')
echo "procId: $procId"
echo -e "test1\ntest2" >> /proc/$procId/fd/0

При этом php-скрипт не получает ничего в STDIN, вывод следующий:

$ sh box.sh
procId: 2818
$ php started
read stdin part: 
php finished

При этом терминал висит, как будто ожидает ручного ввода данных. Делаю вывод, что вот эта строчка не срабатывает:

echo -e "test1\ntest2" >> /proc/$procId/fd/0

Судя по выводу в командную строку выше, у меня баш-скрипт завершает своё выполнение до начала исполнения php-скрипта («procId: 2818» вывелось раньше, чем «php started»). Как-то можно «дать понять» баш-скрипту подождать запуска php перед началом записи в его STDIN?



Последнее исправление: Novascriptum (всего исправлений: 3)

у меня баш-скрипт завершает своё выполнение до начала исполнения php-скрипта

Это не важно, до или после. У вас в целом дичь. fd/0 это не ввод процесса, это симлинк на терминал.

Или co-процессы или mkpipe. https://habr.com/ru/post/307562/

mky ★★★★★
()
Ответ на: комментарий от mky

mkpipe я уже тестил, с ним всё хорошо. Хочу именно в этом вопросе разобраться.

Вы уверены про 0? Потому что я вдохновлялся вот этим. Порождаю процесс php, извлекаю его id, далее пишу в его (а не башевский) /proc/$procId/fd/0.

mkfifo не хочу использовать, т.к. хочу добиться того, чтобы данная конструкция работала даже тогда, когда дисковое пространство исчерпано на 100%.

Novascriptum
() автор топика
Ответ на: комментарий от Novascriptum

По поводу fd/0. Мне лень печатать, здесь на англ. https://unix.stackexchange.com/questions/385771/writing-to-stdin-of-a-process . Если не понятно, могу перевести.

При этом, если в bash'е включён job control, то попытка чтения с управляющего терминала в фоновом процессе карается SIGTTIN. А без оного там вобще /dev/null из которого ничего прочитать нельзя:

If a command is followed by a & and job control is not active, the default standard input for the command is the empty file /dev/null.

И это всё можете проверить сами. Засуньте sleep(300) в ваш php скрпит (ну и sleep 300 в bash скрипт). Запустите и в другом терминале изучите состояние процессов и их /proc/PID/fd.

По вашей ссылке нет ничего подобного. Там вполне понятные хаки с gdb, иначе процесс работает с тем файлом, с которым работает.

В первой ссылке, что я дал (на хабр). Вместо mkfifo можно пробовать coproc. Но, для bash (sh) скриптов создание временных файлов (пайпов и не только) — это норма. На других языках можно хоть pipe()+dup(), чтобы у потомка stdin/stdout были связаны через fifo с родителем, хоть создавать псевдотерминал и в нём запускать потомка...

По поводу дичи. В bash'е пид последнего фонового процесса это $!, а не то что там понаписано. При этом конструкция:

ps axw -o pid,command | grep 'read' | head -1
отгрепает и сам процесс ″grep″ (из-за аргумента), и не факт, что в выводе он не окажется в первой строке. Плюс grep умеет считать совпадения, Поэтому как-то так:
ps axw -o pid,command | grep -m 1 '[r]ead'
Хотя настоящие джедаи сделали бы ″ps | sed″.

mky ★★★★★
()
Ответ на: комментарий от mky

Нашёл по вашей ссылке вот такое:

«If /proc/PID/fd/0 is a regular file, writing to it modifies the file. The data isn’t necessarily what the process will read next: it depends on the position attached to the file descriptor that the process is using to read the file. When a process opens /proc/PID/fd/0, it gets the same file as the other process, but the file positions are independent. If /proc/PID/fd/0 is a pipe, then writing to it appends the data to the pipe’s buffer. In that case, the process that’s reading from the pipe will read the data»

А я могу как-то «настоять» на том, чтобы /proc/PID/fd/0 открылся именно как пайп?

Novascriptum
() автор топика
Ответ на: комментарий от Novascriptum

Так, я, похоже, сам немного продвинулся в этом вопросе. Если в bash-скрипте я запускаю php-субпроцесс вот так:

echo "111" | php read.php &

(здесь, похоже, и происходит инициализация пайпа fd/0), то всё начинает работать более похоже на то, как мне надо:

$ sh box.sh
$ php started as 4983
read stdin part: 111
test1
test2

read stdin part: 
php finished

Но остаётся непонятно:

  1. Почему терминал остаётся «висеть», как будто ждёт какого-то ввода: https://www.screencast.com/t/NkALLTyGkpC
  2. Почему строка «read stdin part» повторяется дважды, и второй раз - значением прочитанной порции STDIN является пустая строка (которой не было в переданных данных).
Novascriptum
() автор топика
Последнее исправление: Novascriptum (всего исправлений: 2)
Ответ на: комментарий от Novascriptum

1. Ваш php-скрипт завершается после завершения bash-скрипта и что-то выводит после того, как баш отрисовал приглашение и ждёт ввода.

2. Видимо, так работает fread() в вашей версии php:

fread() and fwrite() will now return false if the operation failed. Previously an empty string or 0 was returned.

Я не хочу изучать php неизвестной версии, но цикл для меня выглядит странно.

В целом то что у вас написано, выглядит гонкой. echo может отработать (и завершиться) быстрее, чем ваш bash-скрипт откроет pipe. Плюс, подобное поведение неименованных пайпов, ИМХО, хак и может с какой-то версии ядра перестать работать.

mky ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.