Почему запись в консоль, к которой присоединен 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 смущают меня. Кроме того, я чувствую, что иногда они используются в техническом отношении. Может ли кто-нибудь просветить меня?

2 ответа

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

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) этого процесса - символические ссылки, все они связаны с одной и той же вещью, а именно с терминалом (псевдотерминал раб).

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

Другие вопросы по тегам