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


36

ブライアン・カーニハンは、このビデオで、メモリ制限に基づいている小さな言語/プログラムに対する初期のベル研究所の魅力について説明しています

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

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

ウィキペディアから:

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

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

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

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

例:

sed 'simplesubstitution' file | sort | uniq > file2

sedファイルを読み込んで行ごとに吐き出しているのは明らかです。しかしsort、リンクされたビデオでBKが述べているように、完全に停止しているので、すべてのデータをメモリに読み込む必要がありますか(またはそれを行いますか?)、それはに渡されますuniq(私にとっては) -ラインアットアタイムプログラム。しかし、最初のパイプと2番目のパイプの間では、すべてのデータがメモリ内にある必要がありますか?


1
unless swap is used十分なRAMがない場合、スワップは常に使用されている
edc65は

回答:


44

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

あなたの例では、

sed 'simplesubstitution' file | sort | uniq > file2

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

実行することにより、ブロック動作を確認できます

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

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


13
この回答は、さらに多くの小さなものになぜ分割プログラムの説明から利益を得ることができるメモリの使用を節約する:プログラムは、実行にインメモリ合うことができるように持っていたが、唯一、現在実行中のプログラム。初期のUnixでは、他のプログラムはすべてディスクにスワップアウトされていましたが、一度に実際のRAMにスワップされるプログラムも1つだけでした。そのため、CPUは1つのプログラムを実行し、パイプ(当時はディスク上にあった)に書き込み、そのプログラムをスワップアウトし、パイプから読み取ったプログラムにスワップします。論理的に並列なアセンブリラインをインクリメンタルシリアル化された実行に変えるエレガントな方法。
mtraceur

6
@malan:複数のプロセスを開始し、同時に実行可能な状態にすることができます。ただし、最大で1つのプロセスが各物理CPUでいつでも実行でき、各実行可能プロセスにCPU時間の「スライス」を割り当てるのはカーネルのプロセススケジューラの仕事です。現代のシステムでは、実行可能であるが現在CPUタイムスライスがスケジュールされていないプロセスは、通常、次のスライスを待機している間メモリに常駐しますが、カーネルはプロセスのメモリをディスクにページングして、再びメモリに戻すことができます便利だと思います。(ここにいくつかの詳細を手を振る。)
ダニエル・プライデン

5
パイプのいずれかの側のプロセスは、コルーチンのように効果的に動作できます。一方の側は、バッファーと書き込みブロックがいっぱいになるまで書き込みます。この時点で、プロセスは残りのタイムスライスで何もできず、 IO待機モード。次に、OSは残りのタイムスライス(または別の今後のタイムスライス)を読み取り側に渡します。読み取り側は、バッファーと次の読み取りブロックに残りがなくなるまで読み取りを行います。そのタイムスライスとOSに戻ります。データは、一度に1バッファ分のパイプを通過します。
ダニエル・プライデン

6
@malanプログラムは概念的にすべてのUnixシステムで「同時に」開始されます。ちょうどそれらを保持するのに十分なRAMを備えた最新のマルチプロセッサシステムで、文字通りすべてが同時にRAMに保持され、同時にそれらをすべてRAMに同時に保持しないでください。ディスクにスワップアウトされるものもあります。また、多くのコンテキストでの「メモリ」は、ディスク上のRAMスペースとスワップスペースの合計である仮想メモリを意味することに注意してください。ウィキペディアは、実装の詳細よりも概念に焦点を当てています。特に、古いUnixが実際にどのように物事を行ったかは今ではあまり関係がないためです。
mtraceur

2
@malanまた、あなたが見ている矛盾は、「メモリ」の2つの異なる意味(RAMとRAM +スワップ)から来ています。私はハードウェアRAMのみについて話していましたが、そのコンテキストでは、CPUによって現在実行されているコードのみがRAMに収まる必要があります(これはカーニガンが話している決定に影響を与えていました)が、論理的に実行されているすべてのプログラムのコンテキストでは所定の時間にOSによって(タイムスライシングに加えて提供される抽象レベルで)、プログラムは、OSで使用可能な仮想メモリ全体に収まる必要があります。これには、ディスク上のスワップ領域が含まれます。
mtraceur

34

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

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

保留中のデータの量は、ディスク上のiノードの直接ブロックのみがストレージに使用されるという事実によって制約されていました。パイプからの読み取りには通常のファイルの読み取りに使用されたアルゴリズムとほぼ同じアルゴリズムが使用されたため、このメカニズムによりコードがより簡単になりました。

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

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

参考文献

  • Steve D. Pate(1996)。「プロセス間通信」。UNIX Internals:A Practical Approach。アディソン・ウェスリー。ISBN 9780201877212。
  • モーリス・J・バッハ(1987)。「ファイルシステムのシステムコール」。 Unixオペレーティングシステムの設計。プレンティスホール。ISBN 0132017571。
  • スティーブンV.イアハート(1986)。「config(1M)」。 Unixプログラマーズマニュアル:3.システム管理機能。ホルト、ラインハート、ウィンストン。ISBN0030093139。23〜28ページ。

1

部分的には正しいですが、偶然です。

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

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

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

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

多くの単純なプログラムの哲学は、かつてメモリが非常に不足していたときに最も役に立ちました。なぜなら、小さなステップで、一度に1つずつ作業できるからです。最近では、柔軟性が増していることを除けば、その利点はあえて言えません。
ただし、パイプは非常に効率的に実装されているため(そうでなければなりませんでした!)


「パイプに名前が付けられた」と言うと(JdeBPは「パイプデバイス」が1つあると言っているように見えます)、それは、特定の時間に使用できるパイプの数に制限があったことを意味しますか(つまり、|コマンドで何回使用できますか?)
マラン

2
私はそのような限界を見たことはありません、そして、私は理論的にそれがかつてあったとは思いません。実際には、ファイル名を持つものはすべてiノードを必要とし、iノードの数はもちろん有限です。システム上の物理ページの数と同様に、他に何もありません。最新のシステムは4kのアトミック書き込みを保証するため、各パイプ少なくとも1つの完全な4kページを所有する必要があります。これにより、保持できるパイプの数に厳しい制限が課されます。しかし、数ギガバイトのRAMを用意することを検討してください。実際には、これは決して遭遇しない限界です。ターミナルで数百万本のパイプを試してみてください... :)
デイモン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.