Назад | Перейти на главную страницу

Почему запись в консоль, к которой подключен STDIN процесса, не отправляет ввод в само приложение?

Взято из этого ответ:

Терминал 1:

[ciupicri@hermes ~]$ cat
shows on the tty but bypasses cat

Терминал 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0

Я не совсем понимаю, зачем писать в файловый дескриптор, соответствующий stdin из cat процесс обходит сам процесс, но появляется на терминале. Отношения между terminal, file descriptor, device file, console сбивают меня с толку. Кроме того, мне иногда кажется, что в технической документации ими злоупотребляют. Может кто меня просветить?

Вы неправильно думаете о стандартных файловых дескрипторах.

STDIN указывает на устройство ввода. Если вы получили STDIN, принадлежащий другому процессу, вы можете использовать его для кражи входных данных этого процесса. Вы не должны писать в него, и если вы это сделаете, нет особых причин ожидать, что этот вывод будет прочитан целевым процессом; это произойдет только в том случае, если устройство вернет свой выход обратно на свой вход.

Точно так же STDOUT указывает на устройство вывода. Если вы получаете STDOUT, принадлежащий другому процессу, вы можете использовать его для генерации вывода, который будет идти в тот же файл или терминал, что и другой процесс. Вам не следует читать его, и если вы это сделаете, вы вряд ли увидите результат процесса.

В этом конкретном сценарии как уже указал Хокан, оба STDIN и STDOUT указывают на одно и то же устройство, поэтому пока вы не должен напишите в STDIN, если вы это сделаете, это будет иметь тот же эффект, что и запись в STDOUT, т.е. он отправит ваш вывод непосредственно на терминал. Если бы стандартные файловые дескрипторы были чем-то другим, например, если вы использовали перенаправление файлов или конвейерную пересылку, то запись в STDIN не была бы такой же, как запись в STDOUT.

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

В частности, если ваш процесс выполняется на виртуальном терминале (без каких-либо перенаправлений), тогда, когда вы пишете на стандартный вывод, вы ожидаете, что этот вывод будет отображаться на терминале, а не поступать обратно на ваш ввод. Тот факт, что вы неправильно используете дескриптор стандартного входного файла для терминала, а не стандартный дескриптор выходного файла, не изменит этого, и не имеет значения, какой процесс выполняет запись. (*)


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

Что делать, если стандартный ввод был перенаправлен из файла?

foo@bar:~/test$ cat hello
hi
foo@bar:~/test$ (cat; echo hello there > /dev/stdin; cat) < hello
hi
lo there

Содержимое файла перезаписывается; если процесс не завершил чтение файла сначала или если файл стал длиннее, чем был раньше, он прочитает часть или все содержимое, которое только что записал. Обратите внимание: поскольку мы уже прочитали «привет» и новую строку, при продолжении чтения мы пропускаем первые три символа «привет».

Что, если стандартный ввод - это труба?

foo@bar:~/test$ echo weird | (echo hello > /dev/stdin; cat)
weird
hello

По-видимому, конвейерное устройство Linux на самом деле циклически возвращает свой вывод обратно к входу, но я не уверен, гарантируется ли такое поведение POSIX или зависит от конкретной реализации. На вашем месте я бы не воспользовался этим трюком!


Итак, как правильно отправить процессу какие-либо данные?

Ну, один вариант описан в этот ответ.

Другой - правильно использовать трубу, например,

echo I'm sending some input | cat > myinput

Здесь echo процесс отправляет ввод в cat обработать. Это гарантированно будет работать правильно, потому что echo отправляет данные в файловый дескриптор, указывающий на один конец канала и cat получает данные от файлового дескриптора, указывающего на другой конец канала.

В обоих случаях принцип один и тот же. Целевой процесс выполняет чтение с определенного устройства, и нам нужно заставить это устройство генерировать желаемый результат. Как мы можем это сделать, зависит от устройства.


(*) Виртуальный терминал мог были спроектированы таким образом, чтобы предоставлять различные устройства для стандартного ввода и стандартного вывода оболочки, а устройство, подключенное к стандартному вводу, можно было запрограммировать на цикл любых записанных в него символов обратно во входной поток. Но это не так иметь работать таким образом, и, как это часто бывает, это не так, возможно, потому что основная цель дизайна первых виртуальных терминалов заключалась в том, чтобы вести себя так же, как и старомодные физические терминалы которые они заменяли.

Прежде всего, исходя из наблюдаемого поведения, я предполагаю, что речь идет о Linux. Под другой ОС я ожидаю, что все может сложиться иначе (в лучшую или худшую сторону).

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

Основная причина путаницы заключается в том, что вы ожидаете от файловой системы Linux / proc более высоких ожиданий, чем то, что она дает на самом деле.

Если вы внимательно посмотрите на то, что на самом деле находится в файловой системе / proc, вы увидите что-то вроде этого:

$ ls -l /proc/29058/fd/
total 0
lrwx------ 1 user1 user1 64 Jan  1 20:39 0 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan  1 20:39 1 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan  1 20:39 2 -> /dev/pts/2
$

Как видите, представление в / proc для всех "стандартных" fds (0 / stdin, 1 / stdout, 2 / stderr) этого процесса является символическими ссылками, и все они связаны с одним и тем же, а именно с терминалом. (псевдотерминальный раб).

Т.е. вы никогда не писали на стандартный ввод своего cat во-первых, вы писали в терминал, где он находится.
И это произошло не потому, что концепция stdin работает каким-то странным образом, а потому, что файловая система Linux / proc просто ссылается на то, что процесс считывает как свой stdin.
В подобном случае, когда ваш процесс читает с терминала в качестве своего стандартного ввода, ссылка указывает на узел устройства, который представляет как входные (при чтении с устройства), так и выходные (при записи на устройство) аспекты этого терминала. , поэтому этот узел относится только к стандартному вводу вашего процесса, когда вы читаете его, запись в него не имеет отношения к стандартному вводу вашего процесса.