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 自动扫描注册;

  • 通过宏开关灵活控制应用是否启用,适配不同编译配置场景。

posted @ 2026-07-02 21:00  自由的Charles  阅读(4)  评论(0)    收藏  举报