198 lines
8.5 KiB
Markdown
198 lines
8.5 KiB
Markdown
# 问题处理文档
|
||
|
||
---
|
||
|
||
## 2026-06-15
|
||
|
||
### #11 libcom_channel + libcom_scan 合并为 libcom_decode
|
||
|
||
**问题**:两个模块紧耦合(com_channel 调 com_recv_data,com_scan 调 com_channel_interface_get),且 app_comm_channel 线程为空壳
|
||
|
||
**修复**:
|
||
1. 合并为 `libcom_decode` 模块,按功能拆分 4 个文件
|
||
2. 整合为 1 个 app 线程(删除空壳线程)
|
||
3. 枚举值合并(ENUM_APP_COMM + ENUM_APP_COM_SCAN → ENUM_APP_COM_DECODE)
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:新建 `src/system/libcom_decode/`(4源文件+makefile),修改 `mySystem.h`、`app_sys.cpp`、`self_ptl.cpp`、`iec.cpp`、`makefile`
|
||
**验证**:`./release/build.sh` 编译通过
|
||
|
||
---
|
||
|
||
### #10 libcomm + 调用者缺陷修复
|
||
|
||
**问题**:
|
||
1. UDP `recvfrom` 永久阻塞,无超时无退出机制
|
||
2. UDP `close` 后 `state_cb(id, -1, disconnected)`,回调拿到的是已关闭的 -1
|
||
3. `com_scan`/`self_ptl`/`iec` 三处 send+event 回滚竞态:`msg_queue_send` 成功 → `event_send` 失败 → `try_recv` 可能取出旧消息
|
||
4. `com_channel_recv_cb` 中 `comm_send` 到已断开的 TCP_C_0 不检查 fd
|
||
|
||
**修复**:
|
||
1. `udp_run` 中 `recvfrom` 前加 `select` 1s 超时
|
||
2. `udp_close` 先保存 fd 再 close,回调传入正确值
|
||
3. 三处改为先 `event_send` 再 `msg_queue_send`,消除回滚竞态
|
||
4. `com_channel_recv_cb` 在 `comm_send` 前检查 `socket_fd >= 0`
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:`comm_udp.cpp`, `com_scan.cpp`, `self_ptl.cpp`, `iec.cpp`, `com_channel.cpp`
|
||
**验证**:`./release/build.sh` 编译通过
|
||
|
||
---
|
||
|
||
### #9 libcomm 模块修补
|
||
|
||
**问题**:
|
||
1. UART send 不完整:裸 write 无流量控制,缺少原始模式终端设置(~ICANON/~ECHO/~OPOST)
|
||
2. TCP client select 100ms 空轮询,空闲时 CPU 空转
|
||
3. 无 `comm_destroy` 接口,内存泄漏
|
||
4. `stru_comm` 结构体中 6 个函数指针字段从未使用,冗余
|
||
|
||
**修复**:
|
||
1. 重写 `comm_uart.cpp`:恢复原始模式 + VTIME=1 读超时 + tcflush/tcdrain 完整发送
|
||
2. TCP client select 去掉超时参数,永久阻塞
|
||
3. 新增 `comm_destroy(id)` API
|
||
4. 删除 `stru_comm` 中 6 个未使用字段
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:`comm_uart.cpp`, `comm_tcp.cpp`, `comm.cpp`, `comm.h`, `myComm.h`
|
||
**验证**:`./release/build.sh` 编译通过
|
||
|
||
---
|
||
|
||
### #8 libtask 事件/消息队列缺陷修复
|
||
|
||
**问题**:
|
||
1. `task_event_destroy` 和 `task_msg_queue_destroy` 在 `free(p)` 后仍访问 `p->name`(use-after-free)
|
||
2. `task_event_recv` 中 AND 检查用 `opt == TASK_EVENT_FLAG_AND` 而非位测试,`AND | CLEAR` 组合被误识别为 OR
|
||
3. `task_event_send` 用 `pthread_cond_signal` 而非 `cond_broadcast`,多等待者场景可能唤醒不足
|
||
|
||
**修复**:
|
||
1. `free(p)` 移到 `LOG_I` 之后,先打日志再释放
|
||
2. `opt ==` 改为 `opt &` 位测试
|
||
3. `cond_signal` 改为 `cond_broadcast`
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:`src/public/libtask/src/myTask.c`
|
||
**验证**:`./release/build.sh` 编译通过
|
||
|
||
---
|
||
|
||
### #7 libtask 定时器 SIGEV_THREAD 线程爆炸
|
||
|
||
**问题**:`myTask.c` 使用 `timer_create(CLOCK_REALTIME, SIGEV_THREAD)` 模式,每次定时器超时内核创建一个新线程执行回调。9 个 app 线程 × 3 个定时器(10ms/100ms/1000ms)= 27 个定时器,每秒约 1000 次内核线程创建/销毁,系统开销极大。回调仅做 `task_event_send` 设一个事件位(几微秒),线程创建开销远大于实际工作。
|
||
|
||
**需求**:
|
||
1. 消除每次定时器超时创建线程的开销
|
||
2. API 签名全部不变,调用方零修改
|
||
3. 仅 Linux 平台
|
||
|
||
**处理计划**:[libtask定时器SIGEV_THREAD优化](./plan/libtask定时器SIGEV_THREAD优化.md)
|
||
|
||
**修复方案**:用 Linux **timerfd** + **epoll** + **单例管理器线程** 替代 `SIGEV_THREAD`:
|
||
- `stru_task_timer`:`timer_t timerid` → `int timerfd`,新增 `pthread_cond_t cond`、`int in_epoll`
|
||
- 删除 `task_timer_sig_handler`,新增 `timer_manager_thread`(epoll_wait 监听所有 timerfd,超时时调用原始回调)
|
||
- `task_timer_stop/destroy`:`while(running) usleep(1000)` 忙等 → `cond_wait` 零 CPU 阻塞
|
||
- 6 个 API 签名全部不变
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:`src/public/libtask/src/myTask.c`(仅此一个文件,`release/inc/myTask.h` 无改动)
|
||
**验证**:`./release/build.sh` 编译通过,零错误零警告
|
||
|
||
---
|
||
|
||
## 2026-06-12
|
||
|
||
### #6 Tab 补全子命令前缀丢失
|
||
|
||
**问题**:输入 `datacenter ` + `p` + Tab 后,`datacenter ` 前缀被 `param` 覆盖,整行只剩 `param`。
|
||
|
||
**根因**:`linenoiseEdit` 中 Tab 键处理用 `strncpy(buf, completions[0])` 整行覆盖。
|
||
|
||
**修复**:改为找到最后一个空格,只替换空格之后的当前词,保留前缀。
|
||
|
||
---
|
||
|
||
### #5 app_cmd 交互卡顿与 Tab 补全失效
|
||
|
||
**问题**:上一轮将 `cmd_recv` 改为 select 非阻塞 + EV_TIMER2(100ms) 后:
|
||
1. 回车后 `cmd>` 回显不及时,有明显卡顿感
|
||
2. 输入 `d` 后按 Tab,不会弹出命令补全
|
||
|
||
**根因**:`select()` 方案与 `linenoise()` 交互式行编辑器根本性不兼容——select 时终端处于规范模式(行缓冲),Tab 不是行终止符不会触发 select;100ms 定时器引入最多 100ms 延迟。
|
||
|
||
**修复**:将 `app_cmd` 线程从定时器事件驱动改为简单阻塞循环 `while(1) { cmd_recv(); }`。`linenoise()` 自管理终端模式切换和 Tab 补全,线程阻塞在 stdin 上零延迟响应。
|
||
|
||
**验证**:编译通过。
|
||
|
||
---
|
||
|
||
### #4 app_cmd 线程启用后 CPU 飙升至 108%
|
||
|
||
**问题**:`app_cmd` 线程中 `cmd_recv()` 启用后,CPU 占用 108%,导致该入口被暂时屏蔽([app_cmd.cpp:117](src/system/RTU/src/app_cmd.cpp#L117) 被注释)。
|
||
|
||
**根因**:
|
||
1. **死循环层面**:`linenoiseEdit()`([my_cmd.cpp:107](src/public/libcmd/src/my_cmd.cpp#L107))中 `read()` 只检查了返回 `-1`,未处理返回 `0`(EOF)。当进程无真正控制终端时(RTU 嵌入式环境常见),`read()` 立即返回 0 形成死循环。
|
||
2. **架构层面**:`linenoise()` 是同步阻塞调用,却被放在 10ms 定时器 EVI_TIMER1 中执行,与其他定时器事件循环模型不兼容。
|
||
|
||
**修复内容**:
|
||
1. `my_cmd.cpp`:`read()` 返回值判断改为 `<= 0`(含 EOF 处理);`enableRawMode()` 返回值在 `linenoise()` 中检查,失败直接返回 NULL
|
||
2. `app_cmd.cpp`:重构 `cmd_recv` 为 `cmd_recv_nonblock()`,用 `select()` 实现非阻塞 stdin 检查 + `isatty()` 过滤非终端环境,移至 EV_TIMER2(100ms)调用
|
||
3. `CLAUDE.md`:全文翻译为中文
|
||
|
||
**验证**:编译通过(零错误零警告),`./test/RTU < /dev/null` 运行 3 秒 CPU 占用 0.0%。
|
||
|
||
---
|
||
|
||
## 2026-06-10
|
||
|
||
### #1 libmms_m RCB 订阅编号硬编码
|
||
|
||
**问题**:`mms_m_icd_report_init()` 中硬编码 `if(0 == rpt_no.compare("01"))`,只订阅编号为 `"01"` 的 RCB,其他编号被丢弃,无法按需灵活订阅多个 RCB 实例。
|
||
|
||
**需求**:
|
||
1. 灵活可配置订阅的控制块编号
|
||
2. libmms_m 提供接口,由 libiec61850m 传入
|
||
3. 可传入一个或多个编号,不传默认 `"01"`
|
||
4. 无效数据打印错误并返回失败
|
||
|
||
**处理计划**:[RCB订阅编号可配置化](./mid/RCB订阅编号可配置化.md)
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:`myMms_m.h`, `mms_m.h`, `mms_m.cpp`, `iec61850m.cpp`
|
||
|
||
---
|
||
|
||
### #2 libweb_server 模块缺陷修复
|
||
|
||
**问题**:WebSocket 服务端模块存在 10 个缺陷,含严重级别(单客户端、多线程竞态、悬空指针、non-null-terminated UB)和高危/中等级别(调试 printf、SBO 阻塞、LOG 格式化缺失等)。
|
||
|
||
**需求**:
|
||
1. 兼容多个 WebSocket 客户端同时连接
|
||
2. 完善断连处理
|
||
3. 多线程安全保护
|
||
4. 修复 UB 和格式化问题
|
||
5. 增量推送优化(仅变化时发送)
|
||
|
||
**处理计划**:[libweb_server模块分析](../工程/libweb_server模块分析.md)
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:`web_server.cpp`, `ws_method.cpp`
|
||
|
||
---
|
||
|
||
### #3 libweb_server 多连接资源共享冲突
|
||
|
||
**问题**:支持多客户端后,所有连接共享同一套全局信号资源(`g_ws_out_signals` 等),一个客户端的 add/del 操作会影响其他客户端的数据推送。
|
||
|
||
**需求**:
|
||
1. 每个连接独立的信号资源(per-connection session)
|
||
2. 连接建立时自动开辟资源,断开时自动释放
|
||
3. `ws_task()` 按 session 独立构建 JSON 推送到对应连接
|
||
|
||
**处理计划**:[libweb_server模块分析](../工程/libweb_server模块分析.md)
|
||
|
||
**状态**:✅ 已完成
|
||
**涉及文件**:`ws_method.h`, `ws_method.cpp`, `web_server.cpp`
|
||
|
||
---
|