文章目录
- 1.手动备份镜像
- 1.1 安装工具软件
- 1.2 制作空白镜像
- 1.2.1 方式1--parted分区
- 1.2.2 方式2--cfdisk交互模式
- 1.3 检查分区设置是否成功
- 1.4 格式化镜像(即格式化boot/rootfs分区)
- 1.5 挂载两个分区(即boot/rootfs分区)
- 1.6 备份boot分区内容(即启动分区)
- 1.7 填充rootfs分区(即根文件系统)
- 1.8 修改rootfs分区中的文件【cmdline.txt 、fstab】
- 1.9 收尾工作
- 1.7 上电测试
- 2.脚本备份镜像
1.手动备份镜像
树莓派从3B开始就是64位cpu,但是官方系统一直都是32位的,直到2020-05-28才发布64位beta版.
树莓派官方系统是基于Debian的,所以选择Debian Buster进行构建。整个构建过程可以在安装Linux系统的PC或服务器上进行,也可以在64位树莓派上进行(推荐使用基地2.0最新的u3),我使用x64平台的Debian进行演示,过程中我会说明与使用树莓派不同的地方,没有说明的就是通用的。
1.1 安装工具软件
xd@xd:~# sudo apt-get install dosfastools dump parted kpartx //安装所需要的软件
dosfstools:fat32分区格式化工具
dump:dump & restore备份工具
Parted & kpartx:虚拟磁盘工具
1.2 制作空白镜像
我们首先来创建镜像文件,这次我们构建的是lite系统不包含桌面环境,所以我创建一个3G的镜像(其实2G就够),这里可以根据自己定制的需要改变镜像的大小。为了避免频繁使用sudo,全程使用root用户操作。
xd@xd:~$ mkdir debian //创建一个工作目录,所有工作都在此目录进行
xd@xd:~$ cd debian/
xd@xd:~/debian$ dd if=/dev/zero of=buster.img bs=3G count=0 seek=1 //创建镜像文件buster.img
xd@xd:~/debian$ cfdisk buster.img //给镜像文件分区
dd:用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换
if=文件名:输入文件名,缺省为标准输入,即指定源文件
of=文件名:输出文件名,缺省为标准输出,即指定目的文件
bs=bytes:同时设置读入/输出的块大小为bytes个字节
count=blocks:仅拷贝blocks个块
seek=blocks:从输出文件开头跳过blocks个块后在开始复制
树莓派系统镜像有两个分区,一个boot分区类型为FAT32,一个rootfs根分区类型为ext4,下面开始分区。查看分区表:sudo fdisk -l
1.2.1 方式1–parted分区
8192 - 172031 : fat32类型(windows系统可识别),固定boot分区起点和末尾,防止每次备份时镜像文件变大【80M】
172032 - l : ext4类型(windows系统不可识别)
分区大小根据查看的实际分区表大小来定义,第二个分区的终止点为img文件的 末位(-l)
xd@xd:~$ sudo parted buster.img --script -- mklabel msdos
xd@xd:~$ sudo parted buster.img --script -- mkpart primary fat32 8192s 172031s
xd@xd:~$ sudo parted buster.img --script -- mkpart primary ext4 172032s -l
1.2.2 方式2–cfdisk交互模式
进入cfdisk交互模式后,选择dos类型
然后用左右键选择新建,输入分区大小256M,类型选择主分区:
然后用上下键选择刚刚创建的256M分区,左右键选择类型,弹出对话框选择类型c:
然后在剩余空间上选择新建,分区大小默认,类型选择主分区:
然后有左右按键选写入,提示输入yes:
最后按q键或者选择退出
1.3 检查分区设置是否成功
1.检查分区命令:
sudo parted buster.img
1.4 格式化镜像(即格式化boot/rootfs分区)
格式化两个分区,注意替换自己的设备名称(boot / rootfs):
xd@xd:~/debian$ losetup -f --show buster.img //挂载镜像文件
/dev/loop12 //执行完上面的挂载命令会得到一个输出,表示挂载的loop设备,这里是loop12
xd@xd:~/debian$ kpartx -va /dev/loop12 //挂载镜像文件两个分区设备
add map loop0p1 (254:0): 0 204800 linear 7:0 2048 //镜像第1分区/dev/mapper/loop121
add map loop0p2(254:1):0 6084608 linear 7:0 206848 //镜像第2分区/dev/mapper/loop122
xd@xd:~/debian$ mkfs.fat -n "boot" /dev/mapper/loop12p1 //格式化boot分区
mkfs.fat 4.1 (2017-01-24)
mkfs.fat: warning - lowercase labels might not work properly with DOS or Windows //格式化rootfs,设置label
xd@xd:~/debian$ mkfs.ext4 rootfs /dev/mapper/loop12p2 //格式化rootfs分区
mke2fs 1.44.5 (15-Dec-2018)
Discarding device blocks: done
Creating filesystem with 760576 4k blocks and 190464 inodes
Filesystem UUID: 2453d54e-3d58-4b4b-a21e-9d9a46ad3cba
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
xd@xd:~/debian$
1.5 挂载两个分区(即boot/rootfs分区)
创建两个挂载点并挂载两个分区,通过lsblk命令确认一下挂载情况
xd@xd:~/debian$ id //挂载之前先查询一下uid和gid,使用命令id来进行查询
用户id=1000(xd) 组id=1000(xd) 组=1000(xd),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),133(lxd),134(sambashare)
xd@xd:~/debian$ mkdir boot rootfs //创建挂载目录
xd@xd:~/debian$ mount /dev/mapper/loop12p1 boot/ //挂载到boot
xd@xd:~/debian$ mount /dev/mapper/loop12p2 rootfs/ //挂载到rootfs
xd@xd:~/debian$ ls -al boot/
总用量 5
drwxr-xr-x 2 root root 512 1月 1 1970 .
drwxr-xr-x 4 root root 4096 4月 26 13:56 ..
xd@xd:~/debian$ ls -al rootfs/
总用量 24
drwxr-xr-x 3 root root 4096 4月 26 13:49 .
drwxr-xr-x 4 root root 4096 4月 26 13:56 ..
drwx------ 2 root root 16384 4月 26 13:49 lost+found
xd@xd:~/debian$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 3G 0 loop
├─loop0p1 254:0 0 100M 0 part /root/debian/boot
└─loop0p2 254:1 0 2.9G 0 part /root/debian/rootfs
loop1 7:1 0 3G 0 loop
sda 8:0 0 40G 0 disk
├─sda1 8:1 0 39G 0 part /
├─sda2 8:2 0 1K 0 part
└─sda5 8:5 0 975M 0 part [SWAP]
sr0 11:0 1 3.7G 0 rom /media/cdrom0
root@debian:~/debian$
1.6 备份boot分区内容(即启动分区)
命令:
sudo cp /media/xd/boot/* boot/
1.7 填充rootfs分区(即根文件系统)
对目标挂载点设置合适的权限并清空,最后备份:
sudo chmod 777 rootfs/
sudo chown xd.xd rootfs/
sudo rm -rf rootfs/*
sudo dump -0uaf - /media/xd/rootfs/ | sudo restore -rf -
1.8 修改rootfs分区中的文件【cmdline.txt 、fstab】
如果不修改cmdline.txt和fstab文件内容,上电启动后会出现下列错误:
查寻到新建的镜像buster.img对应的loop device的两个分区的 PARTUUID:
/dev/mapper/loop12p1的 PARTUUID是“ b4b1fedf-01”,
/dev/mapper/loop12p2的 PARTUUID是“ b4b1fedf-02”。修改cmdline.txt文件:
修改fstab文件
1.9 收尾工作
root@debian:~/debian# umount /root/debian/boot //取消boot分区挂载
root@debian:~/debian# umount /root/debian/rootfs//取消rootfs分区挂载
root@debian:~/debian# kpartx -d /dev/loop12 //取消镜像文件两个分区挂载
root@debian:~/debian# losetup -d /dev/loop12 //取消挂载在loop0设备上的镜像文件
1.7 上电测试
2.脚本备份镜像
#备份镜像功能
rpi3b_path=~/rpi3b
copy_pwd=~/copy
boot_path=$copy_pwd/boot #boot 的挂载目录
rootfs_path=$copy_pwd/rootfs #rootfs 的挂载目录
media_boot_name=/media/xd/boot #sdb1 的位置
media_rootfs_name=/media/xd/rootfs #sdb2 的位置
img_name=$rpi3b_path/rpi3b_32b_backup.img #备份镜像的名字
#交叉编译工具链
toolchain_verber=gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf
toolchain_path=$rpi3b_path/toolchain
toolchain_source=$toolchain_path/toolchain-rpi32b
cross_compile=$toolchain_source/bin/arm-linux-gnueabihf- #交叉编译工具链路径
function backimg_default(){
echo "============= 默认方式备份镜像[boot(80M) rootfs(2.7G)] ============="
sleep 3
echo "=========================== prart 1, check SD inserted ==========================="
local dev_sdb1=`df -h | grep $media_boot_name | awk '{print $1}'` #检查 /media/xd/boot 是否存在
local dev_sdb2=`df -h | grep $media_rootfs_name | awk '{print $1}'` #检查 /media/xd/rootfs 是否存在
if [[ $dev_sdb1 == "" ]]||[[ $dev_sdb2 == "" ]]; then
echo "检测到SD卡未连接到虚拟机"
echo "dev_sdb1=${dev_sdb1}"
echo "dev_sdb2=${dev_sdb2}"
exit
else
echo "dev_sdb1=${dev_sdb1}"
echo "dev_sdb2=${dev_sdb2}"
fi
sudo apt-get install dosfstools dump parted kpartx
echo "=========================== prart 2, create a new blank img ======================"
# img_name=rpi32_`date +%Y%m%d`_copy.img
if [ -f $img_name ]; then
echo "$img_name 已存在,正在删除并重新创建"
rm -rf $img_name
fi
#local bootsz=`df -P | grep $dev_sdb1 | awk '{print $2}'`
#local rootsz=`df -P | grep $dev_sdb2 | awk '{print $2}'`
local bootsz=81920 #固定为80M,防止每次备份时镜像文件变大
local rootsz=1880064 #固定为1.79G,防止每次备份时镜像文件变大
local totalsz=`echo $bootsz $rootsz | awk '{print int(($1+$2)*1.5/1024)}'`
sudo dd if=/dev/zero of=$img_name bs=1M count=$totalsz #创建一个空白的镜像文件
sync
echo "创建空白镜像[$img_name],boot[${bootsz} KB],rootfs[${rootsz} KB],total[${totalsz} KB]"
echo "=========================== prart 3, partition new blank img ====================="
#local bootstart=`sudo fdisk -l | grep $dev_sdb1 | awk '{print $2}'`
#local bootend=`sudo fdisk -l | grep $dev_sdb1 | awk '{print $3}'`
#local rootstart=`sudo fdisk -l | grep $dev_sdb2 | awk '{print $2}'`
local bootstart=8192 #固定boot分区起点,防止每次备份时镜像文件变大
local bootend=172031 #固定boot分区末尾,防止每次备份时镜像文件变大
local rootstart=172032 #固定rootfs分区起点,防止每次备份时镜像文件变大
#有些系统 sudo fdisk -l 时boot分区的boot标记为*,此时bootstart和bootend最后应改为 $3 和 $4
#local rootend=`sudo fdisk -l /dev/sdb1 | grep loopop2 | awk'{print $3}'`
sudo parted $img_name --script -- mklabel msdos
if [[ $? != 0 ]]; then
rm -rf $img_name
echo "mklabel msdos 执行失败 删除[$img_name]"
echo "<退出执行>"
exit
fi
sudo parted $img_name --script -- mkpart primary fat32 ${bootstart}s ${bootend}s
if [[ $? != 0 ]]; then
rm -rf $img_name
echo "primary fat32 执行失败 删除[$img_name]"
echo "<退出执行>"
exit
fi
sudo parted $img_name --script -- mkpart primary ext4 ${rootstart}s -1 #从起点到结尾
if [[ $? != 0 ]]; then
rm -rf $img_name
echo "primary ext4 执行失败 删除[$img_name]"
echo "<退出执行>"
exit
fi
echo "boot[$bootstart - $bootend] rootfs[$rootstart - l]"
echo "=========================== prart 4, mount/copy img to system(boot) ============"
local loopdevice=`sudo losetup -f --show $img_name` #查找第一个未使用的设备,设置后打印设备名
local device=/dev/mapper/`sudo kpartx -va $loopdevice | sed 's/.*\(loop[0-9]*[0-9]\)p.*/\1/g' | head -1` #创建分区表的设备映射,装载成功在/dev/mapper目录下生成loop*p1 和 loop*p2
if [[ ! -L ${device}p1 ]]||[[ ! -L ${device}p2 ]]; then
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $img_name
echo "kpartx 执行失败 删除[$img_name]"
echo "<退出执行>"
exit
fi
echo "loopdevice[$loopdevice] device[$device]"
local sdb1_uuid=`sudo blkid -o export $dev_sdb1 | grep PARTUUID`
local loop1_uuid=`sudo blkid -o export ${device}p1 | grep PARTUUID`
local sdb2_uuid=`sudo blkid -o export $dev_sdb2 | grep PARTUUID`
local loop2_uuid=`sudo blkid -o export ${device}p2 | grep PARTUUID`
echo "sdb1_uuid[$sdb1_uuid] loop1_uuid[$loop1_uuid] sdb2_uuid[$sdb2_uuid] loop2_uuid[$loop2_uuid]"
sleep 2
sudo mkfs.vfat ${device}p1 -n boot #格式化分区表1的卷标为boot
#这里没有使用id命令来查看uid和gid,而是假设uid和gid都和当前用户名相同
local uid=`whoami`
local gid=$uid
echo "uid[$uid] gid[$gid]"
if [ -e $boot_path ]||[ -e $copy_pwd ]; then
echo "$copy_pwd 已经存在,需先自行确认后再决定是否删除"
exit
fi
mkdir -p $boot_path
rm -rf $boot_path/*
echo "创建 boot 挂载目录成功: $boot_path"
#挂载到新的挂载点
sudo mount -t vfat -o uid=${uid},gid=${gid},umask=0000 ${device}p1 $boot_path #将分区表1(卷标为boot)挂载到~/copy/boot
if [[ $? == 0 ]]; then
echo "${device}p1 挂载到 $boot_path [挂载成功]"
else
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "${device}p1 挂载到 $boot_path [挂载失败]"
echo "<退出执行>"
exit
fi
#开始备份boot分区
sudo cp -rfp ${media_boot_name}/* $boot_path
if [[ $? == 0 ]]; then
echo "$media_boot_name [拷贝成功]"
else
sudo umount $boot_path #卸载挂载点
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "$media_boot_name [拷贝失败]"
echo "<退出执行>"
exit
fi
sync
sleep 2
if [ ! -f $boot_path/cmdline.txt ]; then
sudo umount $boot_path
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "$boot_path/cmdline.txt 文件不存在"
echo "<退出执行>"
exit
fi
sudo sed -i "s/${sdb2_uuid}/${loop2_uuid}/g" $boot_path/cmdline.txt
if [[ $? != 0 ]]; then
sudo umount $boot_path
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "$boot_path/cmdline.txt [修改失败]"
echo "<退出执行>"
exit
else
echo "$boot_path/cmdline.txt [修改成功]"
cat "$boot_path/cmdline.txt"
fi
#卸载新挂载点
sudo umount $boot_path
if [[ $? == 0 ]]; then
echo "$boot_path [卸载新挂载点成功]"
rm -rf $copy_pwd
else
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $img_name
echo "$boot_path [卸载新挂载点失败]"
echo "<退出执行>"
exit
fi
echo "=========================== prart 5, mount/copy img to system(rootfs) ==========================="
sleep 2
sudo mkfs.ext4 ${device}p2 -L rootfs #格式化分区表2的卷标为rootfs
if [ -e $rootfs_path ]||[ -e $copy_pwd ]; then
echo "$copy_pwd 已经存在,拷贝boot时有错误"
exit
fi
mkdir -p $rootfs_path
sudo chmod 777 $rootfs_path
sudo chown ${uid}.${gid} $rootfs_path
sudo rm -rf $rootfs_path/*
echo "创建 rootfs 挂载目录成功: $rootfs_path"
#挂载到新的挂载点
sudo mount -t ext4 ${device}p2 $rootfs_path
if [[ $? == 0 ]]; then
echo "${device}p2 挂载到 $rootfs_path [挂载成功]"
else
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "${device}p2 挂载到 $rootfs_path [挂载失败]"
echo "<退出执行>"
exit
fi
cd $rootfs_path
sudo dump -0uaf - ${media_rootfs_name}/ | sudo restore -rf -
sync
sleep 2
#下面内容是删除树莓派中系统自动产生的文件、临时文件等
sudo rm -rf ./.gvfs ./dev/* ./media/* ./mnt/* ./proc/* ./run/* ./sys/* ./tmp/* ./lost+found/ ./restoresymtable
cd ~
sleep 2
if [ ! -f $rootfs_path/etc/fstab ]; then
sudo umount $rootfs_path
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "$boot_path/etc/fstab 文件不存在"
echo "<退出执行>"
exit
fi
sudo sed -i "s/${sdb1_uuid}/${loop1_uuid}/g" $rootfs_path/etc/fstab
if [[ $? != 0 ]]; then
sudo umount $rootfs_path
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "$rootfs_path/etc/fstab [修改失败1]"
echo "<退出执行>"
exit
fi
sudo sed -i "s/${sdb2_uuid}/${loop2_uuid}/g" $rootfs_path/etc/fstab
if [[ $? != 0 ]]; then
sudo umount $rootfs_path
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
rm -rf $img_name
echo "$rootfs_path/etc/fstab [修改失败2]"
echo "<退出执行>"
exit
fi
echo "$rootfs_path/etc/fstab [修改成功]"
cat "$rootfs_path/etc/fstab"
#卸载新挂载点
sudo umount $rootfs_path
if [[ $? == 0 ]]; then
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $copy_pwd
echo "$rootfs_path [卸载新挂载点成功]"
else
sudo kpartx -d $loopdevice #删除分区表的设备映射
sudo losetup -d $loopdevice #断开一台或多台设备
rm -rf $img_name
echo "$rootfs_path [卸载新挂载点失败]"
echo "<退出执行>"
exit
fi
if [ -e /mnt/hgfs/share ]; then
mv $img_name /mnt/hgfs/share
echo "将新制作的镜像移动到PC端[移动成功]"
else
echo "将新制作的镜像移动到PC端[移动失败]"
echo "<退出执行>"
exit
fi
}