web-dev-qa-db-ja.com

パイプラインはどのようにメモリ使用量を制限しますか?

Brian Kernighanが このビデオ でメモリの制限に基づいている小さな言語/プログラムに対するBell Labsの魅力を説明しています

大きなマシンは64 kバイト(Kであり、MやGではない)になるため、個々のプログラムは非常に大きくなることはできず、小さなプログラムを記述してからパイプメカニズムを作成する自然な傾向がありました。基本的に入出力リダイレクトで、あるプログラムを別のプログラムにリンクすることを可能にしました。

しかし、プログラム間でデータを転送するためにデータをRAM=)に保存する必要があるという事実を考慮すると、これがメモリ使用量をどのように制限するか理解できません。

Wikipedia から:

ほとんどのUnixライクなシステムでは、パイプラインのすべてのプロセスが同時に開始されます[強調]、ストリームが適切に接続され、マシンで実行されている他のすべてのプロセスとともにスケジューラによって管理されます。 Unixパイプを他のパイプ実装とは別に設定するこの重要な側面は、バッファリングの概念です。たとえば、送信プログラムは1秒あたり5000バイトを生成し、受信プログラムは1秒あたり100バイトしか受け入れられないかもしれませんが、データは失われます。代わりに、送信プログラムの出力はバッファに保持されます。受信プログラムがデータを読み取る準備ができると、パイプラインの次のプログラムがバッファーから読み取ります。 Linuxでは、バッファーのサイズは65536バイト(64KB)です。必要に応じて、bfrと呼ばれるオープンソースのサードパーティフィルターを使用して、より大きなバッファーを提供できます。

これは小さなプログラムの目的を完全に無効にするので、私はさらに混乱します(特定の規模までモジュール化されていますが)。

私の最初の質問(サイズのデータ​​に依存する問題のあるメモリの制限)の解決策として考えることができる唯一のことは、大規模なデータセットがその当時単純に計算されず、実際の問題のパイプラインが解決することでしたプログラム自体が必要とするメモリの量。しかし、ウィキペディアの引用の太字のテキストを考えると、これは私を混乱させます:一度に1つのプログラムが実装されていないため。

一時ファイルが使用されている場合、これらすべては非常に理にかなっていますが、パイプがディスクに書き込まないことは私の理解です(スワップが使用されない限り)。

例:

sed 'simplesubstitution' file | sort | uniq > file2

sedがファイルを読み取り、1行ずつファイルを吐いているのは明らかです。しかし、BKがリンクされたビデオで述べているように、sortは完全に停止しているため、すべてのデータをメモリに読み込む必要があります(またはそれを行いますか?)、それはuniqに渡されます。一度に1行のプログラム。しかし、最初のパイプと2番目のパイプの間では、すべてのデータがメモリになければなりません。

36
malan

データをRAMに保存する必要はありません。読者がそこにいない、または追いつけない場合、パイプはライターをブロックします。 Linux(および私が想像する他のほとんどの実装)では、いくつかのバッファリングがありますが、それは必須ではありません。 mtraceurJdeBP後者の答え を参照)で述べたように、ディスクへのUnixバッファパイプの初期バージョンでは、これが制限に役立ちましたメモリ使用量:処理パイプラインは小さなプログラムに分割でき、それぞれがディスクバッファーの制限内でいくつかのデータを処理します。小さなプログラムはメモリが少なくて済み、パイプを使用することで処理をシリアル化できます。最初のプログラムが実行され、その出力バッファーがいっぱいになり、中断され、次に2番目のプログラムがスケジュールされ、バッファーを処理します。初期のUnixシステムよりも規模が大きく、多くのパイプを並行して実行できます。しかし、膨大な量のデータについても、同様の効果が見られます(そして、この種の手法の変形が「ビッグデータ」処理に使用されます)。

あなたの例では、

sed 'simplesubstitution' file | sort | uniq > file2

sedは、必要に応じてfileからデータを読み取り、sortが読み取り可能な状態である限り、データを書き込みます。 sortの準備ができていない場合、書き込みはブロックされます。データは実際には最終的にメモリに存在しますが、これはsortに固有であり、sortはすべての問題に対処する準備ができています(一時ファイルを使用して、ソートするデータの量が大きすぎる場合) )。

実行すると、ブロッキング動作を確認できます

strace seq 1000000 -1 1 | (sleep 120; sort -n)

これにより、かなりの量のデータが生成され、最初の2分間は何も読み取る準備ができていないプロセスにパイプ処理されます。いくつかのwrite操作が実行されますが、非常にすぐにseqが停止し、2分が経過するのを待ちます。カーネル(writeシステム)によってブロックされます。コール待機)。

45
Stephen Kitt

しかし、プログラム間でデータを転送するためにRAM)にデータを保存する必要があるという事実を考えると、これがメモリ使用量をどのように制限するか理解できません。

これは基本的なエラーです。 Unixの初期のバージョンでは、パイプデータをRAMに保持していませんでした。彼らはそれらをディスクに保存しました。パイプにはiノードがありました。 パイプデバイスと示されたディスクデバイス上。システム管理者は/etc/configという名前のプログラムを実行して、ディスクなどのどのデバイスがパイプデバイスで、どのボリュームがルートデバイスであるかを指定しました、およびダンプデバイス

保留中のデータの量は、ディスク上のiノードの直接ブロックのみがストレージに使用されるという事実によって制約されていました。このメカニズムにより、通常のファイルの読み取りと同じアルゴリズムがパイプからの読み取りに使用されたため、コードが単純になりました。パイプがシーク不可能であり、バッファーが循環しているという事実に起因する微調整がいくつかあります。

このメカニズムは1980年代中頃から後半にかけて他のメカニズムに置き換えられました。 SCO XENIXは、iノードをコア内バッファに置き換えた「高性能パイプシステム」を取得しました。4BSDは、名前のないパイプをソケットペアに作成しました。AT&Tは、STREAMSメカニズムを使用してパイプを再実装しました。

そしてもちろん、sortプログラムは入力の32KiBチャンクの限られた内部ソート(または32KiBが利用できない場合に割り当てることができるメモリ量が少ない場合)を実行し、ソートされた結果を中間stmX??ファイルに書き込みました/usr/tmp/では、外部でマージソートされ、最終的な出力が提供されます。

参考文献

  • スティーブ・D・ペイト(1996)。 「プロセス間通信」。 UNIX Internals:A Practical Approach。 Addison-Wesley。 ISBN 9780201877212。
  • モーリス・J・バッハ(1987)。 「ファイルシステムのシステムコール」。 The Unix Operating Systemの設計。プレンティスホール。 ISBN 0132017571。
  • スティーブンV.イアハート(1986)。 「config(1M)」。 Unixプログラマーズマニュアル:3.システム管理機能。ホルト、ラインハート、ウィンストン。 ISBN0030093139。23〜28ページ。
  • Abhijit Menon-Sen(2020-03-23)。 nixパイプはどのように実装されていますか? toroid.org。
35
JdeBP

あなたは部分的に正しいですが、偶然のみです。

あなたの例では、すべてのデータが実際にパイプの「間」で読み取られている必要がありますが、メモリ(仮想メモリを含む)に常駐する必要はありません。 sortの通常の実装では、RAMに収まらないデータセットを並べ替えることができます。これは、一時ファイルにパーシャルソートを実行し、マージすることによって行われます。ただし、すべての要素を読み取る前に並べ替えられたシーケンス。それは明らかです。そのため、sortは、最初からすべてを読み取った後(そして一時ファイルを部分的に並べ替えた後)のみ、2番目のパイプへの出力を開始できます。しかし、それはしないでください必ずしもすべてをRAMに保持する必要があります。

ただし、これはパイプの動作とは関係ありません。パイプには名前を付けることができます(従来はすべて名前が付けられていました)。これは、ファイルのように、ファイルシステム内に場所があることを意味します。パイプがかつてのファイルであったのはそれだけです(最適化として、物理メモリの可用性が許す限り、書き込みを合体させます)。

今日では、パイプはデータがコピーされる小さな有限サイズのカーネルバッファーであり、少なくとも概念的にが発生します。カーネルがそれを助けることができる場合、VMトリックをプレイすることでコピーが省略されます(たとえば、ファイルからのパイプは通常、同じページを他のプロセスが読み取れるようにするだけなので、最終的には読み取り操作、2つのコピーではなく、いずれにしてもバッファキャッシュで既に使用されている追加のメモリは必要ありません。状況によっては、100%ゼロコピーになることもあります。

パイプが小さく、サイズが有限の場合、これは、未知の(場合によっては大量の)データ量に対してどのように機能しますか?それは簡単です。何も収まらない場合、書き込みは再び空きができるまでブロックされます。

多くの単純なプログラムの哲学は、記憶が非常に乏しかった時代に最も有用でした。なぜなら、一度に1つずつ小さなステップで作業できるからです。今日、利点は、いくつかの追加の柔軟性を除いて、私は勇気を振り絞って、もうそれほど大きくはありません。
しかし、パイプは非常に効率的に実装されているため(実際に実装する必要がありました!)、不利な点もありません。また、正常に機能し、慣れている確立されたものであるため、変更する必要はありません。パラダイム。

1
Damon