想做个树莓派的img镜像,然而对SD卡进行全盘复制很浪费空间,且不能恢复到比现有SD卡容量小的卡上,因此探索制作小img的方法,网上看了大神制作的脚本,比如https://github.com/conanwhf/RaspberryPi-script/blob/master/rpi-backup.sh和https://github.com/elespec/rpi-backup/blob/master/rpi-backup.sh,然而做好之后在我的树莓派上无法启动。最终参考了这篇文章的方法:手动一步一步来制作备份Raspberry Pi树莓派SD卡的img映像文件(不用dd命令),感谢原作者分享。结合这些修改成了一个脚本。我的树莓派是Raspbain Stretch系统。 使用时将树莓派的SD卡插到PC上,用PC制作img镜像,最后img文件会放在~/bakcup_img/
文件夹下,测试所用PC的操作系统是Ubuntu16.04。
目录
- 查看SD卡挂载信息
- 脚本自动备份
- 手动备份
- 准备工作
- 创建空白img文件
- 格式化img文件分区并挂载
- 备份/boot
- 备份"/"
- 相应修改PARTUUID设定
- 修改 cmdline.txt 文件
- 修改fstab文件
- 收尾
查看SD卡挂载信息
PC端SD卡已经自动挂载好了,使用df -h
查看SD卡对应的设备名和使用的空间大小
df -h
设备名/dev/mmcblk0p1
和/dev/mmcblk0p2
,根据空间大小判断/dev/mmcblk0p1
为/boot
,/dev/mmcblk0p2
为/
(根)
根据原文步骤,简化了一些写成脚本,可以先按照脚本自动备份
这一节提供的脚本运行,也可以按照手动备份
的方法一步步执行。
脚本自动备份
将脚本复制到一个文本文件中,假设名字为rpi-backup.sh
,需要赋可执行权限,脚本执行有两个参数,第一个参数是树莓派SD卡/boot
分区的设备名,第二个参数是/
分区的设备名。最终img文件会生成在~/backupimg/
文件夹下。
sudo chmod +x rpi-backup.sh
./rpi-backup.sh /dev/mmcblk0p1 /dev/mmcblk0p2
脚本内容如下:
#!/bin/sh
if [ $# != 2 ]; then
echo "argument error: Usage: $0 boot_device_name root_device_name"
echo "example: $0 /dev/mmcblk0p1 /dev/mmcblk0p2"
exit 0
fi
dev_boot=$1
dev_root=$2
mounted_boot=`df -h | grep $dev_boot | awk '{print $6}'`
mounted_root=`df -h | grep $dev_root | awk '{print $6}'`
img=rpi-`date +%Y%m%d-%H%M`.img
#install tools
sudo apt-get install dosfstools dump parted kpartx
echo ===================== part 1, prepare workspace ===============================
mkdir ~/backupimg
cd ~/backupimg
echo ===================== part 2, create a new blank img ===============================
# New img file
#sudo rm $img
bootsz=`df -P | grep $dev_boot | awk '{print $2}'`
rootsz=`df -P | grep $dev_root | awk '{print $3}'`
totalsz=`echo $bootsz $rootsz | awk '{print int(($1+$2)*1.3/1024)}'`
sudo dd if=/dev/zero of=$img bs=1M count=$totalsz
#sync
echo "...created a blank img, size ${totalsz}M "
# format virtual disk
bootstart=`sudo fdisk -l | grep $dev_boot | awk '{print $2}'`
bootend=`sudo fdisk -l | grep $dev_boot | awk '{print $3}'`
rootstart=`sudo fdisk -l | grep $dev_root | awk '{print $2}'`
echo "boot: $bootstart >>> $bootend, root: $rootstart >>> end"
#有些系统 sudo fdisk -l 时boot分区的boot标记会标记为*,此时bootstart和bootend最后应改为 $3 和 $4
#rootend=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p2 | awk '{print $3}'`
sudo parted $img --script -- mklabel msdos
sudo parted $img --script -- mkpart primary fat32 ${bootstart}s ${bootend}s
sudo parted $img --script -- mkpart primary ext4 ${rootstart}s -1
echo ===================== part 3, mount img to system ===============================
loopdevice=`sudo losetup -f --show $img`
device=/dev/mapper/`sudo kpartx -va $loopdevice | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`
sleep 5
sudo mkfs.vfat ${device}p1 -n boot
sudo mkfs.ext4 ${device}p2 -L rootfs
#在backupimg文件夹下新建两个文件夹,将两个分区挂载在下面
mkdir tgt_boot tgt_Root
#这里没有使用id命令来查看uid和gid,而是假设uid和gid都和当前用户名相同
uid=`whoami`
gid=$uid
sudo mount -t vfat -o uid=${uid},gid=${gid},umask=0000 ${device}p1 ./tgt_boot/
sudo mount -t ext4 ${device}p2 ./tgt_Root/
echo ===================== part 4, backup /boot =========================
sudo cp -rfp ${mounted_boot}/* ./tgt_boot/
sync
echo "...Boot partition done"
echo ===================== part 5, backup / =========================
sudo chmod 777 ./tgt_Root
sudo chown ${uid}.${gid} tgt_Root
sudo rm -rf ./tgt_Root/*
cd tgt_Root/
# start backup
sudo dump -0uaf - ${mounted_root}/ | sudo restore -rf -
sync
echo "...Root partition done"
cd ..
echo ===================== part 6, replace PARTUUID =========================
# replace PARTUUID
opartuuidb=`sudo blkid -o export $dev_boot | grep PARTUUID`
opartuuidr=`sudo blkid -o export $dev_root | grep PARTUUID`
npartuuidb=`sudo blkid -o export ${device}p1 | grep PARTUUID`
npartuuidr=`sudo blkid -o export ${device}p2 | grep PARTUUID`
sudo sed -i "s/$opartuuidr/$npartuuidr/g" ./tgt_boot/cmdline.txt
sudo sed -i "s/$opartuuidb/$npartuuidb/g" ./tgt_Root/etc/fstab
sudo sed -i "s/$opartuuidr/$npartuuidr/g" ./tgt_Root/etc/fstab
echo "...replace PARTUUID done"
echo "remove auto generated files"
#下面内容是删除树莓派中系统自动产生的文件、临时文件等
cd ~/backupimg/tgt_Root
sudo rm -rf ./.gvfs ./dev/* ./media/* ./mnt/* ./proc/* ./run/* ./sys/* ./tmp/* ./lost+found/ ./restoresymtable
cd ..
echo ===================== part 7, unmount =========================
sudo umount tgt_boot tgt_Root
sudo kpartx -d $loopdevice
sudo losetup -d $loopdevice
rmdir tgt_boot tgt_Root
echo "==== All done. img file is under ~/backupimg/ "
手动备份
准备工作
安装工具软件
sudo apt-get install dosfstools dump parted kpartx
建立工作目录
mkdir ~/backupimg
cd ~/backupimg
创建空白img文件
源SD卡已使用空间大概3.5G,就创建了4600M大小的img文件
sudo dd if=/dev/zero of=raspberrypi.img bs=1M count=4600
原博客中有提示:
特别注意这里 bs=1M,千万不要写成1MB
1M是1024*1024 Bytes, 而1MB是1000*1000 Bytes,会造成img文件的大小不是512 bytes的整数倍,后面会报错。
查看分区表
sudo fdisk -l
给img文件分区
按照原来的起始位置设置分区
注:分区的起始扇区数都是 8192 的倍数,保证4K对齐
sudo parted raspberrypi.img --script -- mklabel msdos
sudo parted raspberrypi.img --script -- mkpart primary fat32 8192s 96042s
#第二个分区的终止点为img文件的末尾
sudo parted raspberrypi.img --script -- mkpart primary ext4 98304s -1
检查分区是否成功
sudo parted raspberrypi.img
在(parted)后面输入print free
,最后输入quit
退出。
格式化img文件分区并挂载
查看img文件对应的Loop device的设置
sudo losetup -f --show raspberrypi.img
下面的命令中相应输入
/dev/loop0
,如果不是 loop0 请做相应调整(以及以后的各个步骤里的loop0都要改变)
sudo kpartx -va /dev/loop0
终端输入ls /dev/mapper/loop0p*
应该是有/dev/mapper/loop0p1 /dev/mapper/loop0p2
两个设备。
其中loop0p1是/boot,loop0p2是根。
格式化、挂载:
sudo mkfs.vfat -n boot /dev/mapper/loop0p1
sudo mkfs.ext4 -L rootfs /dev/mapper/loop0p2
#在backupimg文件夹下新建两个文件夹,将两个分区挂载在下面
mkdir tgt_boot tgt_Root
sudo mount -t vfat -o uid=zk,gid=zk,umask=0000 /dev/mapper/loop0p1 ./tgt_boot/
sudo mount -t ext4 /dev/mapper/loop0p2 ./tgt_Root/
上面的uid和gid根据自己系统的用户名有所变化。
备份/boot
直接拷贝
下面命令中的/media/zk/boot/*
要根据自己系统中树莓派SD卡的挂载位置进行修改。
sudo cp -rfp /media/zk/boot/* ./tgt_boot/
备份"/"
原文中提供了两种方法,本人尝试时只用了第一种:dump/restore方法
对目标挂载点设置合适的权限,并清空
sudo chmod 777 ./tgt_Root
#下面的zk.zk与上文提到的uid,gid设置一致,需要根据系统的用户名去修改
sudo chown zk.zk tgt_Root
sudo rm -rf ./tgt_Root/*
cd tgt_Root/
开始备份
#下面命令中的/media/zk/rootfs/
要根据自己系统中树莓派SD卡的挂载位置进行修改
sudo dump -0uaf - /media/zk/rootfs/ | sudo restore -rf -
#返回上层目录
cd ..
耗时几分钟,运行完后可以查看下tgt_Root
下是否已把文件备份过来
相应修改PARTUUID设定
这时候整个备份就已经完成了。不过此时的img文件即使写入到空白SD卡里面也是无法启动的,因为Raspbian启动要对应分区的PARTUUID,所以我们还要修改目标img文件里的如下两个文件:
./tgt_boot/cmdline.txt
./tgt_Root/etc/fstab
首先查看img文件对应的loop device的两个分区的PARTUUID
sudo blkid
在输出的信息里查看到:/dev/mapper/loop0p1
的PARTUUID是“961e1351-01”,/dev/mapper/loop0p2
的PARTUUID是“961e1351-02”。
修改 cmdline.txt 文件
将其中root=PARTUUID=af2f8761-02
部分改为/dev/mapper/loop0p2
(即根分区)的PARTUUID,修改后如下:
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=961e1351-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles
修改fstab文件
proc /proc proc defaults 0 0
PARTUUID=961e1351-01 /boot vfat defaults 0 2
PARTUUID=961e1351-02 / ext4 defaults,noatime 0 1
# a swapfile is not a swap partition, no line here
# use dphys-swapfile swap[on|off] for that
收尾
卸载各个挂载的分区,删除loop device,删除挂载点目录
sudo umount tgt_boot tgt_Root
sudo kpartx -d /dev/loop0
sudo losetup -d /dev/loop0
rmdir tgt_boot tgt_Root
完成之后可以用dd或者Etcher烧写img文件到其他SD卡中,注意烧写到新卡中在树莓派中运行后,要用raspi-config先把分区空间expand一下,否则可用空间会很小。