tty
端末でコマンドを実行すると返されます/dev/pts/10
。
これに加えて、ファイル/dev/stdout
/dev/stdin
との/dev/stderr
やり取りは端末に直接表示されます。
user@laptop:build$ tty
/dev/pts/10
user@laptop:build$ echo "Test" > /dev/stdout
Test
user@laptop:build$ echo "Test" > /dev/stdin
Test
user@laptop:build$ echo "Test" > /dev/stderr
Test
また、シェルで起動されたすべてのcliアプリケーションはstdout
//のファイル記述子を開きますstderr
。stdin
つまり、何かを印刷するスクリプトを実行すると、印刷は書き込みと同じですstdout
。
これまでstdout
//は、シェルが使用できる唯一のインターフェースstderr
です。stdin
アプリケーションの場合も同様です。
一部のオペレーティングシステムコンポーネントは、最終的に記録されたデータをstdout
端末に移動します。それ以外の場合は何も印刷されません。
stdout/stdin/stderr
std*
端末の何かと実際にやり取りするために、端末への接続はいつどこで発生しますか?
私が挑戦したいおおよその仮定は次のとおりです。
/dev/stdout
、実行中のシェルによって生成され/dev/stdin
、/dev/stderr
シェルがなければ存在しないものです。シェルは、端末を表す実際のデバイスファイルを介して通信チャネルを設定し、
/dev/pts/10
端末機能を公開します。このように、シェルは、各アプリケーションが簡単な印刷のためにデバイスファイルを処理する方法を心配することなく、アプリケーションへの単純なファイルインタフェースを提供する。/dev/stdout
/dev/stdin
/dev/stderr
修正する
疑似端末にもかかわらず、疑似端末/dev/pts/10
の概念を導入せずに回答を提供する回答にもっと重点を置きます。私のポイントは、これが質問に対する答えを妨げるだけです。
stdout/stdin/stderr
/dev/std*
端末の何かと実際にやり取りするために、端末への接続はいつどこで発生しますか?
答え1
/dev/pts/10
奴隷の終わりは擬似端末装置ペア。もう一方の端には、マスターレプリケーションデバイスを開いてペアで受信するプログラムがあります/dev/ptmx
(/dev/pts/10
開くたびに/dev/ptmx
別のスレーブデバイスを取得します)。/dev/ptmx
との間の接続は/dev/pts/10
基本的に双方向パイプです。反転がある。
ターミナルエミュレータアプリケーションを開くとき:
- それは開き、
/dev/ptmx
もう一方の端の名前を取得します。反対側の端を構成して開きます。 - それはフォーク、
- 新しいプロセスは、擬似端末装置のもう一方の端を開いて接続する。標準入力、標準出力そして標準エラーそれに、
- 新しいプロセスはシェルを実行します。
シェルはこれら3つのファイル記述子を設定するために何もしません。親プロセスから継承します。同様に、その子プロセスはシェルからファイル記述子を継承します。
コメント:Linuxシステムでは、/dev/stdin
および/dev/stdout
は一連のシンボリック/dev/stderr
リンクを指す物理ファイルであり、これは実際の入力/出力デバイスを指します。あなたの場合です。/proc/<pid>/0
/proc/<pid>/1
/proc/<pid>/2
/dev/pts/10
これら3つの存在標準ストリームCライブラリで保証します。
編集する:更新された質問を解決するために、答えのいくつかのポイントを明確にします。
- 書かれたすべては、
/proc/pts/*
それが作成された端末から読み取られ、表示され、読み取られたすべては、/proc/pts/*
端末に接続された入力デバイスから来ます。 - Linuxでは、
/dev/stdout
通常はにシンボリックリンクされており/proc/self/fd/1
、に/dev/stdin
シンボリックリンクされています/proc/self/fd/0
。仮想/proc
ファイルシステムは、アプリケーションプロセスIDを持つ各アプリケーションへの/proc/self
シンボリックリンクを慎重に表示します。/proc/<pid>
<pid>
/proc/<pid>/fd
シンボリックリンクは、アプリケーションが親アプリケーションで開くか継承するファイル、パイプ、およびその他のコンテンツを指します。各アプリケーションは、0
入力読み取り、1
出力書き込み、2
エラー書き込みなど、3つのファイル記述子を開くことが保証されています。あなたに関する限りです/dev/pts/10
。
出力を別のファイルにリダイレクトせずに、シェルで実行されるすべてのコマンドが端末に書き込まれます。このルールの例外は、コマンドが次のような場合です。プロセスグループ展望とは違うプロセスグループ端末への書き込みが失敗し、SIGTTOU
コマンドとして送信されます。stty tostop
この動作を使用して制御できますstty -tostop
。
stty tostop
echo "/dev/stdout points to the terminal, but I won't print anything" &
stty -tostop
echo "You can see me" &
答え2
POSIX互換プログラムは、親プロセスからファイル記述子#0、#1、および#2(それぞれプログラミング定数stdin
と#stdout
2とも呼ばれます)を継承すると予想できます。stderr
開封して使用可能。
最も簡単な場合、テキストコンソールにログインしているセッション内のコマンドラインプログラムでリダイレクトが適用されない場合、この継承チェーンはログインしたgetty
ユーザーのTTYデバイスを初期化したプロセスに戻ります。セッション。
GUIを使用してログインすると、ディスプレイマネージャプロセス(gdm/kdm/sddm/lightdm/xdm/<whatever>dm
)は通常、標準の入力と出力をセッションの最初のプロセスと同様に設定し、これらの/dev/null
ファイル$HOME/.xsession-errors
記述子もセッションで開始されたすべてのGUIプログラムに継承されます。デスクトップ環境の一部であるか、デスクトップメニューまたはアイコンを使用して実行されます。
たとえば、SSHセッションの場合、sshd
セッションを初期化するために分岐されたプロセスは疑似TTYデバイスのペアを割り当て、ファイル記述子はその半分をポイントし、ユーザーstdin/out/err
のexec()
シェルを編集します。分岐のもう一方の端は、疑似TTYデバイスペアの残りの半分を保持し、セッションが終了するまでネットワークと擬似TTYデバイス間の発信/受信トラフィックの暗号化/暗号解読を処理します。
ターミナルエミュレータがGUIセッションで起動すると、新しいセッションを初期化するのとほぼ同じ方法で動作しますsshd
。つまり、疑似TTYfork()
自体を割り当て、ファイル記述子のポイントを含むセッションコピーを設定します。 0、#1、#2を仮想TTYとして、最後にユーザーexec()
シェルでは、フォークの反対側は、端末ウィンドウの視覚的表現を実際に保持する操作を処理し続けます。
簡単に言えば、(疑問?)TTYデバイスは、ターミナルセッションを初期化することによってstdin / stdout / stderrに接続され、そのデバイスとアプリケーションの間に存在できるすべてのプロセスはチェーンに渡されます。継承された人何もしないでくださいこれらのファイル記述子をそのまま子プロセスに渡すようにしてください。
リダイレクト演算子がシェルコマンドラインで使用されると、シェルは実際にコマンドを実行するfork()
準備中に独自の一時コピーを作成するため、一時コピーはそのファイル記述子を閉じてその場所にリダイレクト演算子を開きます。変更されたstdin / out / errファイル記述子を継承するようにコマンドを使用します。exec()
fork()
exec()
一部のUnixスタイルシステムでは、デバイスは/dev/std*
シェルで処理される可能性があります。しかし、Linuxはこれをより「現実的」にします。
Linuxでは、/dev/stdin
および/dev/stdout
は、ファイルシステムを指す/dev/stderr
普通の古いシンボリックリンクです。/proc
$ ls -l /dev/std*
lrwxrwxrwx 1 root root 15 Feb 4 08:22 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Feb 4 08:22 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Feb 4 08:22 /dev/stdout -> /proc/self/fd/1
udev
これらのリンクは、システム起動時にRAMベースのファイルシステムが初期化されると生成されます。/dev
それらは単なる普通のシンボリックリンクであり、魔法ではありません。
しかし、これは/proc
システムのプロセス状態をリアルタイムで反映できる完全な仮想ファイルシステムなので、いくつかの「魔法の」属性を持っています。
/proc/self
/proc/<PID>
ディレクトリへのシンボリックリンクです。進行状況を見る:
$ ls -l /proc/self # the PID of this ls command will be 10839
lrwxrwxrwx 1 root root 0 Feb 4 08:22 /proc/self -> 10839/
$ ls -l /proc/self # the PID of this ls command will be 10843
lrwxrwxrwx 1 root root 0 Feb 4 08:22 /proc/self -> 10843/
/proc/<PID>/fd
<PID>
プロセスで使用されるオープンファイル記述子と名前が一致するシンボリックリンクを含むディレクトリ。このファイル記述子に関連するすべての項目を指します。。
したがって、プロセスが/dev/pts/10
アクセスしようとすると、/dev/stdin
シンボリックリンクは/proc/self/fd/0
...を指してアクセスする/proc/self/fd/0
と、/proc
ファイルシステムドライバはカーネルのプロセステーブルを表示し、それを使用してアクセスするプロセスのファイル記述子テーブルを見つけます。シンボリックリンク/proc/self/fd/0
としてレンダリング/dev/pts/10
だからプロセスは現在/dev/pts/10
ファイル記述子#0に関連付けられています。
Solaris 11では、デバイスは「魔法」でもあるディレクトリへ/dev/std*
のシンボリックリンクです。/dev/fd/
$ uname -sr
SunOS 5.11
$ ls -l /dev/std*
lrwxrwxrwx 1 root root 0 Jun 17 2019 /dev/stderr -> ./fd/2
lrwxrwxrwx 1 root root 0 Jun 17 2019 /dev/stdin -> ./fd/0
lrwxrwxrwx 1 root root 0 Jun 17 2019 /dev/stdout -> ./fd/1
ここで、Solarisファイルシステムドライバは、歴史的な理由でLinuxのようにファイルシステムにリダイレクトするのではなく、ディレクトリの/dev
デバイスノードを使用して魔法を実行します。/dev/fd
/proc
答え3
シェルでコマンドを実行する
これは、コマンドがシェルで実行されると発生します。
- シェル呼び出し
fork
:シェルが両方のプロセスで実行される方法。 - 新しいシェル設定
std in/out/err
: しかし、リダイレクトがなければ何もしません。新しいプロセスは元のシェルからこれらの値を継承し、シェルにはすでに正しい値があります。 - 新しいシェル呼び出しは
exec
新しいプログラムを実行します。この新しいプログラムは値を継承し、std in/out/err
新しいシェルを置き換えます。
この新しいシェルは非常に一時的です(単に実装の詳細なので、現在のドキュメントに記載されています)。サブシェルとは異なります。
新しいコマンドが開きます/dev/stdin
新しいプログラムが開かれると、/dev/stdin
カーネルのファイルシステムコードは、これが自分を指すシンボリックリンクであることを確認し、nnnnがプロセスpidである場所を指すシンボリックリンクであることを/proc/self/fd/0
知ります。/dev/self
たとえば、ファイルを指すファイルです。開くと、新しいファイル記述子が生成されます。ファイル記述子0はすでにファイルを指しているため、通常の状況では開く必要はありません。/proc/nnnn
/proc/nnnn/fd/0
/dev/pts/10
/dev/stdin
dev/stdin
(これはプログラムがstdinを読み取るように書かれていませんが、ファイルから読み込むことができる場合にのみ必要です。)(これはすべてstdoutとstderrにも適用されます)
のファイルは/proc
実際のファイルではありません(どこにも保存されません)。ファイルシステムからアクセスすると動的に作成されます(ディスクには記録されません)。これはディスクではなくカーネルからデータ構造を取得します。