Apache NuttX 13.1.0 系统配置详解

拆解 Apache NuttX 13.1.0 的配置体系,覆盖 defconfig 与 .config 的三层流水线、apps 树 select 强制、ETC_ROMFS 启动脚本挂载链、修改配置的实战方法与 savedefconfig 回写规则,全部结论实测于 sim:nsh。

Apache NuttXNuttXApacheRTOS嵌入式配置详解2026/10/15

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 宏
  1. defconfig:板级配置的最小表达,只记录与默认值不同的项。sim:nsh 的 defconfig 只有 82 行,文件头写着 autogenerated,官方不建议手改。
  2. configure.sh:配置搬运工。它把指定 defconfig 拷贝为内核树根的 .config,再调用 sethost.sh 按宿主类型(Linux/macOS/Windows)调整 HOST_* 宏,最后跑一次 olddefconfig 补齐依赖。实测一次干净配置的输出:
Loaded configuration '.config'
Configuration saved to '.config'
  1. .config:全量展开的配置文件,44405 字节、1900 余行,编译系统唯一的配置事实来源。
  2. 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_COMPONENTSnnot set允许纳入 BSD 许可的第三方组件
CONFIG_ALLOW_GPL_COMPONENTSnnot set允许纳入 GPL 组件(会改变整体许可属性)
CONFIG_ALLOW_MIT_COMPONENTSnnot set允许纳入 MIT 组件
CONFIG_ALLOW_CUSTOM_PERMISSIVE_COMPONENTSnnot set允许自定义宽松许可组件

解说:sim:nsh 默认零外部许可依赖,成品是纯 Apache-2.0。商用产品若引入 GPL 组件需在此显式放行,构建系统会在链接层面做把关。

4.2 构建基础组(Build Setup)

配置项当前值含义与实测依据
CONFIG_EXPERIMENTAL=y开启解锁实验性选项(模拟器配置默认开启)
CONFIG_DEFAULT_SMALLnot set未启用"小体积优先"预设
CONFIG_HOST_LINUX=yLinux 宿主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=ySystem V ABI决定宿主调用约定与汇编桩
CONFIG_SIM_HOSTFS=y宿主文件系统支持/data 挂载的前提
CONFIG_SIM_WALLTIME_SIGNAL=y墙钟信号让模拟器按真实时间派发信号
CONFIG_RAM_SIZE=00使用默认 RAM 尺寸
CONFIG_MM_REGIONS=11 个内存区单堆管理
CONFIG_START_YEAR=20082008系统起始年份(RTC 缺省纪元)

解说:CONFIG_SIM_X8664_SYSTEMV 与 CONFIG_SIM_HOSTFS 由 sethost.sh 依据宿主自动决定,这就是"同一 defconfig 在 macOS 上会得到不同 .config"的原因——defconfig 只存差异,宿主差异属于每次配置时的动态项。

4.4 文件系统组

配置项当前值含义与实测依据
CONFIG_FS_FAT=yFAT/tmp 内存盘的文件系统
CONFIG_FAT_LFN=y / FAT_LCNAMES=y长文件名支持长名与大小写保留
CONFIG_FS_ROMFS=yROMFS/etc 只读根
CONFIG_FS_HOSTFS=yHOSTFS/data 直通宿主目录
CONFIG_FS_BINFS=yBINFS/bin 内建程序挂载点
CONFIG_FS_PROCFS=yPROCFS/proc 进程信息
CONFIG_FS_RAMMAP=yRAMMAP基于 RAM 的 mmap 支持
CONFIG_FS_TMPFSnot 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=1ram1 之外的 romfs 设备号df 可见 /etc 为 64 字节块、8 块
CONFIG_ETC_FATDEVNO=2内存盘设备号 2运行期 /dev/ram2 存在(ls /dev 实测)
CONFIG_ETC_FATSECTSIZE=512512 字节扇区df 的 Block Size 列吻合
CONFIG_ETC_FATNSECTORS=10241024 扇区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=yboardctl 总开关应用态调用板级控制接口的入口
CONFIG_BOARDCTL_POWEROFF=y关机命令NSH poweroff 的后端
CONFIG_BOARDCTL_APP_SYMTAB=y应用符号表内建应用注册所需
CONFIG_BOARDCTL_IOCTLnot 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

于是产生了三个实测可验证的现象:

  1. .config 第 300 行是 CONFIG_BOARDCTL_MKRD=y——虽然内核 Kconfig 默认 n;
  2. 板级 defconfig 与 make savedefconfig 的产物里都找不到这一项——被 select 强制的值等同于默认结果,最小 defconfig 无需记录;
  3. 想真正关掉 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=yNSH 库shell 主体编译进系统
CONFIG_NSH_CONSOLE=y控制台 shellinit.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 相关行,排序去重后加自动生成头写出。三条规则需要牢记:

  1. 只保存与默认值不同的项——所以 44KB 的 .config 回写成 2KB 的 defconfig;
  2. select 强制的值不保存——BOARDCTL_MKRD 就是实例(4.6 节);
  3. 凭据类配置主动剔除——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。

九、参考链接