嵌入式Linux系统镜像制作(基于SD卡)

文章目录

  • 目的
  • 原理
  • 环境准备
  • 系统镜像制作
    • 从现有SD卡备份
    • 从空白文件开始制作
  • 系统镜像使用
  • 其它补充
  • 总结

目的

嵌入式Linux在开发过程中对于软件方面通常是 bootloader、linux kernel、rootfs、applications
分开进行处理的,但到了生产阶段这样就不方便了。比较常见的做法是将所有内容整合到一起制作成系统镜像,这样在生产的时候只要烧录系统进行即可。这篇文件将介绍基于SD卡的系统镜像制作方法。

原理

制作系统镜像的原理非常简单: 系统等内容无非就是一堆放在SD卡上的数据,那么只要保持这些数据在SD卡上分布,原模原样拷贝成一个文件,那这个文件就是系统镜像了。使用时只要将该文件原模原样写入到SD卡上就行。

原理就是上面那样,对于SD卡而言想要更好的制作系统镜像了解一下SD卡的数据分布会更加好:
嵌入式Linux系统镜像制作(基于SD卡)_第1张图片
通常嵌入式Linux系统比较常见的会有两个分区: boot分区 和 root分区。

对于SD卡而言通常从头开始的1M空间是预留的,不会用作分区,并且其中最开头的512字节是主引导区,千万不要随意修改其中内容。主引导区后面的(1M - 512)字节空间很多时候会被用来存放 bootloader(uboot) 。当然这个是看芯片的,很多芯片如果从SD卡启动的话,会从SD卡指定地址读取程序并启动,通常这个地址介于 512~1M 之间的。

boot分区通常从1M往后分布,一般不需要很大,并且通常都是FAT32格式的。boot分区用于存放内核镜像、设备树、内核启动参数文件等。有些芯片的 bootloader(uboot) 也是放在这个分区上的。

root分区通常挨着boot分区往后分布,这个分区一般会设置的大一些,分区格式用到也比广泛,比较常见的使用EXT4格式。root分区用于存放根文件系统和用户应用程序等。

根据上面的原理通常制作基于SD卡的嵌入式Linux系统镜像制作可以分为两种方式:

  • 从现有SD卡备份
    先把所有要用到的内容都烧录到SD卡上,使之可以在设备上正常运行。然后取出SD卡,从头开始备份数据生成镜像文件。
  • 从空白文件开始制作
    从空白文件开始对其分区,然后导入各部分对应内容来制作系统镜像。

环境准备

下载安装Ubuntu Desktop(这里使用版本为20.04.4):
https://ubuntu.com/download/desktop

安装完成后进行基础环境安装与设置:

sudo apt update
sudo apt install -y build-essential
sudo apt install -y kpartx
sudo apt install -y gparted

以下根据需求安装:

sudo apt install -y openssh-server

如果使用虚拟机的话为了方便可以开启共享粘贴板、共享目录以及桥接网络。


如果是 从现有SD卡备份 ,并且所有数据都是在boot分区和root分区中的话,可以直接在Windows上操作,主要需要用到下面两个软件:
DiskGenius:http://www.diskgenius.cn
Win32 Disk Imager:https://win32diskimager.org/

系统镜像制作

从现有SD卡备份

首先将SD卡插入电脑上,使用 dh -h 命令或是 GParted 工具查看SD卡分区分布:
嵌入式Linux系统镜像制作(基于SD卡)_第2张图片
嵌入式Linux系统镜像制作(基于SD卡)_第3张图片
上面是两个不同的嵌入式Linux设备的SD卡内容分布。

对于第一个图的SD卡而言,在备份前要稍作调整。从图上可以看到该SD卡中rootfs分区占满了SD卡剩余的空间(14.58GiB),但是实际上才使用了(2.50GiB)。这种情况下使用备份方式制作的系统镜像体积会很大。所以需要对该分区大小进行调整。

(注意该操作可能会不可逆的损坏系统!!!)在 GParted 工具中选中rootfs分区,(如已挂载,右键 Unmount 卸载该分区),右键 Resize/Move 调整分区大小,大小根据需要而定,也不能设置的太小,对于上面清明而言设置为5GiB大小基本就够用了。调整大小后需要点击上面的 来套用更改

接下来就可以在终端中通过 dd 命令来备份制作系统镜像了:

# 先将SD卡插入Ubuntu中
# 使用 lsblk 查看SD卡设备号sdX
# 我这里显示为sdb,下面均以此进行说明

# 如果已经分过区了那么Ubuntu可能会自动挂载
# 逐条使用 sudo umount /dev/sdbn 进行卸载
# 比如上面只有两个分区就用 sudo umount /dev/sdb1 和 sudo umount /dev/sdb2 

# 创建镜像文件
touch image.bin
# 将SD卡中有用的数据写入到镜像文件中
# 对于上面第一个SD卡而言大小可以设置为如下
# 1M + 4M + 256M(boot) + 5*1024M(rootfs调整后大小) + 2M(多拷贝点,防止不明意外)
# 对于上面第二个SD卡而言大小可以设置为如下
# 1M + 40M(boot) + 160M(rootfs) + 2M(多拷贝点,防止不明意外)
# 将if后设备中数据从头开始拷贝到of后文件中,count表示拷贝大小,单位由bs指定
# 数据比较大会花点时间
sudo dd if=/dev/sdb of=image.bin bs=1M count=203 status=progress

# 系统镜像比较大,可以压缩为zip格式来减少大小
# zip image.zip image.bin 
# 有些时候使用xz方式压缩体积可能会小很多

这个方法操作比较简单,不过有时候可能会有问题,特别是前面哪种需要调整分区大小的情况下出现问题的概率会更加频繁些。


如果所有数据都是在boot分区和root分区中的话,可以直接在Windows上操作,使用 DiskGenius 来调整分区大小,使用 Win32 Disk Imager 读取已分配分区来创建系统镜像。

从空白文件开始制作

从空白文件制作主要过程如下:

  • 建立一个空文件;
  • 作为块设备挂载;
  • 进行分区;
  • 向各个分区拷贝需要的文件;
  • 根据需求向头部添加bootloader;
# 创建镜像文件
touch image.bin
# 向镜像文件写入空数据固定大小
dd if=/dev/zero of=image.bin bs=1M count=235

# 查看可用的设备挂载点
sudo losetup -f
# 我这里显示 /dev/loop13,接下来都以此进行说明

# 将镜像文件挂载到 /dev/loop13
sudo losetup /dev/loop13 image.bin 

# 对挂载的设备进行分区
sudo fdisk /dev/loop13
# 下面是我新建的两个分区的输入情况
# n回车  回车(p)  回车(1)  回车(2048)  +32M回车  (如果有额外提示则Y回车)
# n回车  回车(p)  回车(2)  回车(67584) +200M回车  (如果有额外提示则Y回车)
# 接着 a回车 1回车 (将分区1设置为可启动)
# 输入 w 回车保存退出,会有点问题提示不用管

# 更新分区表
sudo kpartx -av /dev/loop13

# 格式化分区建立文件系统
sudo mkfs.vfat /dev/mapper/loop13p1 -n boot
sudo mkfs.ext4 /dev/mapper/loop13p2 -L rootfs

# 挂载boot分区
sudo mount /dev/mapper/loop13p1 /mnt/
# 用喜欢的方式向里拷贝内核镜像、设备树、内核启动参数文件等
# sudo rsync -a src/ /mnt/ --progress
# sudo cp -rfp src/* /mnt/
sync
sudo umount /dev/mapper/loop13p1

# 挂载root分区
sudo mount /dev/mapper/loop13p2 /mnt
# 用喜欢的方式向里拷贝根文件系统、用户应用程序等
# sudo rsync -a src/ /mnt/ --progress
# sudo cp -rfp src/* /mnt/
sync
sudo umount /dev/mapper/loop13p2

# 卸载设备
sudo kpartx -d /dev/loop13
sudo losetup -d /dev/loop13

# 如果头部有bootloader,则写入bootloader
# bs=512 seek=1用于跳过头部512字节(具体需要跳过多少是需要根据芯片而定的)
# sudo dd if=bootloader.bin  of=image.bin bs=512 seek=1 conv=notrunc
# 也可以从现有的SD卡复制bootloader
# 有的芯片bootloader默认需要使用官方提供的工具来烧写,因为会在头部加入DDR参数等内容,这种时候用这个方法就比较合适
# sudo dd if=/dev/sdb of=image.bin bs=512 seek=1 count=2047 conv=notrunc
# 注意conv=notrunc选项参数一定不能漏

# 系统镜像比较大,可以压缩为zip格式来减少大小
# zip image.zip image.bin 
# 有些时候使用xz方式压缩体积可能会小很多

系统镜像使用

在Linux上可以直接使用 dd 命令将镜像文件写入SD卡中:

# 如果分区已挂载到别的地方先进行卸载
# sudo umount /dev/sdb1
# sudo umount /dev/sdb2

# 解压镜像文件压缩包
# unzip image.zip
# 将镜像文件写入SD卡,写入可能较慢
sudo dd if=image.bin of=/dev/sdb bs=1M

当然我更加推荐使用工具来烧录,这里 推荐使用 BalenaEtcher 工具 ,这个工具支持windos、macos、linux,其官方页面和项目地址分别如下:
https://www.balena.io/etcher/
https://github.com/balena-io/etcher
嵌入式Linux系统镜像制作(基于SD卡)_第4张图片

其它补充

有时候可能完全按照上面方式操作可能镜像烧录后无法正常运行,比如树莓派CM4模块使用目前版本的系统(Raspberry Pi OS Lite(32-bit) 2022-04-04),不管用上面哪种方式操作都有问题,启动后无法挂载分区。检查相关位置发现现在系统中使用PARTUUID来指定要挂载的分区,我是用 sudo blkid 查看PARTUUID,然后修改相关位置( /boot/cmdline.txt/etc/fstab ),结果也没法正常启动。

上面情况下目前发现可行的方案是不调整分区大小的情况下使用 Win32 Disk Imager 整卡备份,生成的镜像烧录到同型号的或是更大容量规格的SD卡上使用就没问题。如果觉得镜像大的话可以对镜像文件进行压缩, BalenaEtcher 可以直接使用压缩包来烧录。
嵌入式Linux系统镜像制作(基于SD卡)_第5张图片

总结

上面介绍了基于SD卡的嵌入式Linux系统镜像制作,了解原理后其实过程并不复杂。
如果是基于Flash情况,制作镜像的过程也差不多,主要的差异是空间分布的不同,完全可以根据各自的情况来制作。

你可能感兴趣的:(嵌入式Linux与设备相关,linux,服务器,运维)