参考链接:https://neucrack.com/p/107
https://www.hao4k.cn/thread-41764-1-1.html(加精)
1 查看SD卡设备号
将树莓派SD卡插上电脑,映射到虚拟机,使用下面命令获取SD卡设备号
sudo lsblk
从上面可以看出,SD卡被识别为了sdb,分为sdb1和sdb2,
2 修改脚本文件
修改“bachup_system.sh”脚本文件,把上面读到的两个挂载地址分别赋给src_boot_device、src_root_device、src_boot_device_blkid、src_root_device_blkid,如下图所示
#!/bin/bash
#
# backup script for raspberry Pi, backup on PC in recommend
# usage:
# 1. Cleanup your unuseful files in system
# 2. Reboot to check system can normally boot
# 3. Shutdown and remove TF card from Pi, and put it in a card reader
# 4. Plug your card reader into a PC's USB port
# 5. PC's OS should be ubuntu(recommend)/dedian/arch/manjaro
# 6. Edit the TODO section below, find your device by `sudo lsblk` command
# 7. Execute script, the script will automatically install software and print size,
# ensure your PC have enough place
# 8. wait and see if error occurrs, if error occurrs,
# see error carefully and maybe you can change config to resolve it
#
# 9. If error occurred, you can manually umount virual device by
# `df -h` to see mount info, then `sudo umount dir`
# `sudo blkid`, then `sudo kpartx -d /dev/loopx && sudo losetup -d /dev/loopx`
# recover:
# 1. Burn image to TF card by `sudo dd if=backup.img of=/dev/sdx status=progress bs=1MiB`
# or other tools like etcher, win32diskimager etc.
# 2. Expand filesystem
# way1: boot system on Pi and switch to console by `ctrl+alt+F1`,
# login and execute `sudo raspi-config`, in `advanced config` select `expand filesystem`, `reboot`, all things done
# way2: use gparted GUI tool to expand filesystem
# author: neucrack (neucrack.com)
# update: 2021-04-26 optimize backup
#
set -o errexit
######################################################
################## TODO: settings#####################
src_boot_device=/dev/sdb1 #/dev/mmcblk0p1
src_root_device=/dev/sdb2 #/dev/root
src_boot_device_blkid=/dev/sdb1 #/dev/mmcblk0p1
src_root_device_blkid=/dev/sdb2 #/dev/mmcblk0p2
root_backup_size=1.3 # root backup size, 1 means the same as used size
os_arch_manjaro=0 # for system Arch or Manjaro, else ubuntu/debian
backup_on_pi=0 # 1: backup on pi(maybe not well supported), 0: backuo on PC (recommend)
# !!!!!if back on PI, it's better to use rsync
copy_use_rsync=0 # 1: use rsync to copy files, 0: use dump command to copy files
# !!!!!if back on PI, you should umount all your disk devices like USB disk or add exclude here
# rsync_exlude='--exclude relative_path1 --exclude relative_path2'
rsync_exlude='--exclude home/pi/data/raid'
######################################################
green="\e[32;1m"
yellow="\e[33;1m"
red="\e[31;1m"
purple="\e[35;1m"
normal="\e[0m"
# check
if [ "${backup_on_pi}x" == "1x" ]; then
echo "====================="
echo -e "${green}backup on pi${normal}"
echo "====================="
root_uid=`sudo cat /etc/passwd | grep root|awk -F : '{print $3}'`
root_gid=`sudo cat /etc/passwd | grep root|awk -F : '{print $4}'`
if [ "${root_uid}x" != "${root_gid}x" ]; then
echo "root user(uid:${root_uid}, gid:${root_gid}) must belong to root group, change by sudo usermod -g root root"
exit 1
fi
else
echo "====================="
echo -e "${green}backup on PC${normal}"
echo "====================="
fi
echo -e "${green} \ninstall software\n ${normal}"
if [ "${os_arch_manjaro}x" == "1x" ]; then
echo "-- install by pacman"
sudo pacman -S dosfstools parted multipath-tools bc
else
sudo apt-get install -y dosfstools dump parted kpartx bc
fi
echo -e "${green} \ninstall software complete\n ${normal}"
echo -e "before backup, it's better to clean your temp files and not useful files, and carefully, not detele useful data!!!"
if [ "${os_arch_manjaro}x" != "1x" ]; then
if [ "${backup_on_pi}x" == "1x" ]; then
echo -e "${gren} \nclean apt cache\n${normal}"
sudo apt-get autoremove -yq --purge
sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*
sudo rm -rf /tmp/*
fi
fi
if [ "${backup_on_pi}x" != "1x" ]; then
echo -e "${green} mount ${normal}"
sudo mkdir -p /media/backup_src_boot
sudo mkdir -p /media/backup_src_root
if df -P | grep $src_boot_device_blkid ; then
echo "already mounted"
else
sudo mount $src_boot_device_blkid /media/backup_src_boot
sudo mount $src_root_device_blkid /media/backup_src_root
fi
echo -e "${green} mount ok ${normal}"
fi
echo -e "${green}create image now\n ${normal}"
used_size=`df -P | grep $src_root_device | awk '{print $3}'`
boot_size=`df -P | grep $src_boot_device | awk '{print $2}'`
if [ "x${used_size}" != "x" ] && [ "x${boot_size}" != "x" ];then
count=`echo "${used_size}*${root_backup_size}/1024+${boot_size}/1024+2"|bc|awk '{printf("%.0f",$1)}'`
else
echo "device $src_root_device or $src_boot_device not exist in `df -P`,mount first"
exit 0;
fi
echo -e "${green}boot size:$(($boot_size/1024+1))MiB, root used_size:$(($used_size/1024)) MiB, will backup as: $count MiB ${normal}"
echo -e "${purple}If your mem > used_size, it's a faster way to backup in tempfs directory~~~${normal}"
echo -e "${yellow}continue? yes or no${normal}"
read x
case "$x" in
y|yes ) echo "#########";;
n|no ) exit 1;;
* ) echo "Only answer yes or no!"
esac
echo -e "${green} now generate empty img, it may take a while, wait please ... ${normal}"
sudo dd if=/dev/zero of=backup.img bs=1M count=$count status=progress
echo -e "${green} now part img ${normal}"
bootstart=`sudo fdisk -l | grep $src_boot_device_blkid | awk '{print $2}'`
bootend=`sudo fdisk -l | grep $src_boot_device_blkid | awk '{print $3}'`
rootstart=`sudo fdisk -l | grep $src_root_device_blkid | awk '{print $2}'`
echo "boot addr: $bootstart - $bootend, root addr: $rootstart >>> end"
sudo parted backup.img --script -- mklabel msdos
sudo parted backup.img --script -- mkpart primary fat32 ${bootstart}K ${bootend}K
sudo parted backup.img --script -- mkpart primary ext4 ${rootstart}K -1
echo -e "${green}mount loop device and copy files to image\n${normal}"
loopdevice=`sudo losetup --show -f backup.img`
echo $loopdevice
device=`sudo kpartx -va $loopdevice`
echo $device
device=`echo $device | sed -E 's/.*(loop[0-9]*)p.*/\1/g' | head -1`
# device=`echo $device |awk '{print $3}' | head -1`
echo $device
device="/dev/mapper/${device}"
boot_device="${device}p1"
root_device="${device}p2"
sleep 5
sudo mkfs.vfat $boot_device
sudo mkfs.ext4 $root_device
sudo mkdir -p /media/img_to_boot
sudo mkdir -p /media/img_to_root
sudo mkdir -p /media/img_src_boot
sudo mkdir -p /media/img_src_root
mount_path=`df -h|grep ${src_boot_device}|awk '{print $6}'`
if [ "x${mount_path}" == "x" ];then
sudo mount -t vfat $src_boot_device /media/img_src_boot
mount_path=/media/img_src_boot
fi
sudo mount -t vfat $boot_device /media/img_to_boot
echo -e "${green}copy /boot(${mount_path} to /media/img_to_boot)${normal}"
sudo cp -rf ${mount_path}/* /media/img_to_boot
sync
#################################
mount_path=`df -h|grep ${src_root_device}|awk '{print $6}'`
echo root mount path: $mount_path
if [ "x${mount_path}" == "x" ];then
sudo mount -t ext4 $src_root_device /media/img_src_root
mount_path=/media/img_src_root
fi
sudo mount -t ext4 $root_device /media/img_to_root
echo -e "${green}copy /${normal}"
if [[ "${copy_use_rsync}x" == "1x" ]]; then
if [ "${backup_on_pi}x" == "1x" ]; then
curr_dir=`pwd`
backup_img_exclude="--exclude '${curr_dir}/backup.img'"
backup_img_exclude=`echo ${backup_img_exclude} | sed -e "s/\///"`
else
backup_img_exclude=""
fi
if [ -f /etc/dphys-swapfile ]; then
SWAPFILE=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=`
if [ "$SWAPFILE" = "" ]; then
SWAPFILE=/var/swap
fi
EXCLUDE_SWAPFILE="--exclude $SWAPFILE"
fi
sudo rsync --force -rltWDEgop --delete --stats --progress \
$EXCLUDE_SWAPFILE \
--exclude '.gvfs' \
--exclude 'media/' \
--exclude 'mnt/' \
--exclude 'tmp/' \
--exclude 'lost\+found/' \
--exclude 'var/lib/apt/lists/' \
$rsync_exlude \
$backup_img_exclude \
${mount_path}/* /media/img_to_root
else
if [ "${backup_on_pi}x" == "1x" ]; then
# exclude backup.img from backup
sudo chattr +d backup.img #exclude img file from backup(support in ext* file system)
fi
cd /media/img_to_root
tmp_inode=`stat ${mount_path}/tmp --printf "%i"`
lost_found_inode=`stat ${mount_path}/lost\+found/ --printf "%i"`
media_inode=`stat ${mount_path}/media --printf "%i"`
mnt_inode=`stat ${mount_path}/mnt --printf "%i"`
apt_inode=`stat ${mount_path}/var/lib/apt/lists --printf "%i"`
sudo dump -e ${tmp_inode},${lost_found_inode},${media_inode},${mnt_inode},${apt_inode} -0auf - ${mount_path} | sudo restore -rf -
fi
sync
echo -e "${green}update partUUID${normal}"
uuid_boot_src=`sudo blkid -o export ${src_boot_device_blkid} | grep PARTUUID`
uuid_boot_dst=`sudo blkid -o export ${boot_device} | grep PARTUUID`
uuid_root_src=`sudo blkid -o export ${src_root_device_blkid} | grep PARTUUID`
uuid_root_dst=`sudo blkid -o export ${root_device} | grep PARTUUID`
echo -e "${green}old boot partUUID: $uuid_boot_src , new: $uuid_boot_dst ${normal}"
echo -e "${green}old root partUUID: $uuid_root_src , new: $uuid_root_dst ${normal}"
sudo sed -i "s/$uuid_root_src/$uuid_root_dst/g" /media/img_to_boot/cmdline.txt
sudo sed -i "s/$uuid_root_src/$uuid_root_dst/g" /media/img_to_root/etc/fstab
sudo sed -i "s/$uuid_boot_src/$uuid_boot_dst/g" /media/img_to_root/etc/fstab
echo -e "${green}update partUUID complete${normal}"
# create temp dirs
sudo mkdir -p /media/img_to_root/tmp
sudo mkdir -p /media/img_to_root/media
sudo mkdir -p /media/img_to_root/mnt
sudo mkdir -p /media/img_to_root/tmp
sudo chmod 777 /media/img_to_root/tmp
sudo mkdir -p /media/img_to_root/var/lib/apt/lists
sync
sleep 5
cd
echo "loopdevice: $loopdevice"
echo -e "${green}umount /media/img_to_boot${normal}"
sudo umount /media/img_to_boot
echo -e "${green}umount /media/img_to_root${normal}"
sudo umount /media/img_to_root
sudo kpartx -d $loopdevice
sudo losetup -d $loopdevice
sudo rm /media/img_to_root /media/img_to_boot /media/img_src_root /media/img_src_boot -rf
if [ "${backup_on_pi}x" != "1x" ]; then
echo -e "${green} umount ${normal}"
sudo umount /media/backup_src_boot
sudo umount /media/backup_src_root
echo -e "${green} umount ok ${normal}"
fi
echo "====================="
echo -e "${green}\nbackup complete\n${normal}"
echo "====================="
3 运行脚本
使用下面命令,查看虚拟机中磁盘剩余情况
df -h
可以看到,/dev/sda1还有27G的可用空间,我们的树莓派镜像实际大小应该在10G以内,虚拟机的空间是足够的,然后就把脚本拷贝到虚拟机中,准备运行
把修改后的bachup_system.sh文件拷贝到家目录下,使用下面命令分配可执行权限
chmod 777 bachup_system.sh
运行脚本
./bachup_system.sh
输入yes,回车,等待。。。
这个时候的镜像,就是压缩后的实际镜像了,看一下大小:
真香。
4 镜像烧录
镜像生成之后,可换一张SD卡,插入虚拟机后,先获得设备号
然后使用下面命令进行烧录:
sudo dd if=backup.img of=/dev/sdb status=progress bs=1MiB
5 遇到的问题
5.1 boot地址错误
boot addr起始地址打印出来为*,如下图
使用fdisk -l 查看SD卡实际分区情况
sudo fdisk -l
可以看到,实际的SD卡boot起始地址是2048-526335,但是脚本打印出来的起始地址确是*-2048,正好错了一位。
查看脚本可以看到,脚本中获取起始地址的命令在这里:
bootstart=`sudo fdisk -l | grep $src_boot_device_blkid | awk '{print $2}'`
bootend=`sudo fdisk -l | grep $src_boot_device_blkid | awk '{print $3}'`
rootstart=`sudo fdisk -l | grep $src_root_device_blkid | awk '{print $2}'`
直接在终端中输入下面命令获取起始地址:
sudo fdisk -l | grep /sdc1 | awk '{print $2}'
打印出来的确是是*,把awk后的$2后移动一位,改为$3,脚本中获取地址的代码改为:
bootstart=`sudo fdisk -l | grep $src_boot_device_blkid | awk '{print $3}'`
bootend=`sudo fdisk -l | grep $src_boot_device_blkid | awk '{print $4}'`
rootstart=`sudo fdisk -l | grep $src_root_device_blkid | awk '{print $2}'`
而有的系统操作的时候不需要修改这里就能压缩成功,区别在哪里呢,我们找了一个无需修改的SD卡看一下他的分区情况
可以发现,他的sd卡分区中,sdb1直接就是起始地址8192,没有那个*字符,直接原脚本中的命令去读取起始地址,返回的也是正确值,所以在没有修改脚本的情况下,他也生成成功了。
至于为什么一个SD卡有 * ,一个没有 * ,留给后人研究吧。
5.2 提示找不到cmdline.txt
发现ubutu18.04的树莓派boot目录下,没有cmdline.txt这个文件,在config.txt中cmdline=nobtcmd.txt,以nobtcmd作为关键字,进行相应搜索,发现了下面的几个帖子
(50条消息) 树莓派 Ubuntu1804 安装k8s CGROUPS_MEMORY: missing 解决办法_nameLessor的博客-CSDN博客
这个链接里,说ubuntu18.04的cmdline.txt改为了nobtcmd.txt
ubuntu树莓派4B+的GPIO硬件串口通信-pudn.com里也提到了4B刷ubuntu后用的是nobtcmd.txt这个文件
鉴于上述情况对脚本进行修改,把cmdLine.txt改为nobtcmd.txt
这样就不再报cmdline.txt缺失的问题了,但是将备份好的镜像烧录到新的SD卡后,不进系统,报下面的错误:
Gave up waiting for suspend/resume device
Gave up waiting for root file system device.
-Boot args (cat /proc /cmdline)
-Check rootdelay=(did the system wait long enough?)
-Missing modules( cat /proc /modules; ls /dev)
ALERT UUID=XXXXXXXXXXX does not exit. Dropping to a shell!
搜索文件,跟cmdline.txt有关系
参照这个大神的帖子,
教你树莓派4B的系统备份方法教程大全(全卡+压缩备份) - 走看看 (zoukankan.com)
脚本中对nobtcmd.txt和对fstab的处理,是更新PARTUUID,将烧录好的SD卡插回linux虚拟机,使用blkid查看SD卡的UUID值,如下所示
将UUID值分别写入nobtcmd.txt和fstab中
改为:
PARTUUID=e0d8fe4b-02 / ext4 defaults 0 0
PARTUUID=e0d8fe4b-01 / /boot/firmware vfat defaults 0 0
改了后,树莓派新的镜像可以进入图像化界面,但是输入用户名、密码无法登录。
5.2 使用dd烧录后SD卡空间变小了
使用64G内存卡烧录压缩好的镜像,烧录完使用df-h发现64G的内存卡变成了8G,可以通过下面命令来恢复被“吃掉”的空间。
sudo parted /dev/mmcblk0 #使用parted来调整磁盘/dev/mmcblk0
进入调整菜单,输入下面指令
print # 打印查看当前的分区情况
resizepart 2 -1 #将第2个分区充满剩下的空间
quit #退出
此时在用df-h发现存储还是8G,接着输入下面指令
sudo resize2fs /dev/mmcblk0p2 #使用resize2fs来调整mmcblk0p2分区的大小
再用df-h看,内存就恢复了
5.3 copy /boot文件时报没有权限
在脚本中找到报错的那一句
echo -e "${green}copy /boot(${mount_path} to /media/img_to_boot)${normal}"
sudo cp -rfp ${mount_path}/* /media/img_to_boot
sync
在终端中单独发下面命令,报错相同
sudo cp -rfp /media/vr/E2A5-8E72/* /media/img_to_boot/
指令跟了一个-p的后缀,查资料,-p是复制过程中保留权限、属主、时间戳的命令,试下把-p去掉,手动复制成功,所以,我们把脚本中的-p也给去除试一下,去除后,镜像生成成功。
echo -e "${green}copy /boot(${mount_path} to /media/img_to_boot)${normal}"
sudo cp -rf ${mount_path}/* /media/img_to_boot
sync
所以,我们把脚本中的-p也给去除试一下,去除后,镜像生成成功。
echo -e "${green}copy /boot(${mount_path} to /media/img_to_boot)${normal}"
sudo cp -rf ${mount_path}/* /media/img_to_boot
sync
5.4 DUMP过程中,报空间不足
漫长的DUMP过到79%时,报空间不足,然后就停止打包,使用df -h命令查看空间使用情况
发现,脚本创建的loop0p2满了,在脚本里寻找这个空间创建相关的语句,发现下面语句中涉及到创建空间大小,并且该大小使用了一个变量root_back_size进行的设定
只管把这个变量设大,改为2,估摸着意思是把创建空间改为实际使用空间的2倍
改完后再运行脚本,bump过程中查看创建的空间大小
果然变大了很多,这次运行,直接到打包结束,没有再报空间不够的问题了。
6 ubunt18.04+ROS备份烧录-终极之战
使用PiShrink 裁剪方式。
通过裁剪用 Win32DiskImager 或者 dd 命令全卡备份的镜像,去掉没有内容的分区,从而减小备份镜像的大小。
将全卡备份的镜像文件复制到 Linux 虚拟中,将pishrink.sh 脚本文件,存到镜像所在文件夹下。执行chmod +x pishrink.sh 增加执行权限,然后执行sudo bash pishrink.sh rpi-back.img 即可。
可以看到,镜像精简后,大小由原来的60G缩减为15G,使用Win32 磁盘映像工具将缩减后的镜像烧入一张新的SD卡,烧写完成后,上电,启动成功。