原理分析:使用 dd 跳过开头若干字节快速拷贝文件

2022-10-27 15:57:21 浏览数 (2)

这篇文章是来自我在 0xffff.one 上的一个帖子 https://0xffff.one/d/900 的回复。 原帖内容: 在折腾一个超大的备份文件,需要把它的前 41 个字节删除掉,没有 WinHEX,想着用 dd 命令来实现 一开始这么干,发现速度奇慢,5分钟过去才复制40MB…

代码语言:javascript复制
dd if=input.bak bs=1 skip=41 > result.bak   

google 一波发现一老哥的操作,配合 dd 和 cat 实现快速拷贝的功能,有些佩服。

代码语言:javascript复制
{  dd bs=41 skip=1 count=0; cat; } < input.bak > result.bak   

原帖: https://unix.stackexchange.com/questions/6852/best-way-to-remove-bytes-from-the-start-of-a-file

干了啥

看一下一开始的指令:

代码语言:javascript复制
dd if=input.bak bs=1 skip=41 > result.bak

为什么这个指令会慢呢?首先一点背景知识:

  计算机中每一向硬盘读取和写入数据,无论读多小的数据量,都至少需要花一段常数时间(称为overhead)。 (就像你去超市买鸡蛋一样,无论你一次只买一个,还是一千个,你都至少要花从家走到超市,再从超市走回家的时间。)

  就好比你现在要100个鸡蛋,但是你去超市一趟只买一颗鸡蛋的话,你就要来回跑100次一样。用 dd 拷文件也是同样的道理,如果一次只跑去给硬盘要一个字节,一个文件就要来回跑特别多次,花费的时间就会特别长。

  为了解决这个问题,dd 在读文件的时候,会将文件切分成大小固定的一小块一小块 (block),每次向硬盘要数据就一次性要一个“块”的大小(默认 512 个字节),也就是说,每次费那么大功夫跑过去,那就干脆多要一点数据,同样大小的文件不就可以少跑很多次了吗?

所以为啥慢?

  前面提到的一次拿一个分块,这个分块的具体大小,是可以通过bs=参数进行人工调节的(bs = block size, 块大小)。

  我们一开始的指令的问题就在于,我们这个指令里有一个参数bs=1,也就是告诉 dd,我想要每 1byte 就当作一个 block,这样 dd 实际上就又变回了跑一次读一个字节的傻 dd 了,所以这个指令最后就慢得出奇啦。(0.26MB/s)

大不就完事了?

路人甲:那么既然是因为块大小 bs 设太小了,那我们改大不就行了吗?

没错,理论上是这样的。划重点:理论上

如果我们只是想要单纯的把文件a.txt拷贝一份到文件b.txt,那我们的确可以直接把 bs 改大就行了:

代码语言:javascript复制
# 块大小:512Bytes,速度93MB/s
dd if=a.txt of=b.txt bs=512

# 块大小:4MB,速度1138MB/s
dd if=a.txt of=b.txt bs=4m

仅仅把每次读的数据块大小改大,就得到 12 倍的速度提升!

然鹅,为什么我们这个例子里不行呢?

代码语言:javascript复制
dd if=input.bak bs=1 skip=41 > result.bak       # 为啥不把 bs 直接改大???

因为我们除了拷贝文件外,还有另外一个要求:跳过前41个字节!

那么我们用什么方法实现跳过41个字节呢?我们一开始的指令使用了 skip=41 来实现这一要求。

好,刹住,大问题来了:skip 的单位是 block!也就是说skip=41不是指跳过41个字节,是指跳过41个 block

0 人点赞