シェルでコマンドを実行する

シェルでコマンドを実行する

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//のファイル記述子を開きますstderrstdinつまり、何かを印刷するスクリプトを実行すると、印刷は書き込みと同じですstdout

これまでstdout//は、シェルが使用できる唯一のインターフェースstderrです。stdinアプリケーションの場合も同様です。

一部のオペレーティングシステムコンポーネントは、最終的に記録されたデータをstdout端末に移動します。それ以外の場合は何も印刷されません。

stdout/stdin/stderrstd*端末の何かと実際にやり取りするために、端末への接続はいつどこで発生しますか?

私が挑戦したいおおよその仮定は次のとおりです。

/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と#stdout2とも呼ばれます)を継承すると予想できます。stderr開封して使用可能

最も簡単な場合、テキストコンソールにログインしているセッション内のコマンドラインプログラムでリダイレクトが適用されない場合、この継承チェーンはログインしたgettyユーザーのTTYデバイスを初期化したプロセスに戻ります。セッション。

GUIを使用してログインすると、ディスプレイマネージャプロセス(gdm/kdm/sddm/lightdm/xdm/<whatever>dm)は通常、標準の入力と出力をセッションの最初のプロセスと同様に設定し、これらの/dev/nullファイル$HOME/.xsession-errors記述子もセッションで開始されたすべてのGUIプログラムに継承されます。デスクトップ環境の一部であるか、デスクトップメニューまたはアイコンを使用して実行されます。

たとえば、SSHセッションの場合、sshdセッションを初期化するために分岐されたプロセスは疑似TTYデバイスのペアを割り当て、ファイル記述子はその半分をポイントし、ユーザーstdin/out/errexec()シェルを編集します。分岐のもう一方の端は、疑似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/stdindev/stdin

(これはプログラムがstdinを読み取るように書かれていませんが、ファイルから読み込むことができる場合にのみ必要です。)(これはすべてstdoutとstderrにも適用されます)

のファイルは/proc実際のファイルではありません(どこにも保存されません)。ファイルシステムからアクセスすると動的に作成されます(ディスクには記録されません)。これはディスクではなくカーネルからデータ構造を取得します。

関連情報