我有一个来自 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)
(不得安装图像!)
注意:这仅适用于图像是单个分区的情况,如果它是具有多个分区的整个块设备,请参见上面的答案...
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 卡:
用于lsblk查看哪些设备可用以及它们的分区是否已安装
卸载要复制到 PC 上的设备的所有分区。例如:
umount /dev/sdc1
umount /dev/sdc2
Run Code Online (Sandbox Code Playgroud)
创建整个 SD 卡的副本,并卸载所有分区
dd if=/dev/sdc of=/path/to/file/myimage.img
Run Code Online (Sandbox Code Playgroud)
问题的背景:
有一个myimage.img更大的硬件支持(如果它更小应该没有问题;但是,使用相同的策略,您可以更好地将图像放入硬件支持中)。
秘诀是使用标准的 Linux 工具和工具:GPartedfdisk和truncate.
要求:
.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)
请注意输出中的两件事:
End)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 方法:
将 img 文件映射到下一个可用的循环分区sudo losetup -f --partscan file.img
检查lsblk您的图像文件映射到哪个循环驱动器,例如/dev/loop0
执行sudo gparted /dev/loop0
根据需要缩小循环分区;请确保卸载这些分区。
执行fdisk /dev/loop0,然后输入p,这将显示各个分区的块大小和结束块号。
执行sudo dd if=/dev/loop0 of=shrunk_image_file.img,将选项应用到该命令bs=[BlockSize],count=[EndBlockNumberOfLastLoopPartition+1]您将获得一个缩小且大小合适的图像文件。可以选择添加status=progress以查看其工作情况。
完成后取消映射图像文件sudo losetup -d loop0
小智 5
我也用qemu-img尝试过,它的作用就像一个魅力:
qemu-img resize test.img 2G
Run Code Online (Sandbox Code Playgroud)
我们正在调整大小test.img以使其成为2G (2GB)。
对我来说完美无缺。