Tar

從 SCSI 磁帶讀取時“無法分配記憶體”

  • May 21, 2017

我正在嘗試使用一些舊的 SCSI 磁帶驅動器,並且我已經成功地將一些數據寫入磁帶,但我正在努力嘗試再次讀取它。

# tar tvf /dev/st0
tar: /dev/st0: Cannot read: Cannot allocate memory
tar: At beginning of tape, quitting now
tar: Error is not recoverable: exiting now

# dd if=/dev/st0 of=test
dd: error reading '/dev/st0': Cannot allocate memory
0+0 records in
0+0 records out
0 bytes copied, 3.20155 s, 0.0 kB/s

在這些命令之後,dmesg說:

st 10:0:3:0: [st0] Block limits 1 - 16777215 bytes.
st 10:0:3:0: [st0] Failed to read 65536 byte block with 512 byte transfer.
st 10:0:3:0: [st0] Failed to read 131072 byte block with 65536 byte transfer.
st 10:0:3:0: [st0] Failed to read 65536 byte block with 10240 byte transfer.
st 10:0:3:0: [st0] Failed to read 94208 byte block with 69632 byte transfer.
st 10:0:3:0: [st0] Failed to read 65536 byte block with 10240 byte transfer.
st 10:0:3:0: [st0] Failed to read 65536 byte block with 512 byte transfer.

其中大部分是因為我正在使用該tar -b選項測試不同的塊大小,但這些都沒有任何效果。

有時我能夠從磁帶上的第一個塊中讀取幾 kB 的數據(tar 可以提取直到數據中斷),但通常它會失敗,根本沒有讀取任何數據。

我已經(顯然)成功地將數據寫入磁帶,將磁帶移動到另一個驅動器,尋找到數據的末尾,然後寫入更多,因此將數據寫入驅動器似乎沒有困難,只需將其讀回再次。

我正在使用兩個 LTO-3 驅動器。一種是半高HP Ultrium 920,另一種是全高HP Ultrium 960。兩者都有這個問題。我嘗試過使用兩種不同的 SCSI 卡(LSI Logic Ultra320 卡和 Adaptec Ultra2/SE 40MB/sec 卡),它們都會產生相同的錯誤。

我嘗試了一根帶有終結器的電纜(即使在 Ultra320 卡上也給了我 40MB/秒),然後是雙連接器電纜,這意味著我只能連接一個驅動器,所以我啟用了驅動器上的“長期電源”跳線,這讓我使用 Ultra160(即使驅動器和控制器都是 Ultra320),但這一切都沒有改變任何東西,並且在整個過程中,我在嘗試從驅動器讀取時仍然遇到相同的錯誤。

我從 Linux 核心 4.10.13 降級到 4.4.3(這台機器上的舊版本),錯誤消息從“無法分配記憶體”變為“輸入/輸出錯誤”,但問題仍然存在。

任何想法可能導致此錯誤?

編輯: 40MB/秒的問題是因為我使用的是 SE 主動終結器。一旦我用一個 LVD 終結器替換它,速度就會上升到 Ultra160。我想我需要新的電纜來連接 Ultra320,但現在這是磁帶頻寬的兩倍(最大 80MB/秒),所以我暫時還可以。不過,與錯誤消息沒有區別。

好的,我想我已經解決了這個問題。

TL; 博士

改為使用dd大塊大小從磁帶讀取:

dd if=/dev/nst0 bs=1M | tar tvf -

背景

當您寫入磁帶時,數據以稱為塊的單位寫入。這些就像硬碟上的扇區。多年來,硬碟塊固定為 512 字節,直到最近才轉移到 4096 字節塊,而磁帶塊可以設置為您喜歡的任何大小。

您希望使用的塊大小使用以下setblk子命令設置mt-st

mt-st -f /dev/nst0 setblk 512    # Use 512-byte blocks
mt-st -f /dev/nst0 setblk 64k    # Use 65536-byte blocks

當您向驅動器發出讀取操作時,它將以塊大小的塊返回數據。您不能讀取半個塊 - 您可以從磁帶讀取的最小數據量是一個塊,這當然可以是任意數量的實際字節,具體取決於塊大小。

這意味著如果您使用的程序提供 16kB 記憶體緩衝區,您將能夠一次從磁帶讀取多達 32 個塊,其中 512 字節塊正好適合 16kB 緩衝區。但是,您將無法從 64kB 塊的磁帶中讀取任何內容,因為您甚至無法將其中一個塊放入 16kB 緩衝區中,並且請記住,您一次只能讀取不到一個完整塊的任何內容。

如果您嘗試這樣做,通過使用對於一個塊來說太小的緩衝區,驅動程序(在本例中為stSCSI 磁帶驅動程序)將返回一個記憶體分配錯誤程式碼,以告知您您的讀取緩衝區太小而無法容納單塊。

更複雜的是,一些磁帶驅動器(顯然我正在使用的 LTO 驅動器)也支持可變大小的塊。這意味著塊大小由每個寫入操作的大小決定,並且每個塊的大小可以與上一個不同。

此模式設置為塊大小為零:

mt-st -f /dev/nst0 setblk 0    # Use variable-sized blocks

這也是預設選項 - 大概,我在這裡猜測 - 錯誤配置的程序會浪費更少的空間。例如,如果您設置了 4k 塊,但您的程序一次只以 512 字節為單位寫入數據,則每個 512 字節數據塊可能會佔用磁帶上的 4k。

原因

如果您現在將所有內容放在一起,您將意識到磁帶可以假設有一個 512 字節的塊,然後是一個 64kB 的塊。如果程序正在讀取帶有 16kB 緩衝區的磁帶,它將成功讀取第一個塊,但是當它嘗試讀取更多時,它將無法在其緩衝區中容納下一個 64kB 塊,因此驅動程序將返回一個錯誤。

這解釋了為什麼我Cannot allocate memory大部分時間都遇到錯誤,偶爾我能夠得到 tar 來提取前幾個文件,但後來我又遇到了錯誤。我沒有設置塊大小,mt-st因此在寫入磁帶時它預設為可變大小的塊,現在tar使用的緩衝區太小而無法讀取其中一些塊。

tar幾個選項可用於設置自己的內部塊大小,即--blocking-factor--read-full-records--record-size,但是這些僅在tar用於直接讀取和寫入磁帶時才有效。

因為我通過mbuffer程序寫入磁帶以減少磁帶擦鞋,所以tar存檔中的塊大小不再匹配磁帶上的塊大小。這意味著--blocking-factor幾乎沒有效果——它將允許讀取磁帶上的第一個塊,其中包括一個標題,該標題告訴tar阻塞因子應該是什麼,其中它切換到那個並忽略命令行上給出的值。這意味著無法再讀取第二個和後續塊!

解決方案

解決方案是使用另一個程序從磁帶中讀取 - 一個可以將讀取緩衝區大小設置為足夠大的值以容納我們可能看到的最大塊。

dd適用於此,並且在緊要關頭這有效:

dd if=/dev/nst0 bs=256k | tar tvf -

如果您的磁帶上有較大的塊,您可能需要增加256k,但這對我有用。 1M也可以正常工作,因此在合理範圍內,值是否太大似乎並不重要。

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