Apache NuttX 13.1.0 系统配置详解
拆解 Apache NuttX 13.1.0 的配置体系,覆盖 defconfig 与 .config 的三层流水线、apps 树 select 强制、ETC_ROMFS 启动脚本挂载链、修改配置的实战方法与 savedefconfig 回写规则,全部结论实测于 sim:nsh。
Apache NuttX 13.1.0 系统配置详解
目标:讲透 NuttX 的配置体系——defconfig 怎么变成 .config、Kconfig 的 select 如何跨内核与应用两棵树强制开关、/tmp 内存盘由哪几条配置驱动、如何安全地增删功能并回写 defconfig。全文每个结论都给出 sim:nsh 上的实测依据,含一次完整的 tmpfs 开启实验(从编译报错到挂载成功再到恢复出厂)。
一、环境信息
| 项目 | 值 |
|---|---|
| 宿主系统 | Ubuntu 22.04(x86_64) |
| NuttX 版本 | 13.1.0(release/13.1 分支源码构建) |
| 源码布局 | 内核 /opt/nuttx/nuttx,应用 /opt/nuttx/apps |
| 配置基线 | sim:nsh(boards/sim/sim/sim/configs/nsh/defconfig) |
| defconfig 规模 | 82 行 / 2029 字节(含自动生成头) |
| .config 规模 | 44405 字节(olddefconfig 全量展开后) |
| Kconfig 前端 | kconfiglib 14.1.0(pip 安装,提供 menuconfig/savedefconfig) |
| 实测日期 | 2026-10-14 至 2026-10-15 |
解说:sim:nsh 是 NuttX 官方模拟器配置,无需交叉工具链即可在本机编译运行,是验证配置行为最快捷的目标。环境搭建与首次编译见本站《Ubuntu 22.04 编译安装 Apache NuttX 13.1.0 指南》。
二、配置体系概览
NuttX 不用 CMake 也不用 Bazel,而是一套 Kconfig + Make 的自研流水线,一次配置要经过四步落地:
板级 defconfig ──configure.sh──▶ .config ──olddefconfig──▶ 全量展开的 .config ──make──▶ include/nuttx/config.h ──▶ 编译期 C 宏
- defconfig:板级配置的最小表达,只记录与默认值不同的项。sim:nsh 的 defconfig 只有 82 行,文件头写着 autogenerated,官方不建议手改。
- configure.sh:配置搬运工。它把指定 defconfig 拷贝为内核树根的 .config,再调用 sethost.sh 按宿主类型(Linux/macOS/Windows)调整 HOST_* 宏,最后跑一次 olddefconfig 补齐依赖。实测一次干净配置的输出:
Loaded configuration '.config'
Configuration saved to '.config'
- .config:全量展开的配置文件,44405 字节、1900 余行,编译系统唯一的配置事实来源。
- include/nuttx/config.h:make 时由 .config 生成的头文件,每个 CONFIG_* 变成 #define,源码里的条件编译全部读它(构建日志可见
CP: /opt/nuttx/nuttx/include/nuttx/config.h)。
💡 技术拓展:三套 RTOS 配置体系的对比。NuttX 把配置收敛成一个 .config 文本文件,diff 友好、脚本可改;Zephyr 用 Kconfig 但叠加 devicetree,配置分散在 overlay 与 dts 两处;Mynewt 的 syscfg 按 package 分层覆盖(见本站 Mynewt 系列),层级多但单文件职责清晰。NuttX 的优势是链路最短、心智负担最低,代价是 defconfig 与 .config 的差异需要靠 olddefconfig/savedefconfig 维护。
三、defconfig 与 .config 的关系
两者是"源"与"展开"的关系,实测对比如下:
| 维度 | defconfig(板级) | .config(展开后) |
|---|---|---|
| 路径 | boards/sim/sim/sim/configs/nsh/defconfig | /opt/nuttx/nuttx/.config |
| 规模 | 82 行 / 2029 字节 | 约 1902 行 / 44405 字节 |
| 内容 | 仅非默认项 | 全部符号(含 is not set 行) |
| 维护方式 | make savedefconfig 生成 | configure.sh/menuconfig/olddefconfig 生成 |
| 是否直接参与编译 | 否(间接) | 是(转 config.h) |
.config 头部的真实内容(摘选):
#
# License Setup
#
# CONFIG_ALLOW_BSD_COMPONENTS is not set
# CONFIG_ALLOW_GPL_COMPONENTS is not set
# CONFIG_ALLOW_MIT_COMPONENTS is not set
# CONFIG_ALLOW_ECLIPSE_COMPONENTS is not set
# CONFIG_ALLOW_ICS_COMPONENTS is not set
# CONFIG_ALLOW_CUSTOM_PERMISSIVE_COMPONENTS is not set
# end of License Setup
#
# Build Setup
#
CONFIG_EXPERIMENTAL=y
# CONFIG_DEFAULT_SMALL is not set
CONFIG_HOST_LINUX=y
# CONFIG_HOST_MACOS is not set
# CONFIG_HOST_WINDOWS is not set
# CONFIG_HOST_OTHER is not set
#
# Build Configuration
#
CONFIG_APPS_DIR="../apps"
CONFIG_BASE_DEFCONFIG="sim:nsh"
CONFIG_BUILD_FLAT=y
解说:# CONFIG_XXX is not set 行同样参与配置语义,是"显式关闭",删掉它再跑 olddefconfig 会变回默认值。CONFIG_BASE_DEFCONFIG="sim:nsh" 记录来源,CONFIG_APPS_DIR="../apps" 指向应用树——应用树的 Kconfig 也会被主 Kconfig source 进来,这引出下一节的关键机制。
四、核心配置分组详解
以下 8 组配置项全部取自实测 .config,逐项给出含义与验证依据。
4.1 许可证组(License Setup)
| 配置项 | 默认 | 当前值 | 含义 |
|---|---|---|---|
| CONFIG_ALLOW_BSD_COMPONENTS | n | not set | 允许纳入 BSD 许可的第三方组件 |
| CONFIG_ALLOW_GPL_COMPONENTS | n | not set | 允许纳入 GPL 组件(会改变整体许可属性) |
| CONFIG_ALLOW_MIT_COMPONENTS | n | not set | 允许纳入 MIT 组件 |
| CONFIG_ALLOW_CUSTOM_PERMISSIVE_COMPONENTS | n | not set | 允许自定义宽松许可组件 |
解说:sim:nsh 默认零外部许可依赖,成品是纯 Apache-2.0。商用产品若引入 GPL 组件需在此显式放行,构建系统会在链接层面做把关。
4.2 构建基础组(Build Setup)
| 配置项 | 当前值 | 含义与实测依据 |
|---|---|---|
| CONFIG_EXPERIMENTAL=y | 开启 | 解锁实验性选项(模拟器配置默认开启) |
| CONFIG_DEFAULT_SMALL | not set | 未启用"小体积优先"预设 |
| CONFIG_HOST_LINUX=y | Linux 宿主 | sethost.sh 按 uname 结果写入 |
| CONFIG_APPS_DIR="../apps" | 应用树路径 | make 时以子 make 方式构建 NSH 等 |
| CONFIG_BASE_DEFCONFIG="sim:nsh" | 来源标记 | savedefconfig 时会将其剔除 |
| CONFIG_BUILD_FLAT=y | 平坦构建 | 内核与应用同一地址空间;保护模式需 PROTECTED/KERNEL 变体 |
4.3 架构与板级组(sim)
| 配置项 | 当前值 | 含义与实测依据 |
|---|---|---|
| CONFIG_ARCH_SIM=y | 模拟器架构 | 编译为本机 x86_64 代码直接运行 |
| CONFIG_ARCH_BOARD="sim" | 板级名 | 对应 boards/sim/sim/sim |
| CONFIG_SIM_X8664_SYSTEMV=y | System V ABI | 决定宿主调用约定与汇编桩 |
| CONFIG_SIM_HOSTFS=y | 宿主文件系统支持 | /data 挂载的前提 |
| CONFIG_SIM_WALLTIME_SIGNAL=y | 墙钟信号 | 让模拟器按真实时间派发信号 |
| CONFIG_RAM_SIZE=0 | 0 | 使用默认 RAM 尺寸 |
| CONFIG_MM_REGIONS=1 | 1 个内存区 | 单堆管理 |
| CONFIG_START_YEAR=2008 | 2008 | 系统起始年份(RTC 缺省纪元) |
解说:CONFIG_SIM_X8664_SYSTEMV 与 CONFIG_SIM_HOSTFS 由 sethost.sh 依据宿主自动决定,这就是"同一 defconfig 在 macOS 上会得到不同 .config"的原因——defconfig 只存差异,宿主差异属于每次配置时的动态项。
4.4 文件系统组
| 配置项 | 当前值 | 含义与实测依据 |
|---|---|---|
| CONFIG_FS_FAT=y | FAT | /tmp 内存盘的文件系统 |
| CONFIG_FAT_LFN=y / FAT_LCNAMES=y | 长文件名 | 支持长名与大小写保留 |
| CONFIG_FS_ROMFS=y | ROMFS | /etc 只读根 |
| CONFIG_FS_HOSTFS=y | HOSTFS | /data 直通宿主目录 |
| CONFIG_FS_BINFS=y | BINFS | /bin 内建程序挂载点 |
| CONFIG_FS_PROCFS=y | PROCFS | /proc 进程信息 |
| CONFIG_FS_RAMMAP=y | RAMMAP | 基于 RAM 的 mmap 支持 |
| CONFIG_FS_TMPFS | not set(出厂) | tmpfs 未编译,mount -t tmpfs 报 failed: 15(实测) |
解说:出厂配置的挂载表是 /bin(binfs)、/data(hostfs)、/etc(romfs)、/proc(procfs)、/tmp(vfat)。其中 /tmp 走的是 FAT 而非 tmpfs,原因见 4.5 与第六节实验。
4.5 /etc 与启动脚本组(ETC_ROMFS)
| 配置项 | 当前值 | 含义与实测依据 |
|---|---|---|
| CONFIG_ETC_ROMFS=y | 内嵌 /etc | 把 boards/sim/sim/sim/src/etc 编进 ROMFS |
| CONFIG_ETC_ROMFSDEVNO=1 | ram1 之外的 romfs 设备号 | df 可见 /etc 为 64 字节块、8 块 |
| CONFIG_ETC_FATDEVNO=2 | 内存盘设备号 2 | 运行期 /dev/ram2 存在(ls /dev 实测) |
| CONFIG_ETC_FATSECTSIZE=512 | 512 字节扇区 | df 的 Block Size 列吻合 |
| CONFIG_ETC_FATNSECTORS=1024 | 1024 扇区 | 512B×1024=512KB,扣 FAT 表开销后 df 报 985 块可用(实测) |
| CONFIG_ETC_FATMOUNTPT="/tmp" | 挂载点 | mount 表中 /tmp type vfat(实测) |
这组配置如何生效?答案在启动脚本 boards/sim/sim/sim/src/etc/init.d/init.rc,它以 C 预处理宏的写法引用上述值:
#ifdef CONFIG_FS_TMPFS
mount -t tmpfs /tmp
#elif defined(CONFIG_FS_FAT)
mkrd -m CONFIG_ETC_FATDEVNO -s CONFIG_ETC_FATSECTSIZE CONFIG_ETC_FATNSECTORS
mkfatfs CONCATENATE(/dev/ram, CONFIG_ETC_FATDEVNO)
mount -t vfat CONCATENATE(/dev/ram, CONFIG_ETC_FATDEVNO) CONFIG_ETC_FATMOUNTPT
#endif
#ifdef CONFIG_FS_HOSTFS
mount -t hostfs -o fs=. /data
#endif
解说:编进 ROMFS 时宏会被替换,运行期 cat /etc/init.d/init.rc 看到的是 mkrd -m 2 -s 512 1024 这样的实体命令。整条链路是——.config 决定走 tmpfs 还是 FAT 分支、决定设备号与容量,ROMFS 重编后启动脚本按新值执行。另外注意 /dev/ram0 出厂即被 sim 架构自带块设备占用(arch/sim/src/sim/sim_blockdevice.c 的 sim_registerblockdevice() 注册了 2048 扇区×512B 的盘),手动 mkrd 必须用 -m 指定空闲号,实测 mkrd -m 5 64 → mkfatfs /dev/ram5 → mount -t vfat /dev/ram5 /mnt 后可正常读写。
4.6 BOARDCTL 组与 select 强制
| 配置项 | 当前值 | 含义 |
|---|---|---|
| CONFIG_BOARDCTL=y | boardctl 总开关 | 应用态调用板级控制接口的入口 |
| CONFIG_BOARDCTL_POWEROFF=y | 关机命令 | NSH poweroff 的后端 |
| CONFIG_BOARDCTL_APP_SYMTAB=y | 应用符号表 | 内建应用注册所需 |
| CONFIG_BOARDCTL_IOCTL | not set | 未启用板级自定义命令透传 |
| CONFIG_BOARDCTL_MKRD=y | 运行期建内存盘 | mkrd 命令的后端(详见下文) |
CONFIG_BOARDCTL_MKRD 是理解 NuttX 配置体系的最佳样本。它的内核侧定义在 boards/Kconfig,默认关闭:
config BOARDCTL_MKRD
bool "Enable application space creation of RAM disks"
default n
select DRVR_MKRD
depends on !DISABLE_MOUNTPOINT
但应用树 apps/nshlib/Kconfig 第 13 行有一句条件强制选中:
select BOARDCTL_MKRD if !NSH_DISABLE_MKRD && !DISABLE_MOUNTPOINT
于是产生了三个实测可验证的现象:
- .config 第 300 行是
CONFIG_BOARDCTL_MKRD=y——虽然内核 Kconfig 默认 n; - 板级 defconfig 与 make savedefconfig 的产物里都找不到这一项——被 select 强制的值等同于默认结果,最小 defconfig 无需记录;
- 想真正关掉 mkrd,改 BOARDCTL_MKRD 本身没用(select 会重新拉起),正确做法是设置
CONFIG_NSH_DISABLE_MKRD=y让 select 条件不成立。
💡 技术拓展:select 与 depends on 的区别。depends on 是"我可见的前提",父选项不可见则子项无法打开;select 是"我被选中就顺手打开你",不问对方默认值与用户意愿。跨树 select(应用树选内核符号)是 NuttX 组件化设计的粘合剂,但也让"在 defconfig 里搜索某个 =y 来历"变成需要双向 grep 的活——先搜
select XXX再看条件表达式。
4.7 NSH 组
| 配置项 | 当前值 | 含义与实测依据 |
|---|---|---|
| CONFIG_NSH_LIBRARY=y | NSH 库 | shell 主体编译进系统 |
| CONFIG_NSH_CONSOLE=y | 控制台 shell | init.rc 的 start console 拉起 |
| CONFIG_NSH_READLINE=y | 行编辑 | 实测支持 Tab 补全与历史 |
| CONFIG_NSH_BUILTIN_APPS=y | 内建应用表 | help 下半区的 Builtin Apps |
| CONFIG_NSH_FILE_APPS=y | 文件型应用 | /bin 下以 ELF/文件形式存在的命令 |
| CONFIG_PATH_INITIAL="/bin" | 初始 PATH | 命令查找路径 |
| CONFIG_NSH_DISABLE_LOSMART 等 | 裁剪项 | 出厂即裁掉 losmart/lomtd/mb/mh/mw/shutdown/timedatectl |
解说:NSH 的命令裁剪全部走 CONFIG_NSH_DISABLE_<命令名大写>=y 模式,菜单集中在 Application Configuration → NSH Library → Disable NSH commands。出厂的裁剪清单说明"默认全开"是错觉——help 表已经是裁剪后的结果。
4.8 调试与优化组
| 配置项 | 当前值 | 含义 |
|---|---|---|
| CONFIG_NDEBUG=y | 关闭断言开销 | release 取向 |
| CONFIG_DEBUG_FEATURES=y | 内核错误日志 | _err/_warn 生效 |
| CONFIG_DEBUG_SYMBOLS=y | -g 调试符号 | gdb/backtrace 可用 |
| CONFIG_DEBUG_ASSERTIONS=y | 断言检查 | DEBUG 断言编译进来 |
解说:这组组合的实效是"带符号、留错误日志、断言开启但 NDEBUG 关闭基础 assert"。实测启动过程完全安静(无冗余打印直接进入 nsh>),因为各模块初始化均未出错,DEBUG_FEATURES 只在有错误时输出。
五、修改配置的实战方法
方法 A:make menuconfig(交互式)
cd /opt/nuttx/nuttx
make menuconfig
本环境的 Kconfig 前端是 pip 安装的 kconfiglib 14.1.0(/usr/local/bin/menuconfig),Unix.mk 检测不到 kconfig-frontends 原生工具时自动回退到它。菜单里改完保存即写 .config,退出后直接 make 即可——menuconfig 保存的是全量配置,不需要额外步骤。
方法 B:kconfig-tweak 脚本化修改(有坑)
kconfig-tweak 可以一行开关一个符号,但直改 .config 后必须补跑 make olddefconfig,否则新开符号的子项不会展开。第六节的 tmpfs 实验完整记录了这个坑:跳过 olddefconfig 直接 make,编译器报出三条 undeclared 错误;补跑后子项全部落位(.config 856~861 行出现 BLOCKSIZE=512 等 6 个符号)。安装篇给出了本环境使用的最小 kconfig-tweak 替换实现,此处不再重复。
方法 C:改 defconfig 后全量重配
改动板级 defconfig(或想换一个基线配置)时走完整链路:
cd /opt/nuttx/nuttx
make distclean
./tools/configure.sh sim:nsh
make -j
实测 make distclean 退出码 0(清理 .config、Make.defs 与全部产物),configure.sh 重新搬运 defconfig 并跑 sethost.sh。注意在未清理的树上重复执行 configure.sh 得到的是 No configuration change.,.config 不会被重建——换配置基线前务必先 distclean。
方法 D:直接改 .config(不推荐长期使用)
适合临时验证,缺点有二:下次 distclean+configure 会丢失;跳过 olddefconfig 会踩方法 B 的坑。验证完用方法 C 或 savedefconfig 固化。
一个与配置无关但会伪造配置故障的坑
手工在内核树创建 external/ 目录(哪怕只放一个空的 Kconfig)会被构建系统注册成构建子目录,而它没有 Makefile,导致 make 报 No rule to make target 'depend'(pass2dep 阶段)、make distclean 报 external_clean Error 2。删除该目录即愈。NuttX 的 external/ 目录应由 apps 预配置步骤自动生成,不要手工创建。
六、tmpfs 实验全记录:一次完整的配置变更
以"给 sim:nsh 开启 tmpfs"为例,走一遍从修改到验证再到恢复的全过程,所有输出均为真实执行记录。
第一步:脚本化开启并直接编译(踩坑)
kconfig-tweak --enable CONFIG_FS_TMPFS
grep -n "^CONFIG_FS_TMPFS=" .config # 856:CONFIG_FS_TMPFS=y
make -j
编译失败,错误清单:
tmpfs/fs_tmpfs.c:328:24: error: 'CONFIG_FS_TMPFS_FILE_FREEGUARD' undeclared
tmpfs/fs_tmpfs.c:342:25: error: 'CONFIG_FS_TMPFS_FILE_ALLOCGUARD' undeclared
tmpfs/fs_tmpfs.c:2564:41: error: 'CONFIG_FS_TMPFS_BLOCKSIZE' undeclared
第二步:补跑 olddefconfig,子项展开
make olddefconfig
grep -n "^CONFIG_FS_TMPFS" .config
856:CONFIG_FS_TMPFS=y
857:CONFIG_FS_TMPFS_BLOCKSIZE=512
858:CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD=64
859:CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD=128
860:CONFIG_FS_TMPFS_FILE_ALLOCGUARD=512
861:CONFIG_FS_TMPFS_FILE_FREEGUARD=1024
第三步:重新编译成功(退出码 0,收尾输出 SIM elf with dynamic libs archive in nuttx.tgz)。
第四步:运行期验证
nsh> mount -t tmpfs /mnt2
nsh> echo tmpfs-ok > /mnt2/t.txt
nsh> cat /mnt2/t.txt
tmpfs-ok
nsh> mount
/bin type binfs
/data type hostfs
/etc type romfs
/mnt2 type tmpfs
/proc type procfs
/tmp type tmpfs
注意挂载表里 /tmp 的类型从 vfat 变成了 tmpfs——这正是 4.5 节 init.rc #ifdef CONFIG_FS_TMPFS 分支在起作用:tmpfs 编进来后,启动脚本自动改走 tmpfs 分支,FAT 内存盘分支整体退出。一个开关同时改变了两处行为,这就是 Kconfig 条件化启动脚本的力量。
第五步:恢复出厂
kconfig-tweak --disable CONFIG_FS_TMPFS
make olddefconfig && make -j
这里实测遇到一个反转:恢复编译后 mount 表里 /tmp 直接消失了——ROMFS 里嵌的启动脚本仍是 tmpfs 分支(cat /etc/init.d/init.rc 可见 mount -t tmpfs /tmp),而 tmpfs 已被关闭,启动挂载静默失败。原因是启动脚本的预处理拷贝(boards/sim/sim/sim/src/etctmp/)只依赖源文件、不依赖 .config(boards/Board.mk 的规则),切换分支不会触发它重新生成。删除 etctmp* 产物(保留 etc/ 源码目录)后重编,mount 表才回到 /tmp type vfat,与出厂一致。该坑的完整分析见 FAQ Q10。
七、savedefconfig 与 defconfig 回写
定制固化用 make savedefconfig。实测输出:
Loaded configuration '.config'
Minimal configuration saved to 'defconfig.tmp'
它在内核树根生成新的 defconfig 文件(2029 字节、带 autogenerated 头)。整理流程在 tools/Unix.mk 的 savedefconfig 目标里:先由 kconfiglib 导出最小配置到 defconfig.tmp,剔除 CONFIG_APPS_DIR 与 CONFIG_BASE_DEFCONFIG 两个环境相关项,追加 ARCH/CHIP/BOARD 相关行,排序去重后加自动生成头写出。三条规则需要牢记:
- 只保存与默认值不同的项——所以 44KB 的 .config 回写成 2KB 的 defconfig;
- select 强制的值不保存——BOARDCTL_MKRD 就是实例(4.6 节);
- 凭据类配置主动剔除——Unix.mk 明确删除 ROMFS 密码相关行并打印 WARNING,防止凭据泄漏进版本库。
八、常见问题(FAQ)
Q1:defconfig 里明明没有 CONFIG_BOARDCTL_MKRD,为什么 mkrd 命令能用?
A:它由应用树 apps/nshlib/Kconfig 的 select BOARDCTL_MKRD if !NSH_DISABLE_MKRD && !DISABLE_MOUNTPOINT 强制选中,.config 里始终是 y(实测第 300 行)。被 select 的值不出现在 defconfig,因为最小 defconfig 只需记录"与默认不同"的用户选择。
Q2:脚本改完 .config 直接 make,报 CONFIG_FS_TMPFS_BLOCKSIZE undeclared?
A:新开启的符号有子项,必须先 make olddefconfig 让 Kconfig 按默认值展开子项再编译(第六节实测三条错误与展开后的完整符号清单)。menuconfig 不需要这步,因为它保存的就是全量配置。
Q3:mount -t tmpfs 报 failed: 15? A:出厂配置未编译 tmpfs(CONFIG_FS_TMPFS not set)。按第六节流程开启重编即可,挂载点直接可用(实测 /mnt2 挂载、读写正常)。
Q4:/tmp 内存盘太小,怎么改?
A:出厂走 FAT 分支,改 CONFIG_ETC_FATNSECTORS(当前 1024 扇区,df 实测 985 块可用,差额是 FAT 表等元数据开销)后重编;或开启 tmpfs 由内存动态分配。运行期临时扩容用 mkrd -m 5 <扇区数> + mkfatfs + mount 自建一块(注意 /dev/ram0 已被 sim 自带块设备占用,必须 -m 指定空闲号)。
Q5:make distclean 报 external_clean Error 2,或 make 报 No rule to make target 'depend'? A:内核树里存在手工创建的 external/ 目录(本环境实测:放了一个空 Kconfig 存根后两个错误同时出现)。删除该目录重跑即可;正常的 external/ 由 apps 预配置步骤生成,不需要也不应该手工创建。
Q6:为什么改了 defconfig 再跑 make 没生效?
A:make 只读 .config,defconfig 仅在 configure.sh 执行时被搬运。正确顺序是 make distclean && ./tools/configure.sh sim:nsh && make;在未清理的树上重配只会得到 No configuration change.(实测)。
Q7:想关掉 mkrd,为什么设置 CONFIG_BOARDCTL_MKRD=n 没用?
A:select 会把它重新拉成 y。设置 CONFIG_NSH_DISABLE_MKRD=y 让 select 的条件表达式不成立(4.6 节的规则三),这也适用于所有被 select 强制的内核符号。
Q8:savedefconfig 生成的 defconfig 为什么比 .config 小这么多? A:两条压缩规则:只保存非默认项(44405 字节压缩到 2029 字节);select 强制值与环境相关项(APPS_DIR 等)不保存。反之从 defconfig 到 .config 靠 olddefconfig 按默认值补齐。
Q9:改完配置怎么确认真的生效了? A:用运行期状态对照法。设备号对 /dev(CONFIG_ETC_FATDEVNO=2 ↔ /dev/ram2 存在)、容量对 df(1024 扇区 ↔ 985 块)、文件系统类型对 mount(FS_TMPFS ↔ /tmp type tmpfs)、命令对 help(NSH_DISABLE_* ↔ 命令从列表消失)。文档中的每个配置项都按这个方法验证过。
Q10:改了 init.rc 源文件,或切换脚本内引用的开关,启动行为没变甚至 /tmp 消失?
A:init.rc 被编进 ROMFS(CONFIG_ETC_ROMFS=y),构建系统先把它预处理拷贝到 boards/sim/sim/sim/src/etctmp/(这一步替换 CONFIG_* 宏、按当前配置展开 #ifdef 分支),再打包进 romfs 镜像。坑在拷贝规则只依赖源文件、不依赖 .config(boards/Board.mk):改脚本源文件会重新拷贝,但仅切换脚本引用的 CONFIG_*(如 FS_TMPFS 与 FAT 分支互换)不会——实测关闭 tmpfs 后重编,ROMFS 里仍是 mount -t tmpfs /tmp,tmpfs 又已不存在,启动挂载静默失败、/tmp 从挂载表消失。解决:删除 etctmp、etctmp.c 等中间产物后重编(务必保留 etc/ 源码目录),实测挂载表恢复 /tmp type vfat。
九、参考链接
- NuttX 官方配置文档:https://nuttx.apache.org/docs/latest/quickstart/configure.html
- NuttX 编译指南:https://nuttx.apache.org/docs/latest/quickstart/compiling.html
- Kconfig 语言参考:https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html
- kconfiglib 项目:https://github.com/ulfalizer/Kconfiglib
- NuttX 13.1.0 发行包:https://dlcdn.apache.org/nuttx/13.1.0/
- sim 板级源码:https://github.com/apache/nuttx/tree/release/13.1/boards/sim/sim/sim