3.3 编译Xenomai用户层
3.3.1 构建 Xenomai ARM64 库和工具的示例
以下示例使用以下约定:
$xenomai_root:Xenomai 源码目录/root/xenomai/xenomai-3.2.4。$build_root:一个干净的构建目录/root/xenomai/xenomai-3.2.4-build。$staging_dir:一个将临时保存安装文件的目录xenomai-3.2.4-install
以下命令,可以完成配置、编译及安装。
xenomai_root="/root/xenomai/xenomai-v3.2.4"
build_root="/root/xenomai/xenomai-v3.2.4-build"
staging_dir="/root/xenomai/xenomai-v3.2.4-install"
rm -fr $build_root $staging_dir
mkdir -p $build_root $staging_dir
cd $xenomai_root
./scripts/bootstrap
cd $build_root
$xenomai_root/configure \
--build=i686-pc-linux-gnu \
--host=aarch64-linux-gnu \
--enable-pshared \
--enable-smp \
--with-core=cobalt \
CC=aarch64-linux-gnu-gcc \
LD=aarch64-linux-gnu-ld \
CFLAGS="-march=armv8-a" \
LDFLAGS="-march=armv8-a"
make -j DESTDIR=$staging_dir install
在configure配置阶段,使用了针对多核ARM64的典型配置。configure执行完毕后,会自动生成一个头文件include/xeno_config.h,里面定义了和配置相关的宏定义,最终用于控制代码编译和运行。
如果configure执行失败,可以参考其自动生成的config.log,该文件包含在运行 configure 时产生的所有消息,
以便在 configure 出现错误时帮助调试。
完成编译并安装后,可以在安装目录xenomai-v3.2.4-install查看到以下内容。
tree xenomai-v3.2.4-install/ -L 3
xenomai-v3.2.4-install/
└── usr
└── xenomai
├── bin
├── demo
├── etc
├── include
├── lib
├── sbin
└── share
对于嵌入式系统来说,将上述usr/xenomai目录完整的拷贝到根文件系统的’/usr’目录即可!
3.3.2 bootstrap生成configure脚本
在软件开发中,特别是在使用 autoconf、automake 和 libtool 等工具的项目中,bootstrap 脚本通常用于生成 configure 脚本。这个过程也被称为“引导”或“初始化”。
如果您正在从 Xenomai GIT 树(git.xenomai.org)构建源码,则需要在 Xenomai 源码树中生成 configure 脚本和 Makefile。推荐的方法是运行随附的自动重新配置脚本,从源码树的顶部运行./scripts/bootstrap。在执行此脚本之前,需先安装相关依赖。
sudo apt install debhelper findutils autotools-dev autoconf automake libtool pkg-config libltdl-dev fuse
./scripts/bootstrap
如果您从发布 tar 包构建,则提取的源码树中将提供一组由 autoconf 生成的文件,因此不需要重新配置。当运行时,生成的 configure 脚本会为 Cobalt 和 Mercury 核心准备构建库和程序。构建过程中会自动透明地选择可能需要的特定核心代码。
对于本书来说,从 tar 包构建。在上一章节,已经解压xenomai源码包xenomai-3.2.4.tar.bz2,生成了xenomai-3.2.4源码目录。但是,此目录下并未包含configure 脚本,仍然需要使用手动生成。
cd ./xenomai-v3.2.4
./scripts/bootstrap
3.3.3 configure完成配置并生成Makefile
执行./configure --help可以看到完整的帮助信息。本章详细解释各配置的意义。
3.3.3.1 安装目录与程序名称
1. 安装目录
- –prefix=PREFIX 将架构无关的文件(如头文件、文档、配置文件等)安装在 PREFIX 目录下,默认值[/usr/xenomai]
- –exec-prefix=EPREFIX 将架构相关的文件(如可执行文件、库文件等)安装在 EPREFIX 目录下,默认值[PREFIX]
默认情况下,make install 将把所有文件安装在 /usr/xenomai/bin、/usr/xenomai/lib 等目录下。可以使用 --prefix 指定不同于 /usr/xenomai 的安装前缀,例如 --prefix=$HOME。但是,一般不建议修改PREFIX,同时也不建议把PREFIX和EPREFIX设置不同的值。
对于嵌入式Linux交叉编译而言,可以使用DESTDIR=来设置一个临时保存安装文件的目录。在编译完成后,将目录下内容拷贝到嵌入式Linux根文件系统。
tree xenomai-v3.2.4-install/ -L 3
xenomai-v3.2.4-install/
└── usr
└── xenomai
├── bin
├── demo
├── etc
├── include
├── lib
├── sbin
└── share
2. 子目录
如需更加精细地控制子目录,可使用以下选项。
--bindir=DIR:用户可执行文件目录 [EPREFIX/bin]--sbindir=DIR:系统管理员可执行文件目录 [EPREFIX/sbin]--libexecdir=DIR:程序可执行文件目录 [EPREFIX/libexec]--sysconfdir=DIR:只读单机数据目录 [PREFIX/etc]--sharedstatedir=DIR:可修改的架构无关数据目录 [PREFIX/com]--localstatedir=DIR:可修改的单机数据目录 [PREFIX/var]--runstatedir=DIR:可修改的每进程数据目录 [LOCALSTATEDIR/run]--libdir=DIR:目标代码库目录 [EPREFIX/lib]--includedir=DIR:C 头文件目录 [PREFIX/include]--oldincludedir=DIR:非 GCC 的 C 头文件目录 [/usr/include]--datarootdir=DIR:只读架构无关数据根目录 [PREFIX/share]--datadir=DIR:只读架构无关数据目录 [DATAROOTDIR]--infodir=DIR:info 文档目录 [DATAROOTDIR/info]--localedir=DIR:区域依赖数据目录 [DATAROOTDIR/locale]--mandir=DIR:man 文档目录 [DATAROOTDIR/man]--docdir=DIR:文档根目录 [DATAROOTDIR/doc/xenomai]--htmldir=DIR:html 文档目录 [DOCDIR]--dvidir=DIR:dvi 文档目录 [DOCDIR]--pdfdir=DIR:pdf 文档目录 [DOCDIR]--psdir=DIR:ps 文档目录 [DOCDIR]
3. 程序名称
--program-prefix=前缀:在安装的程序名称前添加前缀--program-suffix=后缀:在安装的程序名称后添加后缀--program-transform-name=程序:对安装的程序名称运行 sed 程序
3.3.3.2 通用配置选项
-
--enable-shared[=PKGS]- 构建共享库
- 默认构建所有共享库
-
--enable-static[=PKGS]- 构建静态库
- 默认值构建所有静态库
-
--disable-demo- 禁用演示代码
- 默认情况下,会构建demo
-
--disable-testsuite- 禁用测试套件
- 默认情况下,构建测试套件
-
--enable-condvar-workaround- 启用对 glibc 中优先继承(Priority Inheritance,PI)与条件变量(condvars)问题的解决方法。
- 对于Cobalt无需启用,默认关闭
-
--enable-lazy-setsched- 启用延迟更新调度参数。仅在判断线程处于Relax状态时,立即更新调度参数。
- 默认关闭,总是立即更新调度参数
-
--with-core=<type>- 指定要构建支持库的实时核心,可以是
cobalt或mercury。 - 默认为
cobalt。
- 指定要构建支持库的实时核心,可以是
-
--enable-debug[=partial]- 如果不使用
--enable-debug,则默认情况下会关闭所有debug信息。 --enable-debug支持三种模式。当不指定模式的时候,默认时partial模式。symbols:启用调试符号,但仍然开启优化(-O2)。此选项没有开销,适用于在运行应用程序时使用 gdb 快速获取有意义的回溯。partial:包含symbols子集,并且还启用了 Xenomai 代码中的内部一致性检查(主要存在于 Copperplate 层)。它定义了CONFIG_XENO_DEBUG宏,适用于 Xenomai 库和从 xeno-config 脚本(即xeno-config --cflags)获取编译标志的应用程序。部分调试模式隐式地启用了--enable-assert。full:包含partial子集,但禁用了优化(-O0),并且可能执行更多的内部一致性检查。除了CONFIG_XENO_DEBUG,还定义了CONFIG_XENO_DEBUG_FULL宏。此级别引入了最多的开销,可能会使最坏情况下的延迟增加三倍甚至更多。
- 如果不使用
-
--enable-assert- Xenomai 库中包含许多调试断言语句,用于动态检查运行时系统的内部一致性(参见
man assert(3))。 - 默认值依赖于
--enable-debug
- Xenomai 库中包含许多调试断言语句,用于动态检查运行时系统的内部一致性(参见
-
--enable-pshared- 启用后,会定义 CONFIG_XENO_PSHARED 宏。此选项允许不同进程间共享资源,像共享内存、信号量等。在实时系统里,这能让多个进程高效协作,共享数据和状态,减少进程间通信开销,提升系统整体性能和响应速度。启用 --enable-pshared 后,脚本会检查系统是否支持 shm_open 函数,若不支持则报错。
- 默认情况下,不启用
-
--enable-registry[=/registry-root-path]- Xenomai API 可以通过pseudo-filesystem导出其内部状态,可以通过读取这些文件来获取有关现有实时对象的信息,例如任务、信号量、消息队列等。
- 默认路径为
/var/run/xenomai,在其中自动创建//子目录。通过这个路径访问活跃实时对象的内部状态。会话标签<session>是从–session运行时开关获取的。如果没有指定会话名称,则会使用 anon@。例如,查看 VxWorks 任务的属性可以按如下方式操作:
$ cat /var/run/xenomai/root/anon@12656/12656/vxworks/tasks/windTask name = windTask errno = 0 status = ready priority = 70 lock_depth = 0- 此功能由 FUSE(http://fuse.sourceforge.net/)支持,必须在目标系统上可用。构建 Xenomai 库时需要工具链中提供 FUSE 开发库,并且目标内核中必须启用
CONFIG_FUSE_FS。 - 默认情况下,处于关闭状态
-
--enable-lores-clock- 启用低分辨率时钟支持。
- 默认情况下,低分辨率时钟功能未启用。如果您需要此支持(例如用于 pSOS 或 vxworks API),则可以使用此选项启用。
-
--enable-clock-monotonic-raw- Xenomai 库需要底层 PosiX 接口提供一个单调时钟。如果您的系统上可用
CLOCK_MONOTONIC_RAW,则可以传递此开关,否则默认使用CLOCK_MONOTONIC。 Cobalt实现了CLOCK_MONOTONIC_RAW,当选择--with-core=cobalt时,此选项默认打开。
- Xenomai 库需要底层 PosiX 接口提供一个单调时钟。如果您的系统上可用
-
--enable-tls- Xenomai 可以使用 GCC 的线程本地存储扩展(TLS)来加速其内部使用的每线程信息的检索。此开关启用 TLS,使用相反的
--disable-tls来禁用此功能。 - 由于某些版本/架构组合的 GCC 存在这个特性的错误,TLS 是否默认启用是架构特定的决定。目前,默认情况下为 x86 和 powerpc 启用此功能,其他架构需要显式传递
--enable-tls到 configure 脚本。
- Xenomai 可以使用 GCC 的线程本地存储扩展(TLS)来加速其内部使用的每线程信息的检索。此开关启用 TLS,使用相反的
-
--enable-dlopen-libs- 此开关允许程序使用
dlopen(3)动态加载基于 Xenomai 的库。启用动态加载会引入一些开销,尤其是在启用了 TLS(参见--enable-tls)时,这可能会根据架构而有所不同。当与--enable-tls一起使用时,将自动选择全局动态 TLS 模型。 - 默认情况下,动态加载基于 Xenomai 的库是禁用的。
- 此开关允许程序使用
-
--enable-async-cancel- 启用由实时 API 创建的 Xenomai 线程的完全异步取消功能,并对 Xenomai 实现代码进行相应保护。若禁用此功能,Xenomai 会假定线程仅在到达取消点(如系统调用)时,才会因取消请求而退出。
- 完全异步取消功能很容易导致资源泄漏、数据静默损坏、安全问题以及各种难以控制的 bug。启用此功能的唯一合理场景是,需要取消那些运行时间极长、无系统调用且无显式退出条件的忙循环线程,不过这种线程的设计或许本身就需要重新审视。
- 异步取消功能默认处于禁用状态。
-
--enable-smp- 该选项用于控制是否启用对称多处理支持。启用 --enable-smp 选项后,Xenomai 系统能充分利用多核 CPU 的优势,将任务分配到不同核心并行处理,提升系统整体性能和响应速度。
- 默认启用对称多处理支持。
-
--enable-sanity- 用于控制运行时健全性检查(sanity checks),支持
--enable-sanity=yes或-enable-sanity=no。 - 健全性检查是在程序运行过程中对关键数据、状态或操作进行验证,以确保程序运行符合预期,及时发现潜在错误。
- 默认情况下,CONFIG_XENO_SANITY 变量被设置为 y,意味着运行时健全性检查功能处于启用状态。
- 用于控制运行时健全性检查(sanity checks),支持
-
--enable-fortify-
用于控制是否启用 _FORTIFY_SOURCE 功能。_FORTIFY_SOURCE 是 GCC 编译器提供的一种安全特性,能在编译时和运行时检测常见的缓冲区溢出、格式化字符串漏洞等安全问题。
-
在没有使用此参数的情况下,如果系统libc支持__vfprintf_chk函数,则自动启用_FORTIFY_SOURCE(相当于默认开启)
-
在运行 ./configure 脚本时,若使用 --enable-fortify 选项且值为 y 或者 yes,启用 _FORTIFY_SOURCE 功能,若检测失败会报错;若值不为 y、yes,禁用该功能。
-
--enable-debug=full,在这种情况下,--enable-fortify将被忽略。
-
-
--enable-valgrind-client- 用于控制是否启用 Valgrind 客户端 API 支持。在Valgrind上运行Xenomai 3应用程序的功能仅支持Mercury核心。Valgrind 是一款强大的内存调试、内存泄漏检测和性能分析工具,其客户端 API 允许开发者在代码中嵌入特定指令,让 Valgrind 能更深入地理解程序行为,实现更精准的调试和分析。
- 首先检查系统中是否存在 valgrind/valgrind.h 头文件,如果存在,则将 CONFIG_XENO_VALGRIND_API 设置为 y,表示系统可能支持 Valgrind 客户端 API。
- 在运行 ./configure 脚本时,如果使用 --enable-valgrind-client=yes (或者不指定该选项,因为默认行为是尝试启用),CONFIG_XENO_VALGRIND_API 会被设置为 y;如果使用 --enable-valgrind-client=no,则会取消设置 CONFIG_XENO_VALGRIND_API 变量,关闭该 API 支持。
-
--enable-doc-build- 生成基于 Doxygen 标记语言的内联 Xenomai 文档,作为 PDF 和 HTML 文档。还生成基于 Asciidoc 标记语言的其他文档,如手册页。
- 默认不构建文档
-
--enable-x86-vsyscall- 用于控制在 x86 架构且使用 Cobalt 核心模式时,使用 x86/vsyscall 接口发出系统调用。如果禁用,将使用旧的 0x80 向量。
- vsyscall(Virtual System Call)是 x86 架构上的一种机制,能让用户空间程序以较低开销发起系统调用,避免了完整软件中断带来的较大开销。
- 启用此选项需要 NPTL,默认启用。
3.3.3.3 交叉编译选项
为了交叉编译 Xenomai 库和程序,需要向 configure 脚本传递--build和 --host 选项。
-
--build此选项用于选择运行编译工具的架构,即运行 configure 脚本的系统。如果不指定,一般来说configure能够自动解析出正确的值。如果是在x86上执行交叉编译:- 32位x86: --build=i686-pc-linux-gnu
- 64位x86_64: --build=x86_64-pc-linux-gnu
-
--host此选项用于选择要构建库和程序的目标架构。- 当向 configure 传递
--host=aarch64-linux时,它会自动将aarch64-linux-作为所有编译工具名称的前缀,并从前缀中推断目标架构名称。例如,64 位 ARM 架构的编译器被推断为aarch64-linux-gcc。 - 如果实际的交叉编译器与 configure 从前缀中推断的不同,需要在 configure 命令行中手动传递所有编译工具的名称,至少包括 CC 和 LD 变量。
CC=aarch64-linux-gnu-gcc LD=aarch64-linux-gnu-ld - 当向 configure 传递
与之前的版本不同,Xenomai 不再向 gcc 传递任何特定于 ARM 架构的标志或 FPU 标志,因此用户需要使用 CFLAGS 和 LDFLAGS 变量显式传递这些标志。例如:
CFLAGS="-march=armv8-a"
LDFLAGS="-march=armv8-a"
CFLAGS 和 LDFLAGS 变量如何设置? 首先确定目标ARM64 SOC支持的架构及包含的硬件特性,然后查询gcc手册,确定正确的值。
以Ubuntu22.04为例,其gcc版本为11.3.0。
aarch64-linux-gnu-gcc -v
gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
查询GCC手册https://gcc.gnu.org/onlinedocs/,在aarch64架构的GCC编译器中,-march、-mcpu和-mtune的作用和关系如下:
1. -march=架构
- 格式: 指定目标架构的名称,并可选地指定一个或多个特性修饰符。此选项的格式为 -march=arch{+[no]feature}*。feature(功能修饰符)的允许值详见GCC手册中
-march 和 -mcpu 功能修饰符子章节的说明。 - 作用:指定目标架构的版本(如
armv8-a、armv8.1-a),确定编译器可使用的指令集和硬件特性。 - 影响:生成的代码仅兼容指定架构及以上版本,可能无法在更旧的架构上运行。例如,
-march=armv8.1-a会启用ARMv8.1-A的指令(如原子操作扩展)。
下表总结了允许的 arch 值及其默认启用的特性:
arch 值 | 架构 | 默认包含的特性 |
|---|---|---|
armv8-a | Armv8-A | +fp, +simd |
armv8.1-a | Armv8.1-A | armv8-a, +crc, +lse, +rdma |
armv8.2-a | Armv8.2-A | armv8.1-a |
armv8.3-a | Armv8.3-A | armv8.2-a, +pauth |
armv8.4-a | Armv8.4-A | armv8.3-a, +flagm, +fp16fml, +dotprod |
armv8.5-a | Armv8.5-A | armv8.4-a, +sb, +ssbs, +predres |
armv8.6-a | Armv8.6-A | armv8.5-a, +bf16, +i8mm |
armv8-r | Armv8-R | armv8-r |
native 值在原生 AArch64 GNU/Linux 上可用,使编译器选择主机系统的架构。如果编译器无法识别主机系统的架构,则此选项无效。
2. -mtune=CPU型号
- 作用:仅优化代码以适应特定CPU的微架构(如指令调度、分支预测),不改变指令集。
- 典型用例:在兼容旧架构(通过
-march指定)的同时,为新CPU优化性能。例如:-march=armv8-a -mtune=cortex-a76 # 兼容ARMv8-A,但针对A76优化
指定 GCC 应针对其优化代码性能的目标处理器名称。此选项允许的值包括:generic、cortex-a35、cortex-a53、cortex-a55、cortex-a57、cortex-a72、cortex-a73、cortex-a75、cortex-a76、cortex-a76ae、cortex-a77、cortex-a65、cortex-a65ae、cortex-a34、cortex-a78、cortex-a78ae、cortex-a78c、ares、exynos-m1、emag、falkor、neoverse-512tvb、neoverse-e1、neoverse-n1、neoverse-n2、neoverse-v1、neoverse-v2、grace、qdf24xx、saphira、phecda、xgene1、vulcan、octeontx、octeontx81、octeontx83、octeontx2、octeontx2t98、octeontx2t96、octeontx2t93、octeontx2f95、octeontx2f95n、octeontx2f95mm、a64fx、thunderx、thunderxt88、thunderxt88p1、thunderxt81、tsv110、thunderxt83、thunderx2t99、thunderx3t110、zeus、cortex-a57.cortex-a53、cortex-a72.cortex-a53、cortex-a73.cortex-a35、cortex-a73.cortex-a53、cortex-a75.cortex-a55、cortex-a76.cortex-a55、cortex-r82、cortex-x1、ampere1、ampere1a、cobalt-100 和 native。
3. -mcpu=CPU型号
- 格式:指定目标处理器的名称(可选附带一个或多个功能修饰符后缀)。该选项的格式为 -mcpu=cpu{+[no]feature}*,其中:cpu(处理器名称)的允许值与 -mtune 选项支持的处理器名称完全相同。feature(功能修饰符)的允许值详见GCC手册中
-march 和 -mcpu 功能修饰符子章节的说明。 - 作用:指定具体的CPU型号(如
cortex-a53、neoverse-n1),隐式完成两件事:- 设置架构:根据该CPU支持的架构版本,自动设置
-march(例如,cortex-a76对应armv8.2-a)。 - 设置优化:等同于
-mtune=CPU型号,针对该CPU的微架构进行优化(如流水线、缓存策略)。
- 设置架构:根据该CPU支持的架构版本,自动设置
推荐实践:
- 若目标CPU明确且在GCC支持列表,优先用
-mcpu简化配置,同时定义指令集和优化目标(相当于-march+-mtune)。 - 若仅能明确CPU兼容架构,建议使用
-march。 - 避免
-mcpu与-march冲突,除非明确架构版本兼容。 -mtune:仅定义优化目标,不限制指令集。可单独使用,或与-march/-mcpu组合,不影响指令集。
默认情况:如果未指定 -mtune=、-mcpu= 或 -march=,则GCC将保证最大范围目标处理器兼容性。
示例总结:
| 场景 | 选项组合 | 效果 |
|---|---|---|
| 兼容ARMv8-A,针对A76优化 | -march=armv8-a -mtune=cortex-a76 | 生成ARMv8-A指令,优化A76性能 |
| 直接为A76生成代码 | -mcpu=cortex-a76 | 等价于 -march=armv8.2-a -mtune=cortex-a76 |
| 兼容A53指令集,优化A72性能 | -mcpu=cortex-a53 -mtune=cortex-a72 | 不推荐(可能冲突),应分开指定-march和-mtune |
295

被折叠的 条评论
为什么被折叠?



