如何基于Buildroot和Qemu搭建嵌入式Linux系统环境

简介: 为了能够更好的学习嵌入式Linux内核,本文基于qemu-system-arm工具模拟ARM公司的VersatileExpress硬件环境,基于buildroot工具制作rootfs,并且扩展了buildroot,增加内核配置管理,定制rootfs等功能。

为了能够更好的学习嵌入式Linux内核,本文基于qemu-system-arm工具模拟ARM公司的VersatileExpress硬件环境,基于buildroot工具制作rootfs,并且扩展了buildroot,增加内核配置管理,定制rootfs等功能。


我的上一篇文章,基于Buildroot的rootfs制作,讲述了如何基于Buildroot制作一个rootfs,所以,本文对于该部分进行了省略,有需要的请移步到这篇文章。


准备工具


  1. 安装qemu


Ubuntu安装qemu比较简单,一条命令搞定.
$ sudo apt install qemu libncurses5-dev build-essential


  1. 构建Git版本库


$sudo apt install git
$mkdir ~/work/Linux/LinuxVersatile
$ git init


  1. 基于LinuxVersatile创建buildroot版本库


由于buildroot也是基于Git管理的,所以我们使用git submodule完成对buildroot的管理.


$ git submodule add git://git.busybox.net/buildroot


更新buildroot


$ git submodule update --init


基于builroot定制项目


一般情况下,对于一个特定的项目,我们要做的可能包括如下几方面:


  • 配置buildroot(比如,编译选项,toolchain,bootloader,kernel,根文件系统以及image的类型等).


  • 配置其他组件,比如linux内核、bootloader以及busybox.


  • 定制目标根文件系统.


  • 覆盖目标文件系统的某些文件(使用BR2_ROOTFS_OVERLAY).


  • 修改或者删除目标文件系统中的某些文件,或者执行默写shell命令(使用BR2_ROOTFS_POST_BUILD_SCRIPT).


  • 在生成images文件之前,执行任何命令(使用BR2_ROOTFS_POST_BUILD_SCRIPT).


  • 设置文件的访问权限和所有者(使用BR2_ROOTFS_DEVICE_TABLE).


  • 增加特定的设备文件(使用BR2_ROOTFS_STATIC_DEVICE_TABLE).


  • 增加特定的用户(使用BR2_ROOTFS_USERS_TABLES).


  • 在生成文件系统images之后,执行任何命令(使用

BR2_ROOTFS_POST_IMAGE_SCRIPT).


  • 为一些组件增加特定项目需要的补丁(使用BR2_GLOBAL_PATCH_DIR).


  • 增加特定项目需要的组件.


  • 增加特定项目需要的组件.


推荐的目录树结构


理论上,基于buildroot构建一个项目时,可以创建任何文件和目录。buildroot开发者推荐了下面的文件结构,我们可以在buildroot主目录里面创建这些文件,也可以在通过br2-external tree在 buildroot主目录之外创建下面的目录树.


+-- board/
|   +-- <company>/
|       +-- <boardname>/
|           +-- linux.config
|           +-- busybox.config
|           +-- <other configuration files>
|           +-- post_build.sh
|           +-- post_image.sh
|           +-- rootfs_overlay/
|           |   +-- etc/
|           |   +-- <some file>
|           +-- patches/
|               +-- foo/
|               |   +-- <some patch>
|               +-- libbar/
|                   +-- <some other patches>
|
+-- configs/
|   +-- <boardname>_defconfig
|
+-- package/
|   +-- <company>/
|       +-- Config.in (if not using a br2-external tree)
|       +-- <company>.mk (if not using a br2-external tree)
|       +-- package1/
|       |    +-- Config.in
|       |    +-- package1.mk
|       +-- package2/
|           +-- Config.in
|           +-- package2.mk
|
+-- Config.in (if using a br2-external tree)
+-- external.mk (if using a br2-external tree)
+-- external.desc (if using a br2-external tree)


本文基于上述目录树结构来i构建项目,并且使用br2-extern tree这种构建特定项目的模式。需要的注意的是,如果我们选择br2-extern tree模式,上述文件结构中的company和boardname显得就有些多余了.


br2-external tree模式


面一节介绍了如何构建特定项目的目录树,并且指定本项目使用br2-extern tree模式,下面介绍一下如何创建该目录树,并且说明一下如何实现相关的配置文件。需要思考的一个问题是,特定的项目 如何与buildroot进行集成呢?其实很简单,我们只需通过 BR2_EXTERNAL将目录树的位置通过make参数的形式传递给buildroot即可,待到整个项目结构搭建完成之后,我们就会看到这种集成方式。


buildroot/$ make BR2_EXTERNAL=/path/to/foo menuconfig
注意,执行maked的目录为buirdroot的根目录。而且,我们可以再次执行上述命令从而导入另一个项目的目录树位置。


br2-external tree布局


br2-external tree必须包含下面几个文件:


  • external.desc


  • external.mk


  • Config.in


除去上面几个必须的文件之外,其他的文件或目录可以按照需要自行添加,后续会给出一个具体的br2-externnal tree布局示例。


下面详细介绍一下,上面三个主要文件的含义。


external.desc


该文件主要用于说明br2-external tree的名字和基本描述。


该文件的基本格式是:每行表示一个具体描述项,每行的开头是一个关键字,后面跟一个冒号,然后是若干个空格,最后就是一个用于描述关键字的字符串。主要包括两个关键字:


  • name,必须定义的描述项,用于描述br2-external tree的名字。name的命名字符必须是[A-Za-z0-9_],其他的字符不允许出现。Buildroot会设置BR2_EXTERNAL_$(NAME)_PATH环境变量,用于指定br2-exteranl tree的绝对位置,所以,我们可以在自己的br2-external tree中使用该环境变量。同时,因为buildroot可以同时引用多个br2-extreanl tree,所以,这里在命名name时,应该防止与其他的br2-exteranal tree冲突。


  • desc,可选的,用一段简短的话,来描述一下该br2-exteranal tree。buildroot使用环境变量BR2_EXTERNAL_$(NAME)_DESC来指定该描述。


本项目的上述两个关键字定义如下:


name:LinuxVersatile desc: Linux For  QEMU ARM Versatile

注意:BR2_EXTERNAL_(NAME)PATH和BR2_EXTERNAL_(NAME)_PATH和BR2\_EXTERNAL\_(NAME)PATHBR2_EXTERNAL_(NAME)_DESC两个环境变量对于:Kconfig、Makefile、post-build、post-image和in-fakeroot scripts都是可见的,在上述文件中我们可以直接使用上面两个环境变量。


Config.in和external.mk


这两个文件主要用于定义如何配置和编译项目相关的组件,其与Buildroot中组件的管理方式类似。我们可以把我们自己的应用程序或者buildroot不支持的第三方库放到这里。如果项目没有特别的组件,那么这个文件为空。


顾名思义,Config.in用于管理组件的配置,externnal.mk用于管理makefile文件。Buildroot会将Config.in包含的所有组件的Config.in,显示到Buildroot的顶级配置菜单里,供用户选择配置,同时,Buildroot也会包含external.mk包含的所有组件的编译配置文件。


Config.in的一般形式如下:


source "$BR2_EXTERNAL_BAR_42_PATH/package/package1/Config.in"
source "$BR2_EXTERNAL_BAR_42_PATH/package/package2/Config.in"


每个组件中同样会包含自己的配置文件:Config.in。


external.mk文件的定义如下:


include $(sort $(wildcard $(BR2_EXTERNAL_BAR_42_PATH)/package/*/*.mk))


而后,在BR2EXTERNALBAR42PATH/package/package1和BR2_EXTERNAL_BAR_42_PATH/package/package1和BR2EXTERNALBAR42PATH/package/package1BR2_EXTERNAL_BAR_42_PATH/package/package2中创建组件的副本,并定义相应的Config.in和.mk文件。


也可以在Config.in中定义特殊的配置选项,在external.mk中定义make相关的逻辑。


configs目录


该目录用于保存Buildroot的defconfigs,这个可以定义基于特定项目的defconfig文件,Buildroot可以自动的识别这些文件,并可以通过make list-defconfig展示出来,所以,我们可以使用make _deconfig来加载defconfig。


本项目的配置文件名为qemu_linux_versatile_defconfig,里面保存了该项目相关的配置文件。主要的配置包括硬件平台的定义、交叉编译器的配置、根文件系统相关的配置以及Linux内核相关的配置。


下面为qemu_linux_versatile_defconfig的配置内容:


#Target options 
BR2_arm=y       
BR2_cortex_a9=y 
BR2_ARM_INSTRUCTIONS_THUMB2=y
#Build optinons 
BR2_DL_DIR="/home/lhl/develops/linuxCard/businesscard-linux/buildroot/dl"
BR2_JLEVEL=2    
BR2_CCACHE=y    
#Toolchain      
BR2_TOOLCHAIN_BUILDROOT_USE_SSP=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_PACKAGE_HOST_GDB=y
#System Configuration
BR2_TARGET_GENERIC_HOSTNAME="qemu-arm"
BR2_TARGET_GENERIC_ISSUE="Welcome to Linux Versatile!"
BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
BR2_SYSTEM_DHCP="eth0"
BR2_ROOTFS_OVERLAY="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/rootfs_overlay"                                                                                                                                                                                       
BR2_ROOTFS_POST_BUILD_SCRIPT="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/post-build.sh"
 BR2_ROOTFS_POST_IMAGE_SCRIPT="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/after-build.sh "
#Kernel         
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_GIT=y
BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git"
BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v5.5.3"
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/linux_defconfig"
BR2_LINUX_KERNEL_DTS_SUPPORT=y
BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/linuxVersatile.dts"
#Target packages
BR2_PACKAGE_ASCII_INVADERS=y
BR2_PACKAGE_CHOCOLATE_DOOM=y
BR2_PACKAGE_SL=y
# Filesystem images
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_EXT2_4=y
BR2_TARGET_ROOTFS_EXT2_LABEL="rootfs"


生成上述配置文件的方式,可以使用BUildroot的make savedefconfig命令,该命令的具体执行方式如下:


#进入buildroot主目录
$cd buildroot
#进行buildroot配置
$ make menuconfig 
#另存buildroot配置文件
$make savedefconfig BR2_DEFCONFIG=/home/lhl/work/Linux/LinuxVersatile/configs/qemu_linux_versatile_defconfig


项目树


按照buildroot的br2-external tree的配置要求,下面是本项目的目录树节结构。


lhl@ubuntu18:~/work/Linux/LinuxVersatile$ tree 
.
├── board
│   └── LinuxVersatile
│       ├── misc
│       │   ├── linux_defconfig
│       │   ├── linuxVersatile.dts
│       │   └── post-build.sh
│       └── rootfs_overlay
├── buildroot
 |... ...
├── Config.in
├── configs
│   └── qemu_linux_versatile_defconfig
├── external.desc
├── external.mk
├── package
└── README.md
7 directories, 8 files
其中,buildroot目录为Buildroot的主目录,因为里面的文件太多,所以并未一一列出。


项目编译


项目的配置文件完成之后,可以进行下一步的编译工作了,切换到buildroot的目录,执行如下命令:


#加载项目配置文件
$ make BR2_EXTERNAL=$PWD/../ qemu_linux_versatile_defconfig
#编译
$ make
qemu\_linux\_versatile_defconfig的配置项BR2_JLEVEL=2 用于标明make执行的并发CPU数量,这里配置的是2。相当于make -j 2参数,不过buildroot不支持该这种使用方式。


因为需要下载相关的组件,所以编译时十分的缓慢,请耐心等待。


测试


编译完成之后,在buildroot的output为编译产生的文件,我们主要关注的是images中的文件。


lhl@ubuntu18:~/work/Linux/LinuxVersatile/buildroot/output/images$ tree
.
├── linuxVersatile.dtb
├── rootfs.ext2
├── rootfs.ext4 -> rootfs.ext2
├── rootfs.tar
├── vexpress-v2p-ca9.dtb
└── zImage
0 directories, 6 files


运行qemu-system-arm命令,测试内核和rootfs的正确性。


$ qemu-system-arm -M vexpress-a9 -smp 2 -m 1024M -kernel ./zImage -append "root=/dev/mmcblk0 rw console=ttyAMA0" -nographic -dtb ./linuxVersatile.dtb -sd ./rootfs.ext4
各个参数的含义如下:
- M:表示硬件平台,本项目为vexpress-a9
-smp:CPU核心数量,本项目为2个
-m:指定内存大小,本项目为1GBytes
-kernel:指定Linux内核文件
-append:指定Linux内核启动时的'cmdline',本项目中的root=/dev/mmcblk0 rw,指定rootfs文件系统保存在mmcblk0设备中,console=ttyAMA0表示内核的启动信息打印到ttyAMA0虚拟终端
-dtb:指定Linux内核相关的dts配置数据
-nograhpic:关闭图形界面信息输出,并将输出信息重定向到串口终端上,本项目就是上面的ttyAMA0虚拟串口
-sd:挂在sd文件,该文件保存了rootfs系统,内核识别的设备文件为mmcblk0


下面为内核启动后进入文件系统


Booting Linux on physical CPU 0x0
Linux version 5.5.3 (lhl@ubuntu18.04) (gcc version 8.3.0 (Buildroot 2020.02-git-01375-g25b1dc4613)) #2 SMP Wed Feb 19 21:24:36 CST 2020
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
Memory policy: Data cache writealloc
Reserved memory: created DMA memory pool at 0x4c000000, size 8 MiB
OF: reserved mem: initialized node vram@4c000000, compatible id shared-dma-pool
cma: Reserved 16 MiB at 0x9f000000
percpu: Embedded 19 pages/cpu s45644 r8192 d23988 u77824
Built 1 zonelists, mobility grouping on.  Total pages: 260096
Kernel command line: root=/dev/mmcblk0 rw console=ttyAMA0
printk: log_buf_len individual max cpu contribution: 4096 bytes
printk: log_buf_len total cpu_extra contributions: 12288 bytes
printk: log_buf_len min size: 16384 bytes
printk: log_buf_len: 32768 bytes
printk: early log buf free: 14896(90%)
... .... 
Welcome to Linux Versatile!
qemu-arm login: root
# uname -a
Linux qemu-arm 5.5.3 #2 SMP Wed Feb 19 21:24:36 CST 2020 armv7l GNU/Linux
# cat /etc/os-release 
NAME=Buildroot
VERSION=2020.02-git-01375-g25b1dc4613
ID=buildroot
VERSION_ID=2020.02-git
PRETTY_NAME="Buildroot 2020.02-git"
# 


我们,可以将上述命加到after-build.sh中,这样buildroot执行完之后,就会自动启动qemu,从而测试系统的正确性。


#!/bin/bash
 qemu-system-arm -M vexpress-a9 -smp 2 -m 1024M -kernel $BASE_DIR/images/zImage -append "root=/dev/mmcblk0 rw console=ttyAMA0" -nographic -dtb $BASE_DIR/images/linuxVersatile.dtb -sd $BASE_DIR/images/rootfs.ext4


好了,以上就是使用Buildroot来管理Linux内核和根文件系统,然后通过qemu模拟ARM 平台进行相关测试的例子。尽情享受Linux带来的技术盛宴把!

相关文章
|
9天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
37 3
|
9天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
27 2
|
3天前
|
Ubuntu Linux 网络安全
linux系统ubuntu中在命令行中打开图形界面的文件夹
在Ubuntu系统中,通过命令行打开图形界面的文件夹是一个高效且实用的操作。无论是使用Nautilus、Dolphin还是Thunar,都可以根据具体桌面环境选择合适的文件管理器。通过上述命令和方法,可以简化日常工作,提高效率。同时,解决权限问题和图形界面问题也能确保操作的顺利进行。掌握这些技巧,可以使Linux操作更加便捷和灵活。
11 3
|
9天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
31 3
|
12天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
30 6
|
12天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
42 6
|
13天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####
|
消息中间件 缓存 Unix
[面试必备]嵌入式Linux内核开发必须了解的三十道题
[面试必备]嵌入式Linux内核开发必须了解的三十道题
|
Linux
嵌入式Linux QT开发之如何实现获取磁盘空间大小的应用逻辑
嵌入式Linux QT开发之如何实现获取磁盘空间大小的应用逻辑
242 0
|
Linux Go 人机交互
嵌入式linux之go语言开发(十三)LittlevGL,漂亮的嵌入式GUI的go语言绑定
嵌入式linux之go语言开发(十三)LittlevGL,漂亮的嵌入式GUI的go语言绑定