集成指南

XWSH 的集成指南:两种运行模式的详细对比、代码示例和最佳实践。

集成模式概览

XWSH 提供两种集成模式,适应不同的应用场景和资源约束。选择哪种模式取决于线程管理需求、资源可用性和系统架构。

模式对比矩阵

特性 独立线程模式 (xwsh_start()) 嵌入式模式 (xwsh_init() + xwsh_loop())
线程资源 需要专用栈空间(1-2KB) 使用调用者线程栈
线程管理 XWSH 内部创建管理线程 用户控制循环调用
优先级 XWSH_THD_PRIORITY(实时优先级降级) 与调用者线程相同
阻塞方式 线程在 xwsh_loop() 中阻塞 在用户循环中阻塞
冻结支持 自动支持线程冻结 需要用户手动处理
适用场景 独立 CLI 任务、系统调试 集成到现有任务、资源受限环境
代码复杂度 低(自动管理) 中(用户需要管理循环)

独立线程模式

工作原理

独立线程模式创建一个专用线程运行 XWSH,线程由 XWSH 模块内部管理。

// xwmd/cli/xwsh/mi.c:24
#define XWSH_THD_PRIORITY XWOS_SKD_PRIORITY_DROP(XWOS_SKD_PRIORITY_RT_MAX, 0)

优先级说明

  • XWOS_SKD_PRIORITY_RT_MAX:系统实时最高优先级
  • XWOS_SKD_PRIORITY_DROP(max, n):从 max 降低 n
  • XWSH_THD_PRIORITY:实时优先级降级 0 级,确保 CLI 响应性

使用步骤

1. 包含头文件

#include "xwmd/cli/xwsh/mi.h"

2. 定义线程栈

// 栈大小建议:1-2KB,取决于命令复杂度和递归深度
xwstk_t xwsh_stack[1024];  // 1024 * sizeof(xwstk_t) 字节

3. 添加外部命令(可选)

// 必须在 xwsh_start() 之前调用
const struct xwsh_cmd my_cmds[] = {
        { .name = "test", .func = my_test_cmd, .desc = "test command" },
};
xwsh_set_ext_cmd_table(my_cmds, 1);

4. 启动 XWSH

xwer_t rc = xwsh_start(xwsh_stack, sizeof(xwsh_stack));
if (rc < 0) {
        // 错误处理
        printf("Failed to start XWSH: %d\r\n", rc);
}

线程主函数

// xwmd/cli/xwsh/mi.c:59
xwer_t xwsh_thd_mainfunc(void * arg)
{
        char buf[XWSH_MAXINPUT];
        
        XWOS_UNUSED(arg);
        xwsh_init();  // 显示logo,初始化readline
        
        while (!xwos_cthd_shld_stop()) {
                if (xwos_cthd_shld_frz()) {
                        xwos_cthd_freeze();  // 支持线程冻结
                }
                xwsh_loop(buf);  // 读取并执行命令
        }
        
        return XWOK;
}

线程生命周期

  1. 创建线程,优先级为 XWSH_THD_PRIORITY
  2. 显示系统 logo 和版本信息
  3. 初始化 CherryRL 读行库
  4. 进入主循环,等待用户输入
  5. 支持线程冻结/恢复
  6. 线程停止条件:xwos_cthd_shld_stop() 返回 true

完整示例

#include <xwos/standard.h>
#include "xwmd/cli/xwsh/mi.h"

/* 自定义命令 */
xwer_t my_info_cmd(xwsz_t argc, char ** argv)
{
        XWOS_UNUSED(argc);
        XWOS_UNUSED(argv);
        printf("System information:\r\n");
        printf("  XWSH version: 1.0\r\n");
        printf("  Build time: %s %s\r\n", __DATE__, __TIME__);
        return XWOK;
}

xwer_t my_echo_cmd(xwsz_t argc, char ** argv)
{
        for (xwsz_t i = 1; i < argc; i++) {
                printf("%s ", argv[i]);
        }
        printf("\r\n");
        return XWOK;
}

/* 命令表 */
const struct xwsh_cmd my_cmds[] = {
        { .name = "info", .func = my_info_cmd, .desc = "show system info" },
        { .name = "echo", .func = my_echo_cmd, .desc = "echo arguments" },
};

int main(void)
{
        xwstk_t xwsh_stack[1024];
        xwer_t rc;
        
        /* 设置外部命令表(必须在启动前调用) */
        rc = xwsh_set_ext_cmd_table(my_cmds, 
                                   sizeof(my_cmds) / sizeof(my_cmds[0]));
        if (rc < 0) {
                printf("Failed to set command table: %d\r\n", rc);
                return rc;
        }
        
        /* 启动 XWSH */
        rc = xwsh_start(xwsh_stack, sizeof(xwsh_stack));
        if (rc < 0) {
                printf("Failed to start XWSH: %d\r\n", rc);
                return rc;
        }
        
        /* 主线程执行其他任务 */
        while (1) {
                // 其他应用逻辑
                xwos_cthd_sleep_ms(1000);
        }
        
        return 0;
}

嵌入式模式

工作原理

嵌入式模式将 XWSH 集成到现有线程中,由用户代码控制命令循环的执行。

使用步骤

1. 包含头文件

#include "xwmd/cli/xwsh/mi.h"

2. 添加外部命令(可选)

// 必须在 xwsh_init() 之前调用
const struct xwsh_cmd my_cmds[] = {
        { .name = "test", .func = my_test_cmd, .desc = "test command" },
};
xwsh_set_ext_cmd_table(my_cmds, 1);

3. 初始化 XWSH

xwsh_init();  // 显示logo,初始化readline

4. 运行主循环

char buf[XWSH_MAXINPUT];
while (!exit_condition) {
        xwsh_loop(buf);
        // 可在此处添加其他任务逻辑
}

完整示例

#include <xwos/standard.h>
#include "xwmd/cli/xwsh/mi.h"

/* 自定义命令 */
xwer_t my_status_cmd(xwsz_t argc, char ** argv)
{
        XWOS_UNUSED(argc);
        XWOS_UNUSED(argv);
        printf("System status: OK\r\n");
        printf("Free memory: %d bytes\r\n", get_free_memory());
        return XWOK;
}

/* 命令表 */
const struct xwsh_cmd my_cmds[] = {
        { .name = "status", .func = my_status_cmd, .desc = "show system status" },
};

/* 主任务函数 */
xwer_t main_task(void * arg)
{
        char buf[XWSH_MAXINPUT];
        xwer_t rc;
        
        XWOS_UNUSED(arg);
        
        /* 设置外部命令表 */
        rc = xwsh_set_ext_cmd_table(my_cmds, 
                                   sizeof(my_cmds) / sizeof(my_cmds[0]));
        if (rc < 0) {
                printf("Failed to set command table: %d\r\n", rc);
                return rc;
        }
        
        /* 初始化 XWSH */
        xwsh_init();
        
        /* 主循环 */
        while (!xwos_cthd_shld_stop()) {
                /* 处理 CLI 输入 */
                xwsh_loop(buf);
                
                /* 执行其他任务(非阻塞) */
                process_background_tasks();
                
                /* 短暂休眠,避免忙等待 */
                xwos_cthd_sleep_ms(10);
        }
        
        return XWOK;
}

/* 带超时的版本 */
xwer_t main_task_with_timeout(void * arg)
{
        char buf[XWSH_MAXINPUT];
        xwtm_t timeout = 100;  // 100ms 超时
        
        XWOS_UNUSED(arg);
        
        xwsh_init();
        
        while (!xwos_cthd_shld_stop()) {
                /* 设置 stdin 非阻塞模式(如支持) */
                set_stdin_nonblocking();
                
                /* 带超时的命令循环 */
                xwtm_t start = xwos_cthd_get_timestamp();
                while ((xwos_cthd_get_timestamp() - start) < timeout) {
                        xwsh_loop(buf);
                }
                
                /* 执行其他周期性任务 */
                periodic_task();
        }
        
        return XWOK;
}

最佳实践

1. 模式选择指南

选择独立线程模式当

  • 需要独立的 CLI 线程,不影响主任务实时性
  • 系统资源充足(有额外栈空间)
  • CLI 需要特定的优先级设置
  • 希望自动处理线程冻结/恢复

选择嵌入式模式当

  • 系统资源紧张(无法分配额外栈空间)
  • 需要将 CLI 集成到现有任务循环中
  • 需要更精细的控制命令执行时机
  • 应用程序已有一个主循环架构

2. 优先级设置

独立线程模式优先级建议

// 高响应性(调试用途)
#define XWSH_HIGH_PRIORITY XWOS_SKD_PRIORITY_DROP(XWOS_SKD_PRIORITY_RT_MAX, 0)

// 中等优先级(一般用途)
#define XWSH_MEDIUM_PRIORITY XWOS_SKD_PRIORITY_DROP(XWOS_SKD_PRIORITY_RT_MAX, 5)

// 低优先级(后台任务)
#define XWSH_LOW_PRIORITY XWOS_SKD_PRIORITY_DROP(XWOS_SKD_PRIORITY_RT_MAX, 10)

修改方法(编辑 mi.c:22):

#define XWSH_THD_PRIORITY XWOS_SKD_PRIORITY_DROP(XWOS_SKD_PRIORITY_RT_MAX, 5)

嵌入式模式优先级

嵌入式模式的优先级取决于调用者线程。建议:

  • 调试线程:较高优先级,确保快速响应
  • 后台线程:较低优先级,不干扰关键任务
  • 主线程:根据应用需求设置

3. 资源规划

栈大小估算

使用场景 建议栈大小 说明
简单命令 512-1024 字节 基本命令,无递归
复杂命令 1024-2048 字节 有格式化输出、字符串操作
递归命令 2048+ 字节 命令处理函数可能递归

栈使用分析

// 在 xwsh_thd_mainfunc 中添加栈检查
xwsz_t stack_used = xwos_cthd_get_stackused();
xwsz_t stack_size = xwos_cthd_get_stacksize();
printf("Stack usage: %d/%d bytes (%.1f%%)\r\n", 
       stack_used, stack_size, 
       (stack_used * 100.0) / stack_size);

内存占用统计

组件 大小 备注
XWSH 代码 ~3-5 KB 取决于编译优化
静态缓冲区 ~1.2 KB 提示符+历史记录
线程栈 1-2 KB 独立模式需要
CherryRL ~1 KB 库代码大小

4. 错误处理

启动失败处理

xwer_t rc = xwsh_start(stack, stack_size);
if (rc < 0) {
        switch (rc) {
        case -ENOMEM:
                printf("Insufficient memory for XWSH thread\r\n");
                break;
        case -EINVAL:
                printf("Invalid stack parameters\r\n");
                break;
        default:
                printf("Failed to start XWSH: %d\r\n", rc);
                break;
        }
        // 降级到嵌入式模式或禁用 CLI
        fallback_to_embedded_mode();
}

命令执行错误处理

xwer_t safe_cmd_handler(xwsz_t argc, char ** argv)
{
        xwer_t rc = XWOK;
        
        // 参数验证
        if (argc < 2) {
                printf("Usage: %s <param>\r\n", argv[0]);
                rc = -EINVAL;
                goto err_param;
        }
        
        // 资源分配检查
        if (!check_resource_available()) {
                rc = -ENOMEM;
                goto err_resource;
        }
        
        // 主逻辑(可能失败)
        rc = perform_operation(argv[1]);
        if (rc < 0) {
                printf("Operation failed: %d\r\n", rc);
                goto err_operation;
        }
        
        printf("Success\r\n");
        return XWOK;
        
err_operation:
        cleanup_operation();
err_resource:
err_param:
        return rc;
}

5. 性能优化

减少阻塞时间

// 非阻塞输入检查(如平台支持)
if (stdin_has_data()) {
    char buf[XWSH_MAXINPUT];
    xwsh_loop(buf);
} else {
    // 执行其他任务
    process_other_tasks();
}

命令响应时间优化

// 复杂命令分步执行
xwer_t long_running_cmd(xwsz_t argc, char ** argv)
{
        static xwsz_t step = 0;
        static xwsz_t total = 100;
        
        if (argc > 1 && strcmp(argv[1], "reset") == 0) {
                step = 0;
                printf("Reset progress\r\n");
                return XWOK;
        }
        
        if (step < total) {
                printf("Progress: %d/%d\r\n", step + 1, total);
                perform_step(step);
                step++;
                
                if (step == total) {
                        printf("Completed\r\n");
                }
        } else {
                printf("Already completed. Use 'cmd reset' to restart.\r\n");
        }
        
        return XWOK;
}

输出缓冲优化

// 批量输出减少系统调用
void batch_printf(const char *format, ...)
{
        static char buffer[256];
        va_list args;
        
        va_start(args, format);
        vsnprintf(buffer, sizeof(buffer), format, args);
        va_end(args);
        
        // 实际输出
        printf("%s", buffer);
}

6. 安全考虑

输入验证

xwer_t secure_cmd(xwsz_t argc, char ** argv)
{
        // 长度检查
        for (xwsz_t i = 0; i < argc; i++) {
                if (strlen(argv[i]) > MAX_ARG_LEN) {
                        printf("Argument %d too long\r\n", i);
                        return -E2BIG;
                }
        }
        
        // 内容检查(防止注入)
        for (xwsz_t i = 0; i < argc; i++) {
                if (!is_valid_input(argv[i])) {
                        printf("Invalid characters in argument %d\r\n", i);
                        return -EINVAL;
                }
        }
        
        // 执行安全操作
        return XWOK;
}

权限控制

// 基于用户角色的命令访问控制
xwer_t privileged_cmd(xwsz_t argc, char ** argv)
{
        if (!current_user_is_admin()) {
                printf("Permission denied. Admin required.\r\n");
                return -EACCES;
        }
        
        // 特权操作
        return XWOK;
}

调试与测试

1. 单元测试框架

#include "xwmd/cli/xwsh/mi.h"

void test_command_parsing(void)
{
        char line[] = "test arg1 arg2 arg3";
        xwsz_t argc;
        char *argv[XWSH_MAX_PARAM_NUM];
        xwer_t rc;
        
        rc = xwsh_split_cmdline(line, &argc, argv, XWSH_MAX_PARAM_NUM);
        
        assert(rc == XWOK);
        assert(argc == 4);
        assert(strcmp(argv[0], "test") == 0);
        assert(strcmp(argv[1], "arg1") == 0);
        assert(strcmp(argv[2], "arg2") == 0);
        assert(strcmp(argv[3], "arg3") == 0);
        
        printf("Command parsing test passed\r\n");
}

2. 集成测试

void test_xwsh_integration(void)
{
        xwstk_t stack[1024];
        xwer_t rc;
        
        // 测试独立线程模式
        rc = xwsh_start(stack, sizeof(stack));
        assert(rc == XWOK);
        
        // 等待线程启动
        xwos_cthd_sleep_ms(100);
        
        // 测试命令执行(通过实际输入或模拟)
        // ...
        
        printf("Integration test passed\r\n");
}

3. 性能测试

void benchmark_command_execution(void)
{
        xwtm_t start, end;
        xwer_t rc;
        char *test_cmds[] = {
                "help",
                "rd 0x20000000 16",
                "wr 0x20000000 0x1234 w",
        };
        
        for (xwsz_t i = 0; i < sizeof(test_cmds)/sizeof(test_cmds[0]); i++) {
                start = xwos_cthd_get_timestamp();
                
                for (xwsz_t j = 0; j < 1000; j++) {
                        rc = xwsh_run_cmdline(test_cmds[i]);
                        assert(rc == XWOK);
                }
                
                end = xwos_cthd_get_timestamp();
                printf("Command '%s': %lld us per execution\r\n", 
                       test_cmds[i], (end - start) / 1000);
        }
}

常见问题解答

Q1: 如何更改提示符?

A: 修改 readline.c 中的 chry_readline_prompt_edit() 调用,或实现自定义的提示符生成函数。

Q2: 支持多行命令吗?

A: 当前版本不支持。需要扩展 CherryRL 配置或修改命令解析逻辑。

Q3: 如何增加历史记录大小?

A: 修改 readline.c:33XWSH_RL_HISTORY_MAXSIZE,必须保持为 2 的幂。

Q4: 命令执行超时如何实现?

A: 在嵌入式模式中,可以在循环中添加超时检查,或使用带超时的输入函数。

Q5: 如何禁用彩色输出?

A: 修改 chry_readline_prompt_edit() 调用,移除 SGR(Select Graphic Rendition)属性。

Q6: 支持命令别名吗?

A: 当前不支持,但可以在命令处理函数中实现,或扩展命令查找逻辑。

Q7: 如何记录所有执行的命令?

A: 在 xwsh_run_cmdline() 执行后添加日志记录,或修改命令执行流程。