11. Ubuntu 根文件系统构建

借助LubanCat-SDK我们可以方便的一键构建Ubuntu镜像,但是Ubuntu根文件系统的构建过程是相对独立的,不依赖SDK的其他部分。

我们可以使用Ubuntu官方的Ubuntu-base根文件系统来构建适配我们板卡的根文件系统。

我们的Ubuntu构建仓库由很多构建脚本组成,可以最大化的减少人工操作, 使构建的根文件系统具有一致性,同时我们的构建脚本默认配置是Ubuntu22.04,只有修改下版本可以支持Ubuntu18.04/20.04版本的构建。

目前Ubuntu22.04为我们主要支持的版本,本文档也是以Ubuntu22.04为例进行讲解的,

注解

如果不同版本的Ubuntu镜像构建环境可能存在依赖问题,如果构建不成功请参考 Docker构建根文件系统 章节

11.1. Ubuntu系统支持情况

11.1.1. LubanCat-sg200x

主要支持ubuntu 22.04 “Jammy”

  • lite无桌面版本

11.2. 什么是Ubuntu Base

Ubuntu 针对不同的 CPU 架构提供相应的 Ubuntu base 根文件系统,目前提供的架构有amd64、arm64、armhf、i386、s390x、ppc64.

Ubuntu Base 是用于为特定需求创建自定义映像的最小rootfs,是Ubuntu可以运行的最小环境。

Ubuntu Base 的下载地址是 http://cdimage.ubuntu.com/ubuntu-base/releases

11.3. 拉取Ubuntu构建仓库

SDK中包含了Ubuntu源码,不做单独仓库存放,需要获取SDK从而获取Ubuntu源码。

可以参考: SDK源码获取 章节。

拉取完成SDK后进入ubuntu目录下,有以下文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
ls -hgG

-rwxrwxr-x 1  765 Aug  1 09:46 ch-mount.sh
drwxrwxr-x 2 4.0K Aug  1 09:46 Docker
-rwxrwxr-x 1 4.0K Aug  1 09:46 mk-base-ubuntu.sh
-rwxrwxr-x 1  827 Aug  1 09:46 mk-image.sh
-rwxrwxr-x 1 6.0K Aug  1 09:46 mk-ubuntu-rootfs.sh
drwxrwxr-x 4 4.0K Aug  1 09:46 overlay
drwxrwxr-x 3 4.0K Aug  1 09:46 overlay-firmware
drwxrwxr-x 7 4.0K Aug  1 09:46 overlay-sophgo-arm
drwxrwxr-x 7 4.0K Aug  1 09:46 overlay-sophgo-riscv64
-rwxrwxr-x 1 1.8K Aug  1 09:46 post-build.sh
-rw-rw-r-- 1 1.2K Aug  1 09:46 readme.md
-rw-rw-r-- 1  962 Aug  1 09:46 sources.list
drwxrwxr-x 3 4.0K Aug  1 09:46 ubuntu-build-service
  • mk-base-ubuntu.sh:在Ubuntu Base上安装软件包,以构建基础的ubuntu根文件系统。

  • mk-ubuntu-rootfs.sh:在基础的lite版本根文件系统上添加定制内容。

  • mk-image.sh:将根文件系统打包成img镜像文件

  • sources.list:软件源地址

  • overlay:arm核和riscv核通用的文件,主要是rootfs中的配置文件。

  • overlay-sophgo-arm:只能在arm核用的文件,包括一些动态库,可运行程序等。

  • overlay-sophgo-riscv64:只能在riscv核用的文件,包括一些动态库,可运行程序等。

  • overlay-firmware:主要是wifi/bt/固件,默认没有用。

  • ubuntu-build-service:用于搭建构建环境的依赖文件。

目前构建脚本仅支持lite版本的镜像构建

  • lite:无桌面,终端版

11.4. Ubuntu根文件系统构建流程

Ubuntu根文件系统的构建主要分为四个步骤

第一步:从Ubuntu官网下载对应版本的最小rootfs,即下载Ubuntu Base

第二步:在最小rootfs的基础上安装常用的软件包和工具,这个过程完成后得到基础根文件系统。

第三步:在第二步的基础上,我们进一步添加基于sg200x处理器进行定制。

第四步:将构建完成的根文件系统打包成img格式,方便烧录和下一步处理。

一、二步使用mk-base-ubuntu.sh脚本来实现;

第三步使用mk-ubuntu-rootfs.sh来实现;

第四步通过mk-image.sh来实现,在第三步脚本的末尾自动调用。

11.5. 搭建构建环境

在ubuntu目录下执行以下命令

1
2
3
sudo apt-get install binfmt-support qemu-user-static
sudo dpkg -i ubuntu-build-service/packages/*
sudo apt-get install -f

上面的命令执行过程中可能有警告或报错,这是正常现象,我们直接忽略报错即可。

11.6. 构建base镜像

我们以ubuntu-base镜像为基础,构建我们自己的base镜像。

这里我们自己的base镜像是指以ubuntu-base镜像为基础,安装了我们指定软件包, 并进行一些基础设置,如用户名、密码、用户组、时区等配置的根文件系统。

理论上这个根文件系统已经能在我们的板卡上运行了,不过还没有添加针对板卡的配置, 如网络,显示等,只能运行核心的服务。

下面我们来看一下具体的构建过程:

11.6.1. 构建基础根文件系统

我们在ubuntu目录下运行下面的命令

1
2
3
4
5
#如果是riscv核
sudo ./mk-base-ubuntu.sh riscv64

#如果是arm核
sudo ./mk-base-ubuntu.sh armhf

以riscv64为例,等待命令结束以后我们看一下ubuntu目录下的文件变化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
drwxr-xr-x 17 root  root       4096 Aug  7 01:19 binary
-rwxrwxr-x  1 guest guest       765 Aug  7 01:15 ch-mount.sh
drwxrwxr-x  2 guest guest      4096 Aug  7 01:15 Docker
-rwxrwxr-x  1 guest guest      4089 Aug  7 01:15 mk-base-ubuntu.sh
-rwxrwxr-x  1 guest guest       827 Aug  7 01:15 mk-image.sh
-rwxrwxr-x  1 guest guest      6420 Aug  7 01:18 mk-ubuntu-rootfs.sh
drwxrwxr-x  4 guest guest      4096 Aug  7 01:15 overlay
drwxrwxr-x  3 guest guest      4096 Aug  7 01:15 overlay-firmware
drwxrwxr-x  7 guest guest      4096 Aug  7 01:15 overlay-sophgo-arm
drwxrwxr-x  8 guest guest      4096 Aug  7 01:15 overlay-sophgo-riscv64
-rwxrwxr-x  1 guest guest      1799 Aug  7 01:15 post-build.sh
-rw-rw-r--  1 guest guest      1163 Aug  7 01:15 readme.md
-rw-rw-r--  1 guest guest       962 Aug  7 01:15 sources.list
-rw-r--r--  1 root  root   27279767 Feb 16 22:31 ubuntu-base-22.04.4-base-riscv64.tar.gz
-rw-r--r--  1 root  root  137700365 Aug  7 01:26 ubuntu-base-lite-riscv64-20240807.tar.gz
drwxrwxr-x  3 guest guest      4096 Aug  7 01:15 ubuntu-build-service

我们看到,新增了三个文件

  • binary:存放解压后的根文件系统,构建过程在这个文件夹中进行

  • ubuntu-base-22.04.4-base-riscv64.tar.gz:在cdimage.ubuntu.com下载的ubuntu-base根文件系统压缩包

  • ubuntu-base-lite-riscv64-20240807.tar.gz:我们通过上述脚本构建好的基础版根文件系统后的压缩包

上面的命令是使用 ./mk-base-ubuntu.sh 脚本来构建根文件系统。 它具体的工作流程如下:

  • 下载指定版本的ubuntu-base根文件系统压缩包并解压

  • 向解压后的根文件系统添加软件源、DNS服务

  • 根据架构添加模拟器

  • 使用chroot来修改根文件系统
    • 升级并安装系统软件包

    • 创建用户和密码,并设置权限

    • 设定主机名、时区、服务项设置等

    • 清理软件安装缓存

  • 将设置好的根文件系统打包成压缩包便于保存管理

经过以上步骤,一个基础版本的根文件系统就制作好了

11.7. 构建完整的Ubuntu根文件系统镜像

完整版的Ubuntu根文件系统镜像主要是添加了Rockchip overlay层, 里面主要是一些配置文件和固件,用于添加或覆盖根文件系统中原有的配置文件, 以达到我们想要的定制效果。

11.7.1. 根文件系统构建

我们在ubuntu目录下,根据想要构建的根文件系统版本

1
2
3
4
5
#如果是riscv核
sudo ./mk-ubuntu-rootfs.sh riscv64

#如果是arm核
sudo ./mk-ubuntu-rootfs.sh armhf

上述命令中的脚本在最后调用了./mk-image.sh脚本,会自动将binary文件夹中的文件打包成img镜像:

等打包过程结束以后我们看一下ubuntu目录下的文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
drwxr-xr-x 18 root  root       4096 Aug  7 01:36 binary
-rwxrwxr-x  1 guest guest       765 Aug  7 01:15 ch-mount.sh
drwxrwxr-x  2 guest guest      4096 Aug  7 01:15 Docker
-rwxrwxr-x  1 guest guest      4089 Aug  7 01:15 mk-base-ubuntu.sh
-rwxrwxr-x  1 guest guest       827 Aug  7 01:15 mk-image.sh
-rwxrwxr-x  1 guest guest      6420 Aug  7 01:18 mk-ubuntu-rootfs.sh
drwxrwxr-x  4 guest guest      4096 Aug  7 01:15 overlay
drwxrwxr-x  3 guest guest      4096 Aug  7 01:15 overlay-firmware
drwxrwxr-x  7 guest guest      4096 Aug  7 01:15 overlay-sophgo-arm
drwxrwxr-x  8 guest guest      4096 Aug  7 01:15 overlay-sophgo-riscv64
-rwxrwxr-x  1 guest guest      1799 Aug  7 01:15 post-build.sh
-rw-rw-r--  1 guest guest      1163 Aug  7 01:15 readme.md
drwxr-xr-x  2 root  root       4096 Aug  7 01:36 rootfs
-rw-rw-r--  1 guest guest       962 Aug  7 01:15 sources.list
-rw-r--r--  1 root  root   27279767 Feb 16 22:31 ubuntu-base-22.04.4-base-riscv64.tar.gz
-rw-r--r--  1 root  root  137700365 Aug  7 01:26 ubuntu-base-lite-riscv64-20240807.tar.gz
drwxrwxr-x  3 guest guest      4096 Aug  7 01:15 ubuntu-build-service
-rw-r--r--  1 root  root  798199808 Aug  7 01:36 ubuntu-rootfs.ext4

我们看到,新增了两个个文件

  • ubuntu-rootfs.ext4:打包好的完整根文件系统镜像

  • rootfs:mk-image.sh脚本打包的临时目录

mk-ubuntu-rootfs.sh脚本的构建流程如下:

  • 清空binary目录,并将对应的base根文件系统压缩包解压

  • 根据架构添加模拟器

  • 使用chroot来修改根文件系统

  • 升级并安装系统软件包

  • 清理软件安装缓存

  • 调用mk-image.sh脚本打包img镜像

11.8. 定制Ubuntu根文件系统

由于镜像体积的限制,我们提供的定制Ubuntu镜像预装了一部分常用软件, 但是在用户开发时可能还需要预装更多的软件,以及对根文件系统做进一步的定制。 以下部分将对根文件系统的修改做具体说明。

11.8.1. 添加预装软件包

比如我们想要预装git和vim到根文件系统中, 则可以在mk-base-ubuntu.sh添加以下内容

1
2
3
4
5
6
export APT_INSTALL="apt-get install -fy --allow-downgrades"
#添加的位置在 export APT_INSTALL 下一行

#添加的内容是
echo -e "\033[47;36m ---------- LubanCat -------- \033[0m"
\${APT_INSTALL} git vim

11.8.2. 添加外设firmware

如果我们使用无线网卡这样的外设,就需要向根文件系统中添加网卡的firmware, 这时直接将对应的firmware存放在overlay-sophgo-xxx/usr/lib/firmware目录下,按根文件系统中的路径保存。

11.8.3. 添加服务项及配置文件

我们希望对有些服务项的配置进行自定义,就可以在overlay/目录下添加对应的配置文件。 制作根文件系统的过程中,在添加overlay层的时候,就会添加或替换根文件系统中原有的配置文件, 以实现对配置文件自定义的效果。

这里我们以网络配置工具netplan的配置为例,netplan的配置文件在根文件系统的/etc/netplan/目录下, 这里对应的是overlay/etc/netplan/目录。

我们在overlay/etc/netplan/中新建一个名为01-network-manager-all.yaml的文件,在文件中添加以下内容:

1
2
3
4
5
6
7
network:
    renderer: NetworkManager
    ethernets:
        eth0:
            dhcp4: true
        eth1:
            dhcp4: true

上面文件的内容是使用netplan配置网络管理器为NetworkManager, 设置eth0都开启dhcp。

在添加配置文件以后,我们重新构建镜像,再烧录到板卡启动,就可以达到我们想要的网络配置效果。

注意

如果添加的是shell脚本,需要在创建文件后修改文件权限为775,否则在根文件系统中可能无法执行。

11.8.4. 重新打包根文件系统镜像

在对根文件系统的构建脚本做出修改之后,我们要重新打包镜像。

  • 如果我们没有修改 mk-base-ubuntu.sh 脚本中的内容,则不需要重复构建base镜像部分。如果修改了则需要重新构建基础根文件系统

1
2
3
4
5
#如果是riscv核
sudo ./mk-base-ubuntu.sh riscv64

#如果是arm核
sudo ./mk-base-ubuntu.sh armhf
  • 如果修改了mk-ubuntu-rootfs.sh、overlay的内容,则需要执行以下命令

1
2
3
4
5
#如果是riscv核
sudo ./mk-ubuntu-rootfs.sh riscv64

#如果是arm核
sudo ./mk-ubuntu-rootfs.sh armhf

11.9. 使用LubanCat-SDK一键构建

在完成 拉取Ubuntu构建仓库搭建构建环境 这两个步骤以后, 我们也可以直接使用一键构建命令,就可以构建我们提供的定制根文件系统镜像了, 还可以借助SDK的镜像打包功能,将U-boot、内核等部分也一并打包成一个完整的系统镜像。

11.9.1. SDK配置文件说明

板级配置文件中sophon-image-build/build/boards/sg200x/sg200x_lubancat_xxx_ubuntu_xxx/sg200x_lubancat_xxx_ubuntu_xxx_defconfig添加与Ubuntu根文件相关的设置。

1
CONFIG_UBUNTU_FS=y

设置了CONFIG_UBUNTU_FS即使用ubuntu文件系统。

11.9.2. 自动构建脚本

Ubuntu根文件系统的一键构建功能,主要由sophon-image-build/build/Makefile实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# UBUNTU_OVERLAY_DIR
# UBUNTU_ROOTFS_RAWIMAGE
ebf-ubuntu-rootfs-prepare:export CROSS_COMPILE_KERNEL=$(patsubst "%",%,$(CONFIG_CROSS_COMPILE_KERNEL))
ebf-ubuntu-rootfs-prepare:export CROSS_COMPILE_SDK=$(patsubst "%",%,$(CONFIG_CROSS_COMPILE_SDK))
ebf-ubuntu-rootfs-prepare:
    $(call print_target)
    # copy ko and mmf libs
ifeq ($(patsubst "%",%,$(CONFIG_ARCH)),riscv)
    ${Q}mkdir -p $(UBUNTU_OVERLAY_DIR_RISCV)/mnt/system
    ${Q}cp -arf ${SYSTEM_OUT_DIR}/* $(UBUNTU_OVERLAY_DIR_RISCV)/mnt/system/
    # strip
    ${Q}find $(UBUNTU_OVERLAY_DIR_RISCV) -name "*.ko" -type f -printf 'striping %p\n' -exec $(CROSS_COMPILE_KERNEL)strip --strip-unneeded {} \;
    ${Q}find $(UBUNTU_OVERLAY_DIR_RISCV) -name "*.so*" -type f -printf 'striping %p\n' -exec $(CROSS_COMPILE_KERNEL)strip --strip-all {} \;
    ${Q}find $(UBUNTU_OVERLAY_DIR_RISCV) -executable -type f ! -name "*.sh" ! -path "*etc*" ! -path "*.ko" -printf 'striping %p\n' -exec $(CROSS_COMPILE_SDK)strip --strip-all {} 2>/dev/null \;
else
    ${Q}mkdir -p $(UBUNTU_OVERLAY_DIR_ARM)/mnt/system
    ${Q}cp -arf ${SYSTEM_OUT_DIR}/* $(UBUNTU_OVERLAY_DIR_ARM)/mnt/system/
    # strip
    ${Q}find $(UBUNTU_OVERLAY_DIR_ARM) -name "*.ko" -type f -printf 'striping %p\n' -exec $(CROSS_COMPILE_KERNEL)strip --strip-unneeded {} \;
    ${Q}find $(UBUNTU_OVERLAY_DIR_ARM) -name "*.so*" -type f -printf 'striping %p\n' -exec $(CROSS_COMPILE_KERNEL)strip --strip-all {} \;
    ${Q}find $(UBUNTU_OVERLAY_DIR_ARM) -executable -type f ! -name "*.sh" ! -path "*etc*" ! -path "*.ko" -printf 'striping %p\n' -exec $(CROSS_COMPILE_SDK)strip --strip-all {} 2>/dev/null \;
endif

ebf-ubuntu-rootfs-pack:export TARGET_OUTPUT_DIR=$(UBUNTU_DIR)/binary
ebf-ubuntu-rootfs-pack:
    $(call print_target)
ifeq ("$(wildcard $(UBUNTU_DIR)/ubuntu-rootfs.ext4)", "")
ifeq ($(ARCH), arm)
    ${Q} cd $(UBUNTU_DIR) && echo $(PASSWORD) | sudo -S ./mk-base-ubuntu.sh armhf
    ${Q} cd $(UBUNTU_DIR) && echo $(PASSWORD) | sudo -S ./mk-ubuntu-rootfs.sh armhf
else ifeq ($(ARCH), riscv)
    ${Q} cd $(UBUNTU_DIR) && echo $(PASSWORD) | sudo -S ./mk-base-ubuntu.sh riscv64
    ${Q} cd $(UBUNTU_DIR) && echo $(PASSWORD) | sudo -S ./mk-ubuntu-rootfs.sh riscv64
else
    ${Q}echo No ubuntu file system
endif
endif
    # ${Q}rm -rf $(BR_ROOTFS_DIR)/*
    # copy rootfs to rawimg dir
    ${Q}cp $(UBUNTU_DIR)/ubuntu-rootfs.ext4 $(OUTPUT_DIR)/rawimages/rootfs.$(STORAGE_TYPE)
    $(call raw2cimg ,rootfs.$(STORAGE_TYPE))

ifeq ($(CONFIG_BUILDROOT_FS),y)
rootfs:br-rootfs-prepare
rootfs:br-rootfs-pack
else ifeq ($(CONFIG_UBUNTU_FS),y)
rootfs:ebf-ubuntu-rootfs-prepare
rootfs:ebf-ubuntu-rootfs-pack
else
rootfs:rootfs-pack
rootfs:
    $(call print_target)buildroot
ifneq ($(STORAGE_TYPE), sd)
    $(call raw2cimg ,rootfs.$(STORAGE_TYPE))
endif
endif

其工作流程如下

  • 首先判断使用的是buildroot、ubuntu还是默认文件系统,如果是Ubuntu文件系统则执行ebf-ubuntu-rootfs-prepare以及ebf-ubuntu-rootfs-pack

  • 在ebf-ubuntu-rootfs-prepare中执行拷贝驱动、动态库以及脚本到ubuntu的overlay-sophgo-xxx中。

  • 在ebf-ubuntu-rootfs-pack中执行判断是否存在ubuntu-rootfs.ext4,如果存在则不重复构建文件系统,如果不存在则重新构建文件系统。

  • 最后拷贝生成的ubuntu-rootfs.ext4到install对应的输出目录下。

重要

SDK一键构建时是判断ubuntu-rootfs.ext4是否存在的,如果存在则不重复构建文件系统,如果不存在则重新构建文件系统。