嵌入式系统篇--系统镜像制作与备份

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 11  1970 .
drwxr-xr-x 4 root root 4096 426 13:56 ..
xd@xd:~/debian$ ls -al rootfs/
总用量 24
drwxr-xr-x 3 root root  4096 426 13:49 .
drwxr-xr-x 4 root root  4096 426 13:56 ..
drwx------ 2 root root 16384 426 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
}
  • 52
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ARM微控制器和嵌入式系统是两个密切相关的概念。ARM微控制器是一种基于ARM架构的微处理器,常用于嵌入式系统中。嵌入式系统是一种被嵌入在其他设备中的计算机系统,用于控制和管理该设备的各种功能。 ARM微控制器是一种非常常见的微处理器架构,具有低功耗、高性能和高可靠性的特点。它被广泛用于各种嵌入式应用中,比如智能手机、平板电脑、家用电器等。ARM架构具有良好的兼容性,可以支持不同的软件开发工具和操作系统,如Android、Linux等。 嵌入式系统是一种专门设计用于控制和管理设备的计算机系统。它通常由硬件和软件两部分组成,硬件部分包括处理器、存储器、输入输出接口等,软件部分包括操作系统和应用程序。嵌入式系统通常需要满足一些特定需求,如实时性、低功耗、小体积等。 在嵌入式系统中,ARM微控制器被广泛应用。它具有丰富的外围接口和强大的计算能力,可以满足各种设备的控制和管理需求。ARM微控制器的软硬件资源丰富,可供开发人员进行灵活的开发和定制。同时,ARM的生态系统也非常完善,有各种开发工具和支持资源可供选择。 清华MOOC是清华大学开设的在线教育平台,其中也有关于ARM微控制器和嵌入式系统的相关课程。通过学习清华MOOC中的课程,可以深入了解ARM微控制器和嵌入式系统的原理和应用,掌握相应的开发技能。这对于希望从事嵌入式系统开发的人而言,是一种很好的学习资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值