Pipe

管道如何限制記憶體使用?

  • March 27, 2020

Brian Kernighan 在這段影片中解釋了早期貝爾實驗室對基於記憶體限制的小語言/程序的吸引力

一台大機器將是 64 k 字節——K,而不是 M 或 G——這意味著任何單獨的程序不可能很大,所以自然傾向於編寫小程序,然後是管道機制,基本上輸入輸出重定向,可以將一個程序連結到另一個程序。

但考慮到數據必須儲存在 RAM 中才能在程序之間傳輸,我不明白這如何限制記憶體使用。

來自維基百科

在大多數類 Unix 系統中,管道的所有程序同時啟動$$ emphasis mine $$,它們的流適當地連接,並由調度程序與機器上執行的所有其他程序一起管理。將 Unix 管道與其他管道實現區分開來的一個重要方面是緩衝的概念:例如,發送程序可能每秒產生 5000 個字節,而接收程序可能每秒只能接受 100 個字節,但沒有數據失去。相反,發送程序的輸出保存在緩衝區中。當接收程序準備好讀取數據時,管道中的下一個程序從緩衝區中讀取。在 Linux 中,緩衝區的大小為 65536 字節(64KB)。如果需要,可以使用名為 bfr 的開源第三方過濾器來提供更大的緩衝區。

這讓我更加困惑,因為這完全違背了小程序的目的(儘管它們在一定程度上是模組化的)。

我唯一能想到的第一個問題的解決方案(記憶體限制取決於大小數據)是當時根本沒有計算大型數據集,真正的問題管道旨在解決是程序本身所需的記憶體量。但是鑑於維基百科引用中的粗體文本,即使這也讓我感到困惑:因為一次沒有實現一個程序。

如果使用臨時文件,所有這些都會很有意義,但我的理解是管道不會寫入磁碟(除非使用交換)。

例子:

sed 'simplesubstitution' file | sort | uniq > file2

我很清楚,sed正在讀取文件並逐行將其吐出。但是sort,正如 BK 在連結影片中所說的那樣,是一個句點,所以所有數據都必須讀入記憶體(或者是嗎?),然後將其傳遞給uniq,(在我看來)將是一個-一次一行的程序。但是在第一個和第二個管道之間,所有數據都必須在記憶體中,不是嗎?

數據不需要儲存在 RAM 中。如果讀者不在那里或跟不上,管道會阻止他們的作者;在 Linux 下(以及大多數其他實現,我想)有一些緩衝,但這不是必需的。正如mtraceurJdeBP所提到的(見後者的回答),早期版本的 Unix 緩衝管道到磁碟,這就是它們幫助限制記憶體使用的方式:處理管道可以分成小程序,每個小程序都會在磁碟緩衝區的限制內處理一些數據。小程序佔用更少的記憶體,並且管道的使用意味著可以串列化處理:第一個程序將執行,填充其輸出緩衝區,被掛起,然後第二個程序將被調度,處理緩衝區等。現代系統是命令比早期的 Unix 系統大,可以並行執行許多管道;但是對於大量數據,您仍然會看到類似的效果(並且這種技術的變體用於“大數據”處理)。

在你的例子中,

sed 'simplesubstitution' file | sort | uniq > file2

sed根據需要從中讀取數據file,然後在sort準備好讀取時寫入;如果sort尚未準備好,則寫入阻塞。數據最終確實存在於記憶體中,但這是特定於 的sort,並sort準備處理任何問題(它將使用臨時文件,因為要排序的數據量太大)。

您可以通過執行查看阻塞行為

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

這會產生相當多的數據並將其通過管道傳輸到前兩分鐘還沒有準備好讀取*任何內容的程序。*您會看到許多write操作通過,但很快seq就會停止並等待兩分鐘過去,被核心阻塞(write系統呼叫等待)。

引用自:https://unix.stackexchange.com/questions/450877