我有一个来自 4GB SD 卡的 DD 映像,该卡有两个分区,这两个分区仅使用大约 800 MB,因此我希望减小 img 文件的大小。
有人知道如何从 img 文件中删除“可用空间”吗?
答案1
首先确保可用空间确实是空的,并且不包含已删除文件的剩余空间。
对于较新的内核(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
否则,实现此目的的一个简单方法是在磁盘上创建一个仅包含空字节的巨大文件,然后删除它。
# 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
gzip
然后使用或 之类的工具对其进行压缩xz
。即使在最低压缩级别下,一长串零也能很好地压缩:
# ls -s
4096M foo.img
# gzip foo.img
# ls -s
11M foo.img.gz
请注意,将图像写回磁盘时必须解压缩图像。这将“实时”解压缩它:
# cat foo.img.gz | gunzip | dd of=/dev/sda
注意输出设备(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
但如果要再次将其写入真实磁盘,则必须将其转换回原始图像。
答案2
使用resize2fs
更加容易
resize2fs -M xxx.img
您将被要求首先执行 e2fsck - 因此:
e2fsck -f -y xxx.img
(图像不得被安装!)
注意:这仅当图像属于单个分区时才有效,如果它是具有多个分区的整个块设备,请参见上面的答案...
答案3
我最初发布了相同的答案这里,在 StackExchange Ask Ubuntu 上。我在这里重新提出相同的答案,它可能会很有用。
关键信息是命令的使用truncate
。遵循完整的解决方案以免丢失答案。
初步步骤包括在您的 PC 上克隆 SD 卡:
用于
lsblk
查看哪些设备可用以及它们的分区是否已挂载卸载您要在电脑上复制的设备的所有分区。例如:
umount /dev/sdc1 umount /dev/sdc2
创建整个 SD 卡的副本,并卸载所有分区
dd if=/dev/sdc of=/path/to/file/myimage.img
在 Linux 上缩小图像
问题背景:
具有myimage.img
比硬件支持更大的尺寸(如果尺寸较小则应该没有问题;但是,使用相同的策略,您可以更好地将图像放入硬件支持中)。
秘诀是使用标准 Linux 工具和仪器:GPartedfdisk
和truncate
。
要求:
- Linux 电脑
- 您
.img
想要缩小的(myimage.img
在此示例中)
创建环回设备:
GParted 是一款通常用于管理分区表和文件系统的应用程序。为了收缩图像,GParted 将在答案的第一部分中使用。
GParted 操作的是设备,而不是图像等简单文件。这就是为什么我们首先需要为图像创建一个设备。我们使用 Linux 的环回功能来做到这一点。
让我们启用环回:
sudo modprobe loop
让我们请求一个新的(免费)环回设备:
sudo losetup -f
该命令返回空闲环回设备的路径:
/dev/loop0
让我们创建一个图像的设备:
sudo losetup /dev/loop0 myimage.img
该设备/dev/loop0
代表myimage.img
。我们想要访问映像上的分区,因此我们需要让内核也加载这些分区:
sudo partprobe /dev/loop0
这应该会给我们设备/dev/loop0p1
,它代表中的第一个分区myimage.img
。我们不需要直接使用此设备,但 GParted 需要它。
使用 GParted 调整分区大小:
让我们使用 GParted 加载新设备:
sudo gparted /dev/loop0
当 GParted 应用程序打开时,它应该出现类似以下内容的窗口:
现在请注意以下几点:
- 有一个分区。
- 分区分配整个磁盘/设备/图像。
- 分区已部分填充。
我们希望调整此分区的大小以适合其内容,但不能超过这个大小。
选择分区并点击调整大小/移动。将弹出类似以下的窗口:
尽可能将右侧栏拖到左侧。
请注意,有时 GParted 需要额外的几 MB 空间来放置一些与文件系统相关的数据。您可以按几次新大小框上的向上箭头来执行此操作。例如,我按了 10 次(=10MiB)以使 FAT32 正常工作。对于 NTFS,您可能根本不需要这样做。
最后按“调整大小/移动”。您将返回到 GParted 窗口。这次它看起来类似于以下内容:
请注意,磁盘中有一部分未分配。分区不会使用磁盘的这一部分,因此我们可以稍后从映像中删除这一部分。GParted 是一个磁盘工具,因此它不会缩小映像,只会缩小分区,我们必须自己缩小映像。
按下ApplyGParted。它现在将移动文件并最终缩小分区,因此可能需要一两分钟,但大多数情况下它会很快完成。然后关闭 GParted。
现在我们不再需要环回设备,因此卸载它:
sudo losetup -d /dev/loop0
削减图像:
现在我们已经在映像的开头有了所有重要的数据,是时候删除未分配的部分了。我们首先需要知道分区在哪里结束以及未分配部分在哪里开始。我们使用以下命令执行此操作fdisk
:
fdisk -l myimage.img
在这里我们将看到类似以下内容的输出:
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
请注意输出中的两件事:
- 分区结束于块 9181183(如下所示
End
) - 块大小为 512 字节(显示为 扇区
1 * 512
)
我们将在示例的其余部分使用这些数字。块大小 (512) 通常相同,但结束块 (9181183) 会有所不同。这些数字表示分区以字节 9181183 结束512 字节。该字节之后是未分配部分。只有前 9181183512 字节对于我们的图像很有用。
接下来,我们将映像文件缩小到刚好可以包含分区的大小。为此,我们将使用命令truncate
(感谢 uggla!)。使用 truncate 命令需要提供文件的大小(以字节为单位)。最后一个块是 9181183,块号从 0 开始。这意味着我们需要 (9181183+1)*512 字节。这很重要,否则分区将不适合映像。所以现在我们使用 truncate 并进行计算:
truncate --size=$[(9181183+1)*512] myimage.img
答案4
我在我的 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