NuttX NSH 内置应用命令表生成机制解析
一、NSH 命令分类
在 NSH 终端输入help 命令,可查看所有支持的命令,整体分为NSH 内置命令和内置应用命令两类,二者运行机制差异显著。
1.1 NSH 内置命令(上半部分)
通过CMD_MAP 宏注册在 g_cmdmap 数组中,核心特点:
-
功能偏向系统管理;
-
逻辑直接在 NSH 内核中实现;
-
无需创建新任务,直接在 NSH 上下文运行。
1.2 内置应用命令(下半部分)
需开启宏 CONFIG_NSH_BUILTIN_APPS 才可使用,核心特点:
-
以功能性应用程序为主;
-
属于独立应用,拥有独立栈空间、任务优先级;
-
必须创建新任务才能运行。
二、内置应用命令表生成原理
NuttX 内置应用命令表并非静态编写,而是在编译构建阶段动态生成,核心相关文件路径:rtos/apps/builtin。以下为完整生成流程。
2.1 核心数组:g_builtins(builtin_list.c)
builtin_list.c 文件定义了 NuttX 内置应用表核心数组 g_builtins,所有内置应用信息均通过该数组管理,核心代码如下:
const struct builtin_s g_builtins[] =
{
# include "builtin_list.h"
#ifdef CONFIG_SCHED_USER_IDENTITY
{ NULL, 0, 0, 0, 0, 0, 0 }
#else
{ NULL, 0, 0, 0 }
#endif
};
该数组存储每个内置应用的命令名称、入口主函数、任务优先级、栈大小等核心信息。需要注意的是:
-
builtin_list.h为编译时动态生成文件,源码仓库中不存在; -
无法通过静态源码查看完整应用表,仅能在编译完成后确定最终内容。
2.2 配置模板文件(.in 文件)
NuttX 通过.in 模板文件实现编译时动态生成头文件,是自动注册机制的核心。
2.2.1 builtin_list.h.in
文件仅包含一个占位符变量:${builtin_list_string}。
构建时 CMake 会读取该模板文件,将占位符替换为所有内置应用的结构体信息,最终生成可被源码引用的 builtin_list.h。
2.2.2 builtin_proto.h.in
作用与上述模板一致,编译后生成 builtin_proto.h,该文件用于存放所有内置应用的入口函数声明,解决函数调用未定义问题。
2.3 CMake 自动扫描注册逻辑(CMakeLists.txt)
rtos/apps/builtin/CMakeLists.txt 实现了内置应用的自动扫描、信息收集、文件生成全流程,核心代码逻辑如下:
list(APPEND nuttx_app_libs ${only_registers})
set(builtin_list_string)
set(builtin_proto_string)
# 遍历所有已注册的内置应用模块
foreach(module ${nuttx_app_libs})
# 获取应用的名称、入口函数、优先级、栈大小属性
get_target_property(APP_MAIN ${module} APP_MAIN)
get_target_property(APP_NAME ${module} APP_NAME)
get_target_property(APP_PRIORITY ${module} APP_PRIORITY)
get_target_property(APP_STACK ${module} APP_STACK)
# 拼接内置应用结构体数据(用于生成 builtin_list.h)
set(builtin_list_string
"${builtin_list_string}\{ \"${APP_NAME}\", ${APP_PRIORITY}, ${APP_STACK}, ${APP_MAIN} \}, \n"
)
# 拼接函数声明(用于生成 builtin_proto.h)
set(builtin_proto_string
"${builtin_proto_string}int ${APP_MAIN}(int argc, char *argv[]);\n")
endforeach()
# 根据模板生成最终头文件
configure_file(builtin_proto.h.in builtin_proto.h)
configure_file(builtin_list.h.in builtin_list.h)
核心逻辑总结:CMake 在编译前遍历所有应用模块,提取应用属性,动态拼接代码内容,最终生成两个关键头文件,自动完成内置应用注册表构建。
三、单个内置应用的注册方式
所有自定义内置应用,均需在自身的 CMakeLists.txt 中通过 nuttx_add_application 函数完成注册,开启对应宏开关即可生效。
3.1 NuttX 内核应用注册示例
内核应用统一存放路径:rtos/apps,以 wapi 工具为例(路径:rtos/apps/wireless/wapi/CMakeLists.txt):
if(CONFIG_WIRELESS_WAPI)
set(CSRCS src/network.c src/util.c src/wireless.c src/driver_wext.c)
# 开启 WAPI 命令行工具时,注册为 NSH 内置应用
if(CONFIG_WIRELESS_WAPI_CMDTOOL)
nuttx_add_application(
NAME
${CONFIG_WIRELESS_WAPI_PROGNAME}
SRCS
src/wapi.c
STACKSIZE
${CONFIG_WIRELESS_WAPI_STACKSIZE}
PRIORITY
${CONFIG_WIRELESS_WAPI_PRIORITY})
endif()
target_sources(apps PRIVATE ${CSRCS})
endif()
参数说明:
-
CONFIG_WIRELESS_WAPI_CMDTOOL:功能宏开关,控制是否注册为 NSH 命令; -
NAME:NSH 终端调用的命令名;
-
SRCS:包含应用 main 入口的源码文件;
-
STACKSIZE:应用任务栈大小;
-
PRIORITY:应用任务调度优先级。
3.2 OpenVela 系统应用路径
OpenVela 框架层应用存放路径:rtos/frameworks,典型案例:蓝牙应用配置路径 rtos/frameworks/connectivity/bluetooth/CMakeLists.txt。
四、NSH 命令执行逻辑
NSH 解析并执行命令的核心函数为 nsh_execute(路径:rtos/apps/nshlib/nsh_parse.c),支持三类命令处理逻辑:
// 1. 执行内置应用命令
ret = nsh_builtin(vtbl, argv[0], argv, param);
// 2. 执行文件系统应用命令
ret = nsh_fileapp(vtbl, argv[0], argv, param);
// 3. 执行原生 NSH 系统命令
ret = nsh_command(vtbl, argc, argv);
五、新增内置应用开发文档
系统官方提供了完整的新增应用开发教程,包含 nuttx_add_application 函数详解、新增步骤、配置方法,文档路径:
rtos/docs/zh-cn/app_dev/system_apps/hello_world/Hello_World.md
六、动态生成机制核心优点
该设计实现了源码与构建配置解耦,优势显著:
-
无需在源码仓库维护频繁变动的
builtin_list.h文件,减少源码冗余; -
新增内置应用时,无需手动修改
g_builtins数组,CMake 自动扫描注册; -
通过宏开关灵活控制应用是否启用,适配不同编译配置场景。
浙公网安备 33010602011771号