Contents

在Unix中DD命令简介

1. 概述

在古老的 Unix 命令dd 中,磁盘/数据复制器(或者,有时,磁盘销毁器)允许我们将原始数据从一个源复制到另一个源。它不用于复制像*cp 这样的单个文件。*相反,它允许我们读取和写入块设备——例如,物理硬盘驱动器。

就其界面而言,它也是 Unix/Linux 工具包的一个不同寻常的部分。大多数命令使用诸如*–blocksize 1M之类的选项指定选项,但 dd 更喜欢key=value样式,如bs=1M*。

dd在 Unix 的早期有很多用途,例如:

  • 使用磁带备份
  • 在使用不同字节顺序的系统之间进行转换
  • 备份引导扇区
  • 从无法访问或崩溃的文件系统中读取数据。

在本教程中,我们将介绍一些我们今天仍然使用dd的原因,例如:

  • 将映像文件复制到 USB 引导驱动器
  • 创建磁盘映像文件以用作沙箱。

2. dd和 Unix 的“一切都是文件”

与许多 Unix 和 Linux 命令一样,dd 从标准输入读取并写入标准输出

它还依赖于 Unix 模型,大多数东西都可以被视为文件;也就是说,它们需要打开,可以读取,写入,然后关闭。

这个概念产生了*/dev/目录的sd**文件,它们代表我们的原始磁盘。只要有足够的权限,程序就可以对它们进行读写。当然,我们通常想把它留给我们的文件系统

如果我们希望dd从磁盘设备或特殊设备(例如*/dev/urandom/dev/zero*)获取其输入或输出,我们使用ifof选项(分别为输入和输出文件)。我们可以将所有内容从一个磁盘复制到另一个磁盘,包括分区表、引导扇区和文件系统中文件的访问时间——事实上,所有内容都在原始数据级别。

如果我们有两个相同的磁盘,Linux 称为*/dev/sda/dev/sdb*,我们可以使用以下命令使sdb成为sda的副本:

dd if=/dev/sda of=/dev/sdb

这会慢慢地将所有内容从一个磁盘传输到另一个磁盘,从而 消除输出文件磁盘sdb上的所有内容。那里的一切都会消失,取而代之的是sda上的东西。

请注意,如果sdb是比sba更大的磁盘,我们的文件系统将不会调整大小。它将保持精确的原始二进制副本,因此它看起来像较小的源磁盘。

3. 什么时候不使用dd

让我们记住,仅仅因为我们可以使用一个工具并不意味着我们应该使用它。也许我们在 Windows 上并想创建一个可引导的 Linux USB 驱动器。我们可以使用dd,但最好使用Rufus 之类的东西。

或者也许我们想备份我们的文件。dd可以一次性完成,但是像rsync 甚至tar 这样的东西可能更接近我们的需要。

也许我们想将整个块设备从一个复制到另一个或将其备份为图像文件。使用Clonezilla 之类的东西可能更快、更安全。

同样,也许我们希望安全地擦除驱动器的所有敏感内容,超出取证的任何恢复能力。我们可以使用dd将零写入每个块。但是使用硬盘 制造商的实用程序 可能会更快、更安全。

但是在某些情况下dd是适合这项工作的工具。

4. 将映像文件复制到 USB 驱动器

当我们在物理机上安装新操作系统时,我们可能会使用可引导的 USB 磁盘。我们可以从我们的分发供应商处下载磁盘映像。(这些在安装虚拟机时也有效。)使用磁盘映像为我们省去了分区的麻烦,并使我们的 USB 安装程序磁盘可以手动启动

4.1. 在*/dev/*中识别我们的 USB 驱动器设备

如果我们的系统自动挂载了我们的 USB 驱动器,我们将通过df 看到它:

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           1.4G  2.4M  1.4G   1% /run
/dev/sda2       228G  193G   24G  90% /
tmpfs           6.9G   62M  6.8G   1% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           4.0M     0  4.0M   0% /sys/fs/cgroup
/dev/sda1       511M  6.3M  505M   2% /boot/efi
tmpfs           1.4G  6.3M  1.4G   1% /run/user/1000
/dev/sdb1       466G  426G   41G  92% /media/a/9EE8E134E8E10AFB1
/dev/sdc1        15G  5.1G  9.9G  34% /media/a/ESD-USB

在这里,我们看到我们的 USB 驱动器是设备*/dev/sdc*,它的第一个分区当前安装在*/media/a/ESD-USB/*下。

如果它没有挂载但已成功插入,lsblk 将向我们显示块设备:

$ lsblk -e7
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda      8:0    0 232.9G  0 disk 
├─sda1   8:1    0   512M  0 part /boot/efi
└─sda2   8:2    0 232.4G  0 part /
sdb      8:16   1 465.8G  0 disk 
├─sdb1   8:17   1 465.3G  0 part /media/a/9EE8E134E8E10AFB1
└─sdb2   8:18   1   480M  0 part 
sdc      8:32   1  14.9G  0 disk 
└─sdc1   8:33   1  14.9G  0 part 

或者,如果它可用,我们可以使用gnome-disks工具。(在 GNOME GUI 中,它简称为Disks ):

/uploads/dd_command/1.png

所以,我们知道我们想在*/dev/sdc上写入我们的磁盘映像。(/dev/sdc1*指的是第一个分区,但我们想接管整个磁盘。)

**请记住,这将彻底清除我们的 U 盘!**所以,让我们首先仔细检查一下我们是否已经备份了我们想要从那里保留的任何内容。

然后,是时候运行dd了!

4.2. 复制文件图像

接下来,我们获取要复制的 ISO 磁盘映像文件。这可能是 Linux 或 FreeBSD 甚至是其他东西。

我们运行dd命令将原始数据从我们的图像文件复制到块设备。为了确保我们没有写完我们不想丢失的东西,我们将双重和三重检查我们是否拥有正确的*“of”*选项设备:

# dd if=FreeBSD-12.1-RELEASE-amd64-memstick.img of=/dev/sdc bs=1M conv=sync

一块一块地,这是我们使用该命令所做的事情:

  1. 我们正在运行dd
  2. 如果选项指向我们下载的磁盘映像文件,则输入文件(FreeBSD 12.1)
  3. 以及指向我们的磁盘块设备的选项输出文件。同样,我们确保这是正确的,否则我们可能会淘汰我们的系统!
  4. 块大小bs选项以 1 兆字节的块复制。如果我们忽略它,它将默认为 512 字节并且需要更长的时间。
  5. 最后,conv=sync 告诉dd它必须在完成时同步数据和元数据的读写。我们也可以使用同步命令来做到这一点。

另一个流行的选项是status=progress,它在复制磁盘映像时提供视觉进度更新。没有它,dd在复制时会静静地坐在那里(有时会持续几分钟)。

完成后,我们就有了一个可以启动和安装的 USB 驱动器!

5. 创建我们自己的磁盘映像文件

我们也可以在试验文件系统时使用dd 。与虚拟机如何使用虚拟磁盘文件类似,我们可以创建空文件并使用它们来尝试文件系统和 RAID 配置。

让我们创建两个 10 GB 的文件并告诉 ZFS 像使用镜像磁盘驱动器一样使用它们。在我们对实际系统进行不可撤销的更改之前,这是一种很好的练习方式。

首先,我们使用dd将 1 GB 的零复制十次到我们的文件中:

$ dd if=/dev/zero of=first.img bs=1G count=10
$ dd if=/dev/zero of=second.img bs=1G count=10
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.219776 s, 477 MB/s

新选项count符合我们的预期:它多次重复复制操作。没有它,dd将继续运行,直到到达文件末尾。但是*/dev/zero*是一个永无止境的 ‘0’ 喷泉,所以我们需要指定。

现在,我们可以将这些大而空的文件用作块设备——例如,使用mkfs格式化它们并挂载它们。

在我们的 ZFS 实验中,我们可以创建一个新的zpool

# zpool create demo mirror ~/first.img ~/second.img

瞧,我们将在*/demo*上安装一个实验性 ZFS 存储池。

6. 历史背景

在最简单的形式中,dd确实是一个数据复制器。它将数据接收到标准输入并复制到标准输出:

$ echo "test" | dd
test
0+1 records in
0+1 records out
5 bytes copied, 4.6881e-05 s, 107 kB/s

在这种情况下,它复制了test中的四个字符以及换行符。正如 C 程序员会告诉我们的,一个char是一个字节长。

但是在 Unix 管道和转换的传统中,我们可以要求dd转换它复制的字节。这里有些例子:

$ echo "test" | dd conv=swab
etts
0+1 records in
0+1 records out
5 bytes copied, 5.9189e-05 s, 84.5 kB/s

如果我们告诉dd使用swab转换,它将交换字节。我们发送*“test”并输出“etts”*。如果我们有不同字节序 格式的文件或原始数据,这可能会很有用。但这些都不像以前那么常见了。

我们还可以在构成现在标准Unicode 的第一部分的ASCII 文本编码之间进行转换。ASCII 曾经有一个来自 IBM 的共同竞争标准,它的名字很难发音,EBCDIC

dd让我们可以轻松地在 IBM 大型机的秘密语言和老百姓的语言之间来回翻译:

$ echo "test" | dd conv=ibm > ebcdic.txt
0+1 records in
0+1 records out
5 bytes copied, 0.00010055 s, 49.7 kB/s
$ cat ebcdic.txt 
����%
$ cat ebcdic.txt | dd conv=ascii
test
0+1 records in
0+1 records out
5 bytes copied, 5.65e-05 s, 88.5 kB/s

但这些大多是历史。