如何调整用dd创建的img文件的大小?

A_P*_*ine 31 dd img

我有一个来自 4GB SD 卡的 DD 映像,它有两个分区,这两个分区只使用了大约 800 MB,因此我希望减小 img 文件的大小。

有谁知道从 img 文件中删除“可用空间”的方法?

小智 27

使用resize2fs要容易得多

resize2fs -M xxx.img
Run Code Online (Sandbox Code Playgroud)

您将首先被要求 e2fsck - 所以:

e2fsck -f -y xxx.img
Run Code Online (Sandbox Code Playgroud)

(不得安装图像!)

注意:这仅适用于图像是单个分区的情况,如果它是具有多个分区的整个块设备,请参见上面的答案...

  • 这是一个非常好的答案,但 `resize2fs` 仅适用于 `extN` 类型的分区。其他类型的分区将需要其他工具。 (2认同)

use*_*686 13

首先确保可用空间实际上是空的,并且不包含已删除文件的剩余部分。

随着最近的内核(3.2或更高),这是最简单的通过安装环图像的每个分区,然后发出一个这样做丢弃使用fstrim上挂载点。这在循环设备上的工作方式与 SSD 上的 TRIM 类似;未使用的区域用零替换,底层.img文件变得稀疏。

# losetup --find --partscan foo.img
# lsblk
NAME      MAJ:MIN RM    SIZE RO TYPE MOUNTPOINT
loop0       7:0    0   4096M  0 loop 
??loop0p1 259:0    0   2048M  0 loop 
??loop0p2 259:1    0   2048M  0 loop 
# for part in /dev/loop0p*; do
    mount $part /mnt
    fstrim -v /mnt
    umount /mnt
  done
/mnt: 2xxx MiB trimmed
/mnt: 2xxx MiB trimmed
# losetup --detach /dev/loop0
Run Code Online (Sandbox Code Playgroud)

否则,实现此目的的一种简单方法是在磁盘上创建一个仅包含空字节的大文件,然后将其删除。

# losetup --find --partscan foo.img
# lsblk
NAME      MAJ:MIN RM    SIZE RO TYPE MOUNTPOINT
loop0       7:0    0   4096M  0 loop 
??loop0p1 259:0    0   2048M  0 loop 
??loop0p2 259:1    0   2048M  0 loop 
# for part in /dev/loop0p*; do
    mount $part /mnt
    dd if=/dev/zero of=/mnt/filler conv=fsync bs=1M
    rm /mnt/filler
    umount /mnt
  done
dd: error writing ‘/mnt/filler’: No space left on device
dd: error writing ‘/mnt/filler’: No space left on device
# losetup --detach /dev/loop0
Run Code Online (Sandbox Code Playgroud)

然后用gzip或 之类的工具压缩它xz。即使在最低压缩级别,一长串零也可以很好地压缩:

# ls -s
4096M foo.img
# gzip foo.img
# ls -s
11M foo.img.gz
Run Code Online (Sandbox Code Playgroud)

请注意,将图像写回磁盘时必须解压缩图像。这将解压缩它“实时”:

# cat foo.img.gz | gunzip | dd of=/dev/sda
Run Code Online (Sandbox Code Playgroud)

请注意,输出设备 (sda)的大小必须足以容纳原始图像,否则数据将丢失或损坏。


如果您想继续使用映像(例如使用虚拟机),另一种方法是将原始映像转换为虚拟化软件使用的一种映像格式;例如 Qemu 的 qcow2、VirtualBox 的 VDI 或 VMware 的 VMDK。

请注意,这仍然需要您通过使用上述方法清理可用空间来准备映像。

# qemu-img convert -f raw -O qcow2 foo.img foo.qcow

# qemu-img convert -f raw -O vmdk foo.img foo.vmdk
Run Code Online (Sandbox Code Playgroud)

但是,如果要再次将其写入真实磁盘,则必须将其转换回原始图像。


小智 7

我最初发布了同样的答案在这里,在StackExchange提出的Ubuntu。我在这里重新提出相同的答案,它可能很有用。

关键信息是命令的使用truncate。为了不丢失答案,请遵循完整的解决方案。

初步步骤包括在您的 PC 中克隆 SD 卡:

  1. 用于lsblk查看哪些设备可用以及它们的分区是否已安装

  2. 卸载要复制到 PC 上的设备的所有分区。例如:

     umount /dev/sdc1
     umount /dev/sdc2
    
    Run Code Online (Sandbox Code Playgroud)
  3. 创建整个 SD 卡的副本,并卸载所有分区

     dd if=/dev/sdc of=/path/to/file/myimage.img
    
    Run Code Online (Sandbox Code Playgroud)

在 Linux 上缩小图像

问题的背景

有一个myimage.img更大的硬件支持(如果它更小应该没有问题;但是,使用相同的策略,您可以更好地将图像放入硬件支持中)。

秘诀是使用标准的 Linux 工具和工具:GPartedfdisktruncate.

要求

  • 一台 Linux 电脑
  • .img要收缩(myimage.img在这个例子中)

创建环回设备

GParted 是一个通常用于管理分区表和文件系统的应用程序。为了缩小图像,将在答案的第一部分使用 GParted。

GParted 在设备上运行,而不是像图像这样的简单文件。这就是为什么我们首先需要为图像创建一个设备。我们使用 Linux 的环回功能来做到这一点。

让我们启用启用环回:

sudo modprobe loop
Run Code Online (Sandbox Code Playgroud)

让我们请求一个新的(免费)环回设备:

sudo losetup -f
Run Code Online (Sandbox Code Playgroud)

该命令返回自由环回设备的路径:

/dev/loop0
Run Code Online (Sandbox Code Playgroud)

让我们创建一个图像设备:

sudo losetup /dev/loop0 myimage.img
Run Code Online (Sandbox Code Playgroud)

该设备/dev/loop0代表myimage.img。我们想要访问映像上的分区,因此我们需要让内核也加载这些分区:

sudo partprobe /dev/loop0
Run Code Online (Sandbox Code Playgroud)

这应该给我们设备/dev/loop0p1,它代表myimage.img. 我们并不直接需要这个设备,但是 GParted 需要它。

使用 GParted 调整分区大小

让我们使用 GParted 加载新设备:

sudo gparted /dev/loop0
Run Code Online (Sandbox Code Playgroud)

当 GParted 应用程序打开时,它应该会出现一个类似于以下内容的窗口:

截屏

现在注意几点:

  • 有一个分区。
  • 该分区分配整个磁盘/设备/映像。
  • 分区被部分填充。

我们想调整这个分区的大小,使其适合它的内容,但不能更多。

选择分区并单击调整大小/移动。会弹出类似如下的窗口:

对话框截图

尽可能向左拖动右侧栏。

请注意,有时 GParted 需要额外几 MB 来放置一些与文件系统相关的数据。您可以按几次新尺寸框上的向上箭头来执行此操作。例如,我按 10 次 (=10MiB) 使 FAT32 工作。对于 NTFS,您可能根本不需要。

最后按调整大小/移动。您将返回到 GParted 窗口。这一次,它将类似于以下内容:

右侧未分配空间

请注意,有一部分磁盘未分配。磁盘的这部分将不会被分区使用,因此我们可以稍后将这部分从映像中删除。GParted 是一个磁盘工具,所以它不缩小图像,只缩小分区,我们必须自己缩小图像。

ApplyGParted。它现在将移动文件并最终缩小分区,因此可能需要一两分钟,但大多数情况下它会很快完成。然后关闭 GParted。

现在我们不再需要环回设备,所以卸载它:

sudo losetup -d /dev/loop0
Run Code Online (Sandbox Code Playgroud)

剃须图像

现在我们在图像的开头拥有了所有重要的数据,是时候去除未分配的部分了。我们首先需要知道我们的分区在哪里结束以及未分配部分从哪里开始。我们使用fdisk

fdisk -l myimage.img
Run Code Online (Sandbox Code Playgroud)

在这里,我们将看到类似于以下内容的输出:

Disk myimage.img: 6144 MB, 6144000000 bytes, 12000000 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000ea37d

      Device Boot      Start         End      Blocks   Id  System
myimage.img1            2048     9181183     4589568    b  W95 FAT32
Run Code Online (Sandbox Code Playgroud)

请注意输出中的两件事:

  • 分区结束于块 9181183(如下所示End
  • 块大小为 512 字节(显示为 的扇区1 * 512

我们将在示例的其余部分使用这些数字。块大小 (512) 通常相同,但结束块 (9181183) 会有所不同。数字表示分区以文件的字节 9181183 512结束在该字节之后是未分配部分。只有前 9181183 512 个字节对我们的图像有用。

接下来,我们将图像文件缩小到可以只包含分区的大小。为此,我们将使用truncate命令(感谢 uggla!)。使用 truncate 命令需要以字节为单位提供文件的大小。最后一个块是 9181183,块号从 0 开始。这意味着我们需要 (9181183+1)*512 个字节。这很重要,否则分区将不适合图像。所以现在我们使用 truncate 进行计算:

truncate --size=$[(9181183+1)*512] myimage.img
Run Code Online (Sandbox Code Playgroud)


小智 6

我在我的 Ubuntu 16.10 计算机上使用了 gparted 方法:

  1. 将 img 文件映射到下一个可用的循环分区sudo losetup -f --partscan file.img

  2. 检查lsblk您的图像文件映射到哪个循环驱动器,例如/dev/loop0

  3. 执行sudo gparted /dev/loop0

  4. 根据需要缩小循环分区;请确保卸载这些分区。

  5. 执行fdisk /dev/loop0,然后输入p,这将显示各个分区的块大小和结束块号。

  6. 执行sudo dd if=/dev/loop0 of=shrunk_image_file.img,将选项应用到该命令bs=[BlockSize]count=[EndBlockNumberOfLastLoopPartition+1]您将获得一个缩小且大小合适的图像文件。可以选择添加status=progress以查看其工作情况。

  7. 完成后取消映射图像文件sudo losetup -d loop0


小智 5

我也用qemu-img尝试过,它的作用就像一个魅力:

qemu-img resize test.img 2G
Run Code Online (Sandbox Code Playgroud)

我们正在调整大小test.img以使其成为2G (2GB)。

对我来说完美无缺。