cat は遅延評価を使用しますか?

cat は遅延評価を使用しますか?

例えばパイプを使う場合

sudo cat /dev/sda | strings | less

SDA デバイスの文字列の行を移動できます。しかし、SDA デバイスの内容は完全にロードされ、cat の出力ストリームに出力されますか? それとも、プログラムが cat からの出力を要求するたびに新しい行が評価されますか? (つまり、less ページャーで j を押します)

答え1

これは、「どのように機能するか」とlessいうよりも、「どのように機能するか」に関係しています。catstrings

このコマンドはデータを標準出力にプッシュするだけで、コマンドと 間のパイプ バッファーがいっぱいで誰も読み取っていない cat場合はブロックされます。は自身で最小限のバッファリングを行い、パイプ バッファーは通常小さくなります。stringscat

これは にも当てはまりますstrings。 は からのデータを処理し、 が が生成するデータを読み取っていないcat場合は をブロックします。lessstrings

lessは入力をバッファリングして、表示されるデータ内を前後に移動できるようにします。次のページにスクロールすると、バッファlessからさらにデータが読み込まれますstrings。前方にスクロールしていない間は、限られた量のデータのみが読み込まれると思いますless(したがって、前方にスクロールしていない間はstringsブロックcatされます)。

大量のデータをパイプするとless、このバッファリングにかなりのメモリが使用されます。もしあなたはそれを最後まで全部読むことにしましたless

-Bバッファリングに使用されるメモリの量を 64 キロバイト (または オプションで指定した量) に制限するオプション があります-b。このようにバッファ サイズを制限すると、指定されたバッファ スペースに格納できる量を超えてスクロールバックすることがなくなりますが、lessメモリ不足に陥ることなく、 で大量のデータを読み取ることもできます。

man lessシステムについても参照してください。

答え2

パイプには限られたバッファ スペースがあり、パイプ リーダー (less例のように) がパイプからそれ以上データを読み取らない場合、バッファーがいっぱいになった後にライターがブロックされます。これはコマンドに影響し、パイプがいっぱいになった後にコマンドがstringsブロックされます。cat

当然、catコマンドは sda デバイスの内容全体をメイン メモリに読み込むことはできません。そのため、まだ読み込まれていないブロックが変更されている場合は、cat変更された内容が表示されます。

答え3

catと、およびほとんどstringsの類似ユーティリティ¹ は、一度に少しずつ入力を読み取り、処理してから、さらに入力を読み取り、という処理を繰り返します。したがって、あなたの場合、 は表示されているcatものだけを読み取りless、さらに転送中のものを少し読み取ります。

より詳細には、 の基本的な動作はcat次のとおりです。

  • バッファとして使用するために数キロバイトのメモリを予約します。
  • 他にも入力可能な項目があります:
    • 最大 N バイトの入力をバッファに読み取ります (これにより、前のサイクルで書き込まれたデータが上書きされます)。
    • バッファの内容を出力に書き込みます。

書き込み操作は、出力をコピーする場所ができるまでブロックされます。パイプの出力の場合、パイプ自体はカーネル内の小さなメモリを消費します。これはパイプバッファいっぱいになると、catパイプに書き込もうとすると、空きができるまで書き込みの試行がブロックされます。パイプの読み取り側のプロセスがデータを読み取ると、パイプ バッファーに空きができることがあります。

このstringsプログラムは と同じように動作しますがcat、入力全体をコピーするのではなく、選択した部分のみをコピーする点が異なります。

プログラムlessの動作は少し異なります。読み込んだものはすべてメモリに保存されます。バッファはリサイクルされず、入力が続く限りバッファは大きくなり続けます。ただし、読み取り部分は、less必要なときだけデータを読み取るという点で似ています。つまり、表示される最後の行まで読み取り、さらに、利用可能な場合は予測してもう少し読み取ります。

したがって、 を実行するとsudo cat /dev/sda | strings | less、 から読み取られる内容は/dev/sda次のようになります。

  • lessすでに表示されている(またはスクロールして通過した)データ。
  • 読み取られたがまだ表示されていないデータは最大数 KB ありますless
  • stringsとの間のパイプ バッファーでは最大数 kB ですless
  • のメモリに最大数kB strings
  • catとの間のパイプ バッファーでは最大数 kB ですstrings
  • のメモリに最大数kB cat

システム コールをトレースすることで、各プログラムがいつデータを読み書きするかを監視できます。

sudo strace -e read,write -o cat.strace cat /dev/sda | strace -e read,write -o cat.strace strings | strace -e read,write -o less.strace less

そして、ファイルを監視します。また、またはなどのファイル オフセットをチェックすることで、読み取られた*.strace量を確認することもできます。ここで、 はのプロセス ID です。catlsof -p1234head /proc/1234/fdinfo/01234cat

¹基本的なテキスト処理ユーティリティの中で、主な例外は ですsort。これは、入力全体を読み取るまで出力を生成できません。出力の最初の行が、到達した入力の最後の行になる可能性もあります。

答え4

一部のシステム (MS-Dos など) では、最初のコマンドの出力をファイルにコピーし、2 番目のコマンドを実行してこのファイルから読み取ることによってパイプが実装されます。 Unixではそうはならない

Unix では、生産ラインのようなものです。各ステージは同時に動作し、入力を読み取り、出力を生成します。プロセス A の生産速度がプロセス B の消費速度よりも速い場合、プロセス A と B の間に在庫が蓄積されます。この量が多すぎる場合 (½KiB から 4 KiB)、プロセス A は一時停止します。B が処理する在庫がない場合は、B が一時停止します。プロセスは、在庫レベルを低く保つために一時停止したり、一時停止を解除したりします。

これらのプログラムのコードは、これらをまったく考慮しません。入力を読み取り、出力を書き込むだけです。データが利用可能になる前に読み取りを試みたり、次のプロセスの準備ができる前に書き込みを試みたりすると、オペレーティング システムは準備ができるまでそれを一時停止します。

読み取るものがなくなると(そしてそれ以上読み取る予定がなくなると)、リーダーはファイルの終わりを受け取り、終了します。これにより、次のプロセスでファイルの終わりがトリガーされます。

関連情報