web-dev-qa-db-ja.com

プロセスの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に対応するファイル記述子への書き込みがプロセス自体をバイパスするのに、ターミナルに表示される理由がよくわかりません。 terminalfile descriptordevice fileconsoleの関係は私を混乱させます。また、テクニカルライティングでこれらが悪用されることもあると思います。誰かが私を啓発できますか?

2
Holmes.Sherlock

あなたは標準のファイル記述子について間違った方法で考えています。

STDINは入力デバイスを指します。別のプロセスに属するSTDINを取得した場合、それを使用してそのプロセスの入力を盗むことができます。書き込むことは想定されていません。書き込む場合、その出力が最終的にターゲットプロセスによって読み取られることを期待する特別な理由はありません。これは、デバイスが出力を入力にループバックした場合にのみ発生します。

同様に、STDOUTは出力デバイスを指します。別のプロセスに属するSTDOUTを取得した場合、それを使用して、他のプロセスが使用しているのと同じファイルまたは端末に送られる出力を生成できます。そこから読み取るべきではありません。読み取ると、プロセスの出力が表示されなくなる可能性があります。

この特定のシナリオでは、 Håkanがすでに指摘しているように 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

ファイルの内容は上書きされます。プロセスが最初にファイルの読み取りを完了しなかった場合、またはファイルが以前よりも長くなった場合は、書き込んだばかりのコンテンツの一部またはすべてを読み取ります。 「hi」と改行をすでに読んでいるので、読み続けるときは「hellothere」の最初の3文字をスキップすることに注意してください。

標準入力がパイプの場合はどうなりますか?

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

どうやらLinuxパイプデバイスは実際には出力を入力にループバックしますが、この動作がPOSIXによって保証されているのか、特定の実装に依存しているのかはわかりません。私があなただったら、このトリックを利用するのは避けたいです!


では、プロセスに入力を送信する適切な方法は何ですか?

さて、1つのオプションは この答え で説明されています。

もう1つは、パイプを正しく使用することです。

echo I'm sending some input | cat > myinput

ここで、echoプロセスはcatプロセスに入力を送信しています。 echoはパイプの一方の端を指すファイル記述子にデータを送信し、catはパイプのもう一方の端を指すファイル記述子からデータを受信するため、これは正しく機能することが保証されています。パイプ。

どちらの場合も、原則は同じです。ターゲットプロセスは特定のデバイスからの読み取りであり、そのデバイスに必要な出力を生成させる必要があります。それをどのように行うかは、デバイスが何であるかによって異なります。


(*)仮想端末couldは、シェルの標準入力と標準出力に異なるデバイスを提供するように設計されており、標準入力に接続されたデバイスは次のようにプログラムできます。書き込まれた文字をループして入力ストリームに戻します。しかし、そのように機能することはありません、そしてたまたま、最初の仮想端末の主要な設計目標が同じように動作することであったため、そうではありません。 昔ながらの物理端末 交換していた方法。

3
Harry Johnston

まず、観察された動作に基づいて、これはLinuxに関するものだと思います。別のOSでは、状況が異なる場合があると思います(良くも悪くも)。

これから最も実用的に役立つポイントは、これがプロセスと対話する適切な方法ではないということかもしれません。

ここでの混乱の主な原因と思われるのは、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
$

ご覧のとおり、このプロセスのすべての「標準」fds(0/stdin、1/stdout、2/stderr)の/ procでの表現はシンボリックリンクであり、すべてがまったく同じもの、つまり端末にリンクされています。 (疑似端末スレーブ)。

つまり、そもそもcatプロセスのstdinに書き込んでいなかったので、それが存在するターミナルに書き込んでいました。
これは、stdinの概念が奇妙な方法で機能するためではなく、Linux/procファイルシステムがプロセスがstdinとして読み取っているものにリンクしているために発生しました。
プロセスが端末からstdinとして読み取っているこのような場合、リンクは、入力(デバイスからの読み取り時)と出力(デバイスへの書き込み時)の両方を表すデバイスノードを指します。 )その端末の側面、したがって、このノードは、そこから読み取るときにのみプロセスのstdinに関連し、ノードへの書き込みはプロセスのstdinとは関係ありません。

2