3.4制作根文件系统

3.4 制作根文件系统

基于ubuntu基础镜像

根文件系统(Root File System)是Linux操作系统的核心组成部分,包含启动和运行系统所需的全部目录结构和关键文件。它是内核挂载的第一个文件系统,提供了/bin、/etc、/lib等基础目录,是用户空间程序运行的基础环境。

Ubuntu-base是由Ubuntu官方提供的轻量化根文件系统基础镜像,其设计遵循最小化原则,核心组件仅包含Debian软件包管理器(APT)及必要的运行时依赖,整体体积通常控制在数十兆级别。该镜像深度集成Ubuntu软件仓库生态,能够便捷地通过apt-get install指令扩展功能,具备高度的可定制性与稳定性,因而广泛应用于嵌入式设备的根文件系统(rootfs)构建场景。

在嵌入式开发领域,常见的文件系统构建方案(如BusyBox、Yocto、Buildroot等)通常需要开发者手动管理依赖链与编译环境,其灵活性与维护成本存在显著权衡。相比之下,Ubuntu-base依托成熟的APT包管理体系,可直接调用逾数万款经过严格测试的预编译软件包,大幅降低系统扩展复杂度。同时,Ubuntu活跃的开发者社区及长期支持(LTS)策略,进一步保障了嵌入式系统的安全更新与生态兼容性。

Ubuntu-base支持多架构跨平台部署,涵盖x86_64、ARM(32/64位)等多种指令集。本文以ARM64架构为范例详细阐述构建流程。核心目标是,基于Ubuntu-base最小化镜像,通过模块化软件包安装与配置,逐步构建包含Xenomai用户层地完整Ubuntu ARM64根文件系统。此根文件系统既能用于演示Xenomai用户层编程与实践,同时也可适应于真正的嵌入式系统。

3.4.1 准备工作

假定当前工作目录为/root/xenomai/rootfs

目标是生成一个 linuxroot.img块文件,作为根文件系统,传递给QEMU ARM64 qemu-system-aarch64

1. 获取Ubuntu Core基础镜像

从Ubuntu官方仓库下载:ubuntu-base-22.04.5-base-arm64.tar.gz

http://cdimage.ubuntu.com/ubuntu-base/releases/22.04.5/release/

bash 复制代码
rm -fr ./temp; mkdir ./temp
tar -xpf ubuntu-base-22.04.5-base-arm64.tar.gz -C ./temp

ls temp/
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
  • -p:保留文件权限,避免后续权限错误。

  • -C:指定解压目录,确保目录结构完整。

2. 安装QEMU实现跨架构仿真
bash 复制代码
sudo apt-get install qemu-user-static

注入QEMU仿真器

bash 复制代码
sudo cp /usr/bin/qemu-aarch64-static temp/usr/bin/  # 注入QEMU仿真器

同时需QEMU支持跨架构命令执行。

3. 配置网络
bash 复制代码
sudo cp -b /etc/resolv.conf temp/etc/resolv.conf  # 同步DNS配置

在chroot环境中,需解析域名以下载软件包.

4. 替换软件源
bash 复制代码
sudo sed -i 's/ports.ubuntu.com/mirrors.ustc.edu.cn/g' temp/etc/apt/sources.list

设置国内ports源

设置Ubuntu arm源

根据ubuntu | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror

本镜像仅包含 32/64 位 x86 架构处理器的软件包,在 ARM(arm64, armhf)、PowerPC(ppc64el)、RISC-V(riscv64) 和 S390x 等架构的设备上(对应官方源为ports.ubuntu.com)请使用 ubuntu-ports 镜像。

实际使用中科大的源比较好用

方法:Ubuntu 源使用帮助 --- USTC Mirror Help 文档

实际使用源:Ubuntu Ports 源使用帮助 --- USTC Mirror Help 文档

http://mirrors.ustc.edu.cn/ubuntu-ports

3.4.2 chroot根文件系统

创建mount.sh脚本:

bash 复制代码
#!/bin/bash

function mnt() {
    echo "MOUNTING"
    sudo mount -t proc /proc ${2}proc
    sudo mount -t sysfs /sys ${2}sys
    sudo mount -o bind /dev ${2}dev
    sudo mount -B /dev/pts ${2}dev/pts

    sudo chroot ${2}
}

function umnt() {
    echo "UNMOUNTING"
    sudo umount ${2}proc
    sudo umount ${2}sys
    sudo umount ${2}dev/pts
    sudo umount ${2}dev
}

if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
    mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
    umnt $1 $2
else
    echo ""
    echo "Either 1'st, 2'nd or both parameters were missing"
    echo ""
    echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
    echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
    echo ""
    echo "For example: ch-mount -m /media/sdcard/"
    echo ""
    echo 1st parameter : ${1}
    echo 2nd parameter : ${2}
fi

创建mount.sh脚本并执行:

bash 复制代码
sudo ./mount.sh -m temp/

挂载点解析:

/proc:提供进程和内核状态信息,ps、top等工具依赖此目录。

/sys:暴露硬件设备与驱动信息,用于动态设备管理。

/dev:设备文件入口,如/dev/sda对应磁盘设备。

chroot的作用:

将当前进程的根目录切换到指定路径,实现隔离的"虚拟系统",便于安全地进行系统级修改。

此时已经进入到了ARM64根文件系统,通过lscpu命令可以确认当前的处理器架构aarch64

bash 复制代码
lscpu
Architecture:            aarch64
  CPU op-mode(s):        32-bit, 64-bit
  Address sizes:         43 bits physical, 48 bits virtual
  Byte Order:            Little Endian
CPU(s):                  8
  On-line CPU(s) list:   0-7
Vendor ID:               GenuineIntel
  Model name:            12th Gen Intel(R) Core(TM) i5-12500H
    CPU family:          6
    Model:               154
    Thread(s) per core:  1
    Core(s) per cluster: 8
    Socket(s):           128
    Cluster(s):          1
    Stepping:            3
    BogoMIPS:            6220.81
    Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1g
                         b rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid tsc_known_freq pni pclmulqdq vmx ssse
                         3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dn
                         owprefetch invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx
                         2 smep bmi2 invpcid rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xsaves arat pku ospke md_clear flush_l1d arch_
                         capabilities
Virtualization features:
  Virtualization:        VT-x
Caches (sum of all):
  L1d:                   384 KiB (8 instances)
  L1i:                   256 KiB (8 instances)
  L2:                    10 MiB (8 instances)
  L3:                    18 MiB (1 instance)
NUMA:
  NUMA node(s):          1
  NUMA node0 CPU(s):     0-7
Vulnerabilities:
  Itlb multihit:         KVM: Mitigation: VMX disabled
  L1tf:                  Not affected
  Mds:                   Not affected
  Meltdown:              Not affected
  Mmio stale data:       Not affected
  Spec store bypass:     Mitigation; Speculative Store Bypass disabled via prctl and seccomp
  Spectre v1:            Mitigation; usercopy/swapgs barriers and __user pointer sanitization
  Spectre v2:            Mitigation; Enhanced IBRS, IBPB conditional, RSB filling
  Srbds:                 Not affected
  Tsx async abort:       Not affected

3.4.3 定制根文件系统

1. 更新系统与安装必备软件
bash 复制代码
apt update && apt upgrade -y
apt install systemd -y
apt install sudo -y
apt install vim -y
apt install net-tools -y 
apt install ethtool -y
apt install ifupdown -y
apt install iputils-ping -y
apt install htop -y
apt install openssh-server -y

为何必须安装systemd?

Ubuntu 20.04采用systemd作为初始化系统(init),负责启动守护进程、挂载文件系统等核心任务。缺少systemd将导致系统无法正常启动。

2. 主机名与用户管理
bash 复制代码
echo xeno-demo > /etc/hostname

passwd root

useradd -s '/bin/bash' -m -G adm,sudo xeno
passwd xeno
  • 启用root账户,并设置密码
  • 创建用户xeno并加入sudo组,并设置密码
    • -s:指定用户默认Shell。
    • -m:自动创建家目录(/home/xeno)。
    • -G:附加用户到管理员组(adm)和超级用户组(sudo)。
3. 配置网络
(1)配置IP地址

在 Ubuntu 系统中,/etc/network/interfaces 文件支持从 /etc/network/interfaces.d/ 目录加载额外的配置文件。这样可以将不同网络接口的配置分离到独立的文件中,便于管理和维护。

以下是为 eth0(以太网接口)创建配置文件的步骤:

/etc/network/interfaces.d/ 目录下创建eth0.cfg,并添加以下内容,使用 DHCP 动态获取 IP 地址。

plaintext 复制代码
auto eth0
iface eth0 inet dhcp
  • auto eth0:表示系统启动时自动启用 eth0 接口。
  • iface eth0 inet dhcp:配置 eth0 使用 DHCP 协议动态获取 IP 地址。

使用vi编辑eth0.cfg,或直接用如下命令快速追加内容。

bash 复制代码
echo "auto eth0" > /etc/network/interfaces.d/eth0.cfg
echo "iface eth0 inet dhcp" >> /etc/network/interfaces.d/eth0.cfg
(2)SSH登录配置

在上面的步骤中,已经安装了openssh-server软件包,默认会启动sshd服务。

默认情况下,sshd不允许root账号登录。如果希望使用root账号ssh登录QEMU虚拟机,则需要修改配置。

bash 复制代码
echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/permitroot.conf

PermitRootLogin yes 写入到 /etc/ssh/sshd_config.d/permitroot.conf 文件中。这样做的目的是启用 SSH 服务对 root 用户的登录权限,而不需要直接修改主配置文件 /etc/ssh/sshd_config。SSHD支持从 /etc/ssh/sshd_config.d/ 目录加载额外的配置文件。这些文件的内容会被合并到主配置文件 /etc/ssh/sshd_config 中。

4. 设置console控制台

ARM64默认使用ttyAMA0,而Ubuntu默认仅启动tty1,因此默认情况下,使用此文件系统会遇到如下问题:

bash 复制代码
[ TIME ] Timed out waiting for device /dev/ttyAMA0. 
[DEPEND] Dependency failed for Serial Getty on ttyAMA0

如果安装了Ubuntu的桌面,例如apt install lubuntu-desktop,实测是不会出现此问题的。其背后的原理在于systemd-getty-generator会自动生成并启动getty@ttyAMA0.service服务:

bash 复制代码
/run/systemd/generator/getty.target.wants/serial-getty@ttyAMA0.service

本文仅使用串口登录,并不安装Ubuntu桌面(一般占用2~3GB空间)。为了解决此问题,需要手动生成getty@ttyAMA0.service服务文件:

bash 复制代码
ln -s /lib/systemd/system/getty\@.service /etc/systemd/system/getty.target.wants/getty\@ttyAMA0.service

sed -i 's/TTYVTDisallocate=yes/TTYVTDisallocate=no/g' /lib/systemd/system/getty\@.service
  • ln命令创建一个符号链接,将 getty@.service 模板服务实例化为 getty@ttyAMA0.service,以便在 ttyAMA0 终端上启用登录服务。
  • sed命令,修改配置文件getty@.service,将 TTYVTDisallocate 的值从 yes 改为 no,禁止在登录时清屏,以保留内核启动信息。

3.4.4 退出chroot根文件系统

bash 复制代码
exit

sudo ./mount.sh -u temp/

在完成上述定制后,在ARM64根文件系统下,执行exit命令退出并返回到主机根文件系统。

最后务必调用./mount.sh -u取消/proc等之前已经挂载的虚拟文件系统。

3.4.5 添加Xenomai用户层库和工具

在上一章节,已经完成了Xenomai用户层的编译,并临时安装到了/root/xenomai/xenomai-v3.2.4-install目录。将其中的/usr/xenomai目录完整的拷贝到temp/usr目录下。

bash 复制代码
cp -af /root/xenomai/xenomai-v3.2.4-install/usr/xenomai/ ./temp/usr/

在 Linux 系统中,/etc/profile.d/ 目录用于存放用户登录时自动加载的脚本文件。这些脚本会在用户启动 shell 时执行,主要用于设置全局环境变量或运行初始化命令。通过在此目录下创建 xenomai.sh 文件并添加相关配置,可以为 Xenomai 实时框架设置必要的环境变量。

bash 复制代码
export XENOMAI_ROOT_DIR=/usr/xenomai
export XENOMAI_PATH=/usr/xenomai
export PATH=$PATH:$XENOMAI_PATH/bin:$XENOMAI_PATH/sbin:$XENOMAI_PATH/demo
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$XENOMAI_PATH/lib/pkgconfig
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$XENOMAI_PATH/lib
export OROCOS_TARGET=xenomai
  • export PATH=$PATH:$XENOMAI_PATH/bin:$XENOMAI_PATH/sbin

    将 Xenomai 的 binsbin 目录添加到系统的 PATH 环境变量中,使用户能够直接执行 Xenomai 的命令和工具。

  • export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$XENOMAI_PATH/lib/pkgconfig

    添加 Xenomai 的 pkgconfig 目录到 PKG_CONFIG_PATH,以便编译时能够找到 Xenomai 库的配置文件。

  • export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$XENOMAI_PATH/lib

    将 Xenomai 的库路径添加到 LD_LIBRARY_PATH,确保运行时可以正确加载 Xenomai 的共享库。

  • export OROCOS_TARGET=xenomai

    设置 OROCOS 实时框架的目标平台为 xenomai,以适配 Xenomai 实时内核。

3.4.6 生成块设备

查看temp目录的大小,大概在600MB左右。

bash 复制代码
du -hs temp
573M    temp

通过如下命令,生成一个1GB大小的linuxroot.imgRAW块文件,格式化为ext4文件系统后,把temp目录的内容拷贝其中。

bash 复制代码
dd if=/dev/zero of=linuxroot.img bs=1M count=1024
mkfs.ext4 linuxroot.img

mkdir -p rootfs
sudo mount linuxroot.img rootfs/
sudo cp -rfp temp/* rootfs/
sudo umount rootfs/
e2fsck -p -f linuxroot.img

最终,linuxroot.img可以作为根文件系统,传递给QEMU ARM64 qemu-system-aarch64

如果需要更新根文件系统的内容:

  • 可以先更新temp目录,然后重新生成linuxroot.img
  • 直接把linuxroot.img挂载到临时目录,修改其中的内容。
相关推荐
无聊到发博客的菜鸟6 天前
STM32 手册寄存器属性
stm32·单片机·嵌入式·rtos·寄存器
aspirestro三水哥6 天前
5.3RTDM用户层驱动
rtos·xenomai
无聊到发博客的菜鸟6 天前
STM32 RTC时钟不准的问题
stm32·嵌入式·rtc·rtos
aspirestro三水哥9 天前
4.7POSIX进程与线程实例
rtos·xenomai
无聊到发博客的菜鸟9 天前
使用STM32对SD卡进行性能测试
stm32·单片机·rtos·sd卡·fatfs
切糕师学AI11 天前
Azure RTOS ThreadX 简介
microsoft·嵌入式·azure·rtos
切糕师学AI15 天前
FreeRTOS是什么?
嵌入式·rtos
aspirestro三水哥16 天前
3.5启动QEMUARM64虚拟机
rtos·xenomai
时光の尘17 天前
嵌入式面试八股文(十九)·裸机开发与RTOS开发的区别
linux·stm32·单片机·iic·rtos·spi
aspirestro三水哥19 天前
3.2编译Xenomai内核
rtos·xenomai