# app_cmd 线程 CPU 108% 问题修复 ## 日期 2026-06-12 ## 背景 `app_cmd` 线程是 CLI 命令行处理线程,属于 9 个应用线程之一。其主循环采用定时器驱动的事件模型(EV_TIMER1=10ms、EV_TIMER2=100ms、EV_TIMER3=1000ms)。原本 `cmd_recv()` 在 EV_TIMER1 中每次被调用,但该调用已被注释掉,原因是启用后 CPU 占用飙升至 108%。 ## 根因 ### 直接原因:`read()` 返回 EOF 未处理 `linenoiseEdit()`([my_cmd.cpp:107](src/public/libcmd/src/my_cmd.cpp#L107))中读取 stdin 的代码: ```c if (read(STDIN_FILENO, &c, 1) == -1) return -1; ``` 只检查了 `-1`(错误),未处理返回 `0`(EOF)。当进程无真正控制终端时(RTU 嵌入式部署环境常见),stdin 处于 EOF 状态,`read()` 立即返回 0。代码不匹配 `-1`,继续执行,`c` 为未定义值,后落入 `buf[len++] = c` 分支,循环回 `read()` — 死循环,CPU 100%+。 ### 架构层面问题 | 问题 | 说明 | |------|------| | 阻塞 I/O 在定时器线程中 | `linenoise()` 是同步阻塞调用,放在 10ms 定时器中导致线程被阻塞 | | 无 stdin 就绪检查 | 没有用 `select()`/`poll()` 先检查 stdin 是否有数据 | | 终端模式反复切换 | 每次 `linenoise()` 调用都 `tcgetattr`+`tcsetattr`,频率过高 | | `tcsetattr` 返回值未检查 | `linenoise()` 中调用 `enableRawMode()` 未检查返回值 | ### 定时器机制 定时器使用 POSIX `timer_create(CLOCK_REALTIME, SIGEV_THREAD, ...)`,每次超时内核创建新线程执行回调 → `task_event_send` → `pthread_cond_signal` 唤醒 app 线程。 ## 修复内容 ### 1. my_cmd.cpp — 修复 EOF 处理和返回值检查 - `read()` 返回值判断从 `== -1` 改为 `<= 0`,覆盖 EOF 场景 - `linenoise()` 中检查 `enableRawMode()` 返回值,失败直接返回 NULL,避免在有问题的终端上执行读取循环 ### 2. app_cmd.cpp — 重构为非阻塞模式 - 新增 `isatty(STDIN_FILENO)` 前置检查,非终端环境直接跳过 - 用 `select()` 零超时检测 stdin 可读性,仅在数据就绪时才调用 `linenoise()` - 从 EV_TIMER1(10ms)移至 EV_TIMER2(100ms),降低终端模式切换频率 ## 验证 - 编译:零错误零警告 - 测试:`./test/RTU < /dev/null` 运行 3 秒,CPU 占用 0.0%(修复前为 108%) ## 影响文件 | 文件 | 改动 | |------|------| | [my_cmd.cpp](src/public/libcmd/src/my_cmd.cpp) | `read()` EOF 处理;`enableRawMode()` 返回值检查 | | [app_cmd.cpp](src/system/RTU/src/app_cmd.cpp) | 非阻塞 `cmd_recv_nonblock()`;`isatty` + `select`;移至 EV_TIMER2 | | [CLAUDE.md](CLAUDE.md) | 全文翻译为中文 |