例如使用管道時
sudo cat /dev/sda | strings | less
我可以在 sda 裝置的字串中移動。但是sda設備的內容是否完全載入並輸出到cat的輸出流中呢?或者每當程式請求 cat 的輸出時都會評估新行? (即我在 less 尋呼機上按 j)
答案1
這與如何工作有關less
,而不是與如何cat
或strings
工作有關。
該cat
命令只會將資料推送到其標準輸出,並且只要其之間的管道緩衝區strings
已滿並且沒有人在讀取,它就會阻塞。 cat
本身進行最小緩衝,並且管道緩衝區通常很小。
對於 來說也是如此strings
。它將處理來自的數據並在不讀取產生的數據cat
時阻塞。less
strings
less
將緩衝其輸入,以便您可以在其顯示的資料中來回移動。當您滾動到下一頁時,將從其緩衝區中less
讀取更多資料。strings
當您不向前滾動時,我相信less
只會讀取有限數量的數據(因此當您不向前滾動時將被阻止)strings
。cat
如果將大量資料透過管道傳輸到less
,則將使用大量記憶體進行緩衝如果您決定將其全部讀完less
。
有一個選項-B
,將用於緩衝的記憶體量限制為 64 KB(或您使用該-b
選項指定的大小)。以這種方式限制緩衝區大小將防止您回滾超過指定緩衝區空間中可以儲存的內容,但也允許您讀取大量資料而不會less
耗盡記憶體。
另請參閱man less
您的系統。
答案2
管道的緩衝區空間有限,如果管道讀取器(例如less
在您的範例中)沒有從管道讀取更多數據,則寫入器在填充緩衝區後將被阻塞。這將影響該strings
命令,而該命令又會在cat
其管道已滿後阻止該命令。
當然,該cat
命令無法將整個 sda 裝置內容讀取到主記憶體中,因此如果尚未讀取的區塊正在更改,cat
則會看到更改的內容。
答案3
和cat
以及strings
大多數類似的實用程式1,一次讀取一點輸入,對其進行處理,然後讀取更多輸入,依此類推。因此,就您而言,cat
僅讀取less
顯示的內容,再加上一些正在傳輸的內容。
更詳細地說,基本操作cat
是:
- 保留幾千位元組的記憶體用作緩衝區。
- 雖然還有更多可用的輸入:
- 將最多 N 個位元組的輸入讀入緩衝區。 (這會覆蓋上一個週期所寫出的資料。)
- 將緩衝區內容寫入輸出。
寫入操作會阻塞,直到有地方可以複製輸出。當一個管道輸出時,管道本身會消耗內核中的一點內存,稱為管道緩衝區。一旦滿了,如果cat
嘗試寫入管道,寫入嘗試將被阻止,直到有空間為止。當管道讀取端的進程讀取一些資料時,管道緩衝區中可能有空間。
程式strings
的工作方式與 相同cat
,只是它不複製整個輸入,僅複製選定的部分。
該less
程式的工作方式有點不同:它將讀取的所有內容保留在記憶體中。它less
不會回收其緩衝區,只要有更多輸入不斷進入,它就會不斷增長它。,再加上預期讀取的內容(如果有)。
因此,當您運行時sudo cat /dev/sda | strings | less
,讀取的內容/dev/sda
包括:
less
已顯示(或捲動過去)的資料。less
已讀取但尚未顯示的資料最多為幾 kB 。strings
和之間的管道緩衝區最多為幾 kBless
。- 記憶體中最多可達幾 kB
strings
。 cat
和之間的管道緩衝區最多為幾 kBstrings
。- 記憶體中最多可達幾 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
文件。您也可以cat
透過檢查檔案偏移量來檢查已讀取的資料量,例如使用lsof -p1234
或使用head /proc/1234/fdinfo/0
where 1234
is 的進程 ID cat
。
1在基本文字處理實用程式中,主要的例外是sort
,它在讀取整個輸入之前無法發出任何輸出:據它所知,輸出的第一行很可能是它到達的輸入的最後一行。
答案4
在某些系統(例如 MS-Dos)上,管道是透過將第一個命令的輸出複製到文件,然後執行第二個命令從該文件讀取來實現的。 Unix 不這樣做。
在 Unix 上它就像一條生產線。每個階段同時工作,讀取輸入並產生輸出。如果流程 A 的生產速度快於流程 B 的消耗速度,則流程 A 和 B 之間會累積庫存。當沒有庫存可供 B 處理時,B 就會暫停。流程會暫停和取消暫停,以保持較低的庫存水準。
這些程式中的程式碼不關心這些。它只是讀取輸入並寫入輸出。如果它在資料可用之前嘗試讀取,或者在下一個進程準備好之前嘗試寫入,則作業系統將暫停它,直到準備好。
當沒有更多內容可供讀取時(並且沒有更多內容正在讀取),讀取器將收到文件結尾並將退出。這反過來會在下一個進程中觸發文件結束。