我正在尝试使用一些旧的 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/秒卡),这两种卡都会产生相同的错误。
我尝试过一根带有终结器的电缆(即使在 Ultra320 卡上也能达到 40MB/秒),然后是一根双连接器电缆,这意味着我只能连接一个驱动器,因此我启用了驱动器上的“术语电源”跳线,这让我到了 Ultra160(尽管驱动器和控制器都是 Ultra320),但这一切都没有改变任何东西,并且在整个过程中,当我尝试从驱动器读取数据时,仍然遇到相同的错误。
我从 Linux 内核 4.10.13 降级到 4.4.3(本机上的先前版本),错误消息从“无法分配内存”更改为“输入/输出错误”,但问题仍然相同。
有什么想法可能导致此错误吗?
编辑:40MB/秒的问题是因为我使用 SE 主动终结器而引起的。当我用 LVD 终结器替换它后,速度就达到了 Ultra160。我想我需要新的电缆来连接 Ultra320,但这现在是磁带带宽的两倍(最大 80MB/秒),所以暂时对我来说没问题。但与错误消息没有区别。
答案1
好吧,我想我已经解决了这个问题。
长话短说
使用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 内存缓冲区,您将能够一次从具有 512 字节块的磁带中读取最多 32 个块,因为这些块正好适合 16kB 缓冲区。但是您将无法阅读任何事物从具有 64kB 块的磁带中读取数据,因为您甚至无法将其中一个块放入 16kB 缓冲区中,并且请记住,您一次无法读取小于一整个块的任何内容。
如果您尝试执行此操作,则使用对于一个块来说太小的缓冲区,驱动程序(在本例中为st
SCSI 磁带驱动程序)将返回一个内存分配错误代码,告知您读取缓冲区太小,甚至无法容纳单块。
更复杂的是,一些磁带驱动器(显然是我正在使用的 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
也工作得很好,所以如果值太大,在合理范围内,似乎并不重要。