61 lines
2.6 KiB
Markdown
61 lines
2.6 KiB
Markdown
# 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) | 全文翻译为中文 |
|