質問

bash がパイプを介したデータ送信をどのように処理するかを知っている人はいますか?

cat file.txt | tail -20

このコマンドは、file.txt のすべての内容をバッファーに出力し、その後末尾で読み取られますか?それとも、このコマンドは、たとえば、file.txt の内容を 1 行ずつ出力し、最後が処理されるまで各行で一時停止し、さらにデータを要求するのでしょうか?

私が尋ねる理由は、基本的にデータの塊に対して一連の操作を実行する、ある操作の出力が次の操作の入力として送信される、組み込みデバイス上でプログラムを書いているからです。Linux (bash) がこれをどのように処理するのか知りたいので、「cat file.txt | tail -20」を実行すると何が起こるかではなく、一般的な答えを教えてください。

ご回答いただきありがとうございます。

編集:Shog9 は、関連する Wikipedia 記事を指摘しました。これは私をその記事に直接誘導しませんでしたが、これを見つけるのに役立ちました。 http://en.wikipedia.org/wiki/Pipeline_%28Unix%29#Implementation そこには私が探していた情報がありました。


はっきりさせなくてごめんなさい。もちろんパイプを使用し、コマンドの各部分の stdin と stdout を使用します。それは明白すぎて言えないと思っていました。

私が尋ねているのは、これがどのように処理/実装されるかです。両方のプログラムを同時に実行することはできないため、データはどのように stdin から stdout に送信されるのでしょうか?最初のプログラムが 2 番目のプログラムよりも大幅に高速にデータを生成するとどうなるでしょうか?システムは最初のコマンドを終了するか標準出力バッファがいっぱいになるまで実行し、その後次のプログラムに移り、処理すべきデータがなくなるまでループで繰り返すのか、それとももっと複雑なメカニズムがあるのか?

役に立ちましたか?

解決

もう少し詳しい説明を書いてみることにしました。

ここでの「魔法」はオペレーティング システムにあります。どちらのプログラムも、コンピュータ上で同時に実行されている他のすべてのプロセス (ターミナル アプリケーションとカーネルを含む) とほぼ同時に起動し、同時に実行されます (オペレーティング システムは、実行するプロセッサ上の時間のスライスをプログラムに割り当てます)。 。したがって、データが渡される前に、プロセスは必要な初期化を実行します。あなたの例では、tail は '-20' 引数を解析し、cat は 'file.txt' 引数を解析してファイルを開きます。ある時点で、tail は入力が必要なポイントに到達し、入力を待っていることをオペレーティング システムに伝えます。別の時点 (前後は関係ありません) で、cat は stdout を使用してオペレーティング システムにデータを渡し始めます。これはオペレーティング システムのバッファに入ります。cat によってデータがバッファに入れられた後、次回 tail がプロセッサ上でタイム スライスを取得すると、オペレーティング システム上のバッファに残されたデータの一部 (またはすべて) が取得されます。バッファが空の場合、ある時点で tail は cat がさらにデータを出力するのを待たなければなりません。cat が tail の処理よりもはるかに速くデータを出力すると、バッファが拡張されます。cat は最終的にデータの出力を終了しますが、tail はまだ処理中であるため、cat は終了し、tail はバッファーに残っているすべてのデータを処理します。オペレーティング システムは、EOF を持つ受信データがなくなるとテール信号を送信します。Tail は残りのデータを処理します。この場合、tail はおそらくすべてのデータを 20 行の循環バッファに受信しているだけであり、オペレーティング システムから受信データがなくなったことを通知されると、最後の 20 行を自身の stdout にダンプします。ターミナルに表示されるだけです。tail は cat よりもはるかに単純なプログラムであるため、cat がバッファにデータを入れるのを待つのにほとんどの時間を費やすことになるでしょう。

複数のプロセッサを備えたシステムでは、2 つのプログラムは同じプロセッサ コア上で交互のタイム スライスを共有するだけでなく、別のコア上で同時に実行される可能性があります。

もう少し詳しく説明すると、Linux の「top」など、ある種のプロセス モニター (オペレーティング システム固有) を開くと、実行中のプロセスの全リストが表示されますが、そのほとんどがプロセッサーを実質的に 0% 使用しています。ほとんどのアプリケーションは、データを処理している場合を除き、ほとんどの時間を何もせずに過ごします。これにより、他のプロセスが必要に応じてプロセッサに自由にアクセスできるようになるため、これは良いことです。これは基本的に 3 つの方法で実現されます。プロセスは、基本的に、別のタイム スライスを処理する前に n ミリ秒待機するようカーネルに指示する sleep(n) スタイルの命令に到達する可能性があります。最も一般的には、プログラムは別のプログラムからの何かを待つ必要があります。たとえば、「tail」はバッファに追加のデータが入るのを待っています。この場合、より多くのデータが利用可能になったときにオペレーティング システムがプロセスを起動します。最後に、カーネルは実行中のプロセスをプリエンプトして、プロセッサのタイム スライスの一部を他のプロセスに与えることができます。「cat」と「tail」は単純なプログラムです。この例では、tail はバッファ上のさらなるデータの待機にほとんどの時間を費やし、cat はオペレーティング システムがハードドライブからデータを取得するのを待機するのにほとんどの時間を費やします。ボトルネックは、ファイルが保存されている物理メディアの速度 (または遅さ) です。このコマンドを初めて実行するときに検出される可能性のある遅延は、ディスク ドライブの読み取りヘッドがハードドライブ上の「file.txt」がある位置をシークするのにかかる時間です。このコマンドを 2 回目に実行すると、オペレーティング システムの file.txt の内容がメモリにキャッシュされる可能性が高く、知覚できる遅延はほとんど発生しません (file.txt が非常に大きいか、ファイルがキャッシュされなくなった場合を除く) 。)

コンピューター上で行うほとんどの操作は IO バウンドです。つまり、通常はハードドライブやネットワーク デバイスなどからデータが送信されるのを待っています。

他のヒント

Shog9 は既に Wikipedia の記事を参照しましたが、 実装セクション あなたが望む詳細があります。基本的な実装は境界付きバッファーです。

cat はデータを標準出力に出力するだけですが、これはたまたま tail の標準入力にリダイレクトされます。これは bash の man ページで確認できます。

言い換えれば、一時停止は発生せず、tail は標準入力からの読み取りだけを行い、cat は標準出力への書き込みを行うだけです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top