fix: 修复南向分包、线程通信和批量下发逻辑
1. apps/chrg/debug_hot.ini: 增加调试断点配置 2. apps/chrg/applications/thread/chrg_north.h: 开启北向调试日志 3. 修正多个头文件函数返回值类型,从void改为int 4. 重构chrg_thread.c: 新增mb消息释放函数,优化邮箱发送逻辑 5. 重写chrg_south.c: 修复分包越界和内存泄漏,增加帧超时处理 6. 重构chrg_sink.c: 统一批量下发接口,修正初始化和运行逻辑 7. 重写chrg_comm.c: 修复系统寄存器写入逻辑,增加通道激活校验 8. 重构chrg_north.c: 修复北向分包越界和内存泄漏,增加超时处理和诊断命令 9. 重构chrg_roll_sou.c: 重构批量下发逻辑,增加重试机制和错误处理
This commit is contained in:
+111
@@ -0,0 +1,111 @@
|
||||
静态排查后,暂未看到明确的 ABBA 型互斥锁环。当前现象更像“内存破坏/资源泄漏后线程停滞”或“状态机无限重试”,而不是整个 RTOS 调度器死锁。
|
||||
|
||||
另外,代码中使用的是 STM32 内部 IWDG,不是外部独立看门狗。
|
||||
|
||||
**可疑点**
|
||||
|
||||
1. **高风险:南北向分包接收存在确定的越界写和泄漏路径**
|
||||
|
||||
[north](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:548) 和 [south](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:309) 在续接分包时计算:
|
||||
|
||||
```text
|
||||
len_need = len_msg - 帧头长度
|
||||
```
|
||||
|
||||
但此时 `pFrame->length` 已经包含之前收到的数据,正确的剩余长度应基于 `len_msg - pFrame->length`。当前代码却从 `payload[pFrame->length]` 开始写入整个“帧头后的总长度”。
|
||||
|
||||
例如总长 21 字节,首批已存 8 字节,下一次仍可能从偏移 8 写入 18 字节,最终写到偏移 26,越过 21 字节堆块。这会破坏相邻堆块、邮箱消息甚至 RT-Thread 内核对象,最终表现为随机线程卡死、互斥量异常或堆分配失败。
|
||||
|
||||
同时,分包超时路径只清除了 `findhead`:
|
||||
|
||||
- [chrg_north.c:627](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:627)
|
||||
- [chrg_south.c:399](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:399)
|
||||
|
||||
没有释放 `pMB/payload`,也没有清除 `length/len_msg`。而且没有新数据时会提前 `continue`,超时检查根本不会执行。串口噪声、丢字节或正常分包都可能不断积累泄漏。
|
||||
|
||||
此外,payload 分配失败时只返回,没有释放刚分配的 `pMB`:
|
||||
|
||||
- [chrg_north.c:518](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:518)
|
||||
- [chrg_south.c:280](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:280)
|
||||
|
||||
这是目前最符合“运行时间越长越容易出现”的可疑点。
|
||||
|
||||
2. **高风险:看门狗喂法无法覆盖业务线程死锁**
|
||||
|
||||
主线程每秒无条件喂狗:[main.c:36](D:/proj/char/chrg_v/apps/chrg/applications/main.c:36)。
|
||||
|
||||
北向轮询线程又在每轮无条件喂同一只狗:[chrg_roll_nor.c:421](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_nor.c:421)。
|
||||
|
||||
因此即使 `thr.comm`、`thr.sou`、`thr.rollsou`、LCD 线程全部停止工作,只要主线程或北向轮询线程仍能调度,IWDG 就永远不会复位。这只是在检测“CPU/调度器是否完全停止”,没有检测业务线程是否健康。
|
||||
|
||||
驱动最终调用的是 `HAL_IWDG_Refresh()`:[drv_wdt.c:40](D:/proj/char/chrg_v/rt-thread/bsp/stm32/libraries/HAL_Drivers/drivers/drv_wdt.c:40)。代码中没有找到外部 WDI 引脚翻转或外部看门狗驱动。
|
||||
|
||||
3. **高风险:当前新增的南向批处理可能永久停在 `RUN_COM`**
|
||||
|
||||
南向批处理遇到 CRC 错误或应答超时后,不增加重试次数、不设置截止时间,也不清除任务:
|
||||
|
||||
- CRC 错误:[chrg_roll_sou.c:608](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:608)
|
||||
- 应答超时:[chrg_roll_sou.c:627](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:627)
|
||||
|
||||
线程会一直重发同一个寄存器,`pending` 永远不清除,`run` 永远保持 `RUN_COM`,正常南向轮询无法恢复。这属于业务状态机死锁,调度器仍正常运行,所以看门狗不会复位。
|
||||
|
||||
这套批处理逻辑是当前工作区相对 `HEAD` 新增的,优先级很高。
|
||||
|
||||
4. **高风险:南向共享状态的互斥保护实际无效**
|
||||
|
||||
[chrg_set_sou_reg](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:435) 先在锁外写入:
|
||||
|
||||
```text
|
||||
pROLL->ch
|
||||
pROLL->reg
|
||||
pROLL->value
|
||||
```
|
||||
|
||||
随后互斥量只保护 `pROLL->run`。消费者也只在锁内读取 `run`,实际使用 `ch/reg/value` 时已经离开锁。
|
||||
|
||||
多个生产者包括 LCD、Modbus 和状态机可能交叉覆盖这些字段,形成“通道来自命令 A、寄存器来自命令 B、值来自命令 C”的组合。
|
||||
|
||||
批处理数组 `g_sou_com_batch` 同样由生产者和 `thr.rollsou` 同时读写,却没有共同的锁:[chrg_roll_sou.c:394](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:394)。
|
||||
|
||||
5. **中高风险:先切换 `RUN_COM`,再填充批处理数据**
|
||||
|
||||
[chrg_sink_diff_send_reg](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_comm.c:2338) 先调用 `chrg_sou_com_sou_set()` 将状态切到 `RUN_COM`,然后才追加寄存器。
|
||||
|
||||
`chrg_sink_work()` 也是相同顺序:[chrg_sink.c:541](D:/proj/char/chrg_v/apps/chrg/applications/utils/chrg_sink.c:541)。
|
||||
|
||||
如果消费者在状态切换后看到尚未填充的批次,会认为没有任务并切回自动模式;随后加入的 `pending` 数据可能一直无人处理。当前线程优先级降低了部分窗口概率,但没有从同步机制上消除竞态。
|
||||
|
||||
6. **中高风险:应答邮箱没有请求、通道或序号关联**
|
||||
|
||||
邮箱消息只有 `length + payload`:[chrg_def.h:95](D:/proj/char/chrg_v/apps/chrg/applications/utils/chrg_def.h:95)。
|
||||
|
||||
南北轮询线程默认“收到的下一帧就是刚才请求的响应”。南向批处理只检查 CRC,任何延迟到达的旧响应都可能让当前批次 `cur_idx++`:[chrg_roll_sou.c:574](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_sou.c:574)。
|
||||
|
||||
在超时、重发、通道切换或邮箱积压后,请求与响应很容易错位,最终造成状态机停在错误步骤或无限重试。
|
||||
|
||||
7. **中风险:所有业务互斥量都是永久等待**
|
||||
|
||||
TTY、南北轮询互斥量统一使用 `RT_WAITING_FOREVER`。例如:
|
||||
|
||||
- [chrg_tty.c:457](D:/proj/char/chrg_v/apps/chrg/applications/bsp/chrg_tty.c:457)
|
||||
- [chrg_comm.c:2246](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_comm.c:2246)
|
||||
- [chrg_roll_nor.c:439](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_roll_nor.c:439)
|
||||
|
||||
目前没有发现确定的反向加锁环,但一旦锁所有者因堆破坏、状态机卡死或设备写阻塞而不再释放,其他线程会永久挂起,没有超时和故障上报。
|
||||
|
||||
8. **中风险:串口 RingBuffer 写入结果被忽略**
|
||||
|
||||
[chrg_north.c:623](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_north.c:623) 和 [chrg_south.c:383](D:/proj/char/chrg_v/apps/chrg/applications/thread/chrg_south.c:383) 没有检查 `rt_ringbuffer_put()` 实际写入长度。
|
||||
|
||||
缓冲区满时会静默丢字节,随后触发分包超时、泄漏和上述越界路径,是长期运行后出现问题的重要放大器。
|
||||
|
||||
**优先判断**
|
||||
|
||||
最可能的两条故障链是:
|
||||
|
||||
1. 串口分包 → 长度计算错误/超时泄漏 → 堆损坏或耗尽 → 某些业务线程永久等待;主线程继续喂狗。
|
||||
2. 南向某条命令超时或响应错位 → `RUN_COM` 无限重发 → 南向业务永久停止;主线程和北向线程继续喂狗。
|
||||
|
||||
现场故障尚能进入 Shell 时,优先保存 `list_thread`、`list_mutex`、`list_mailbox`、`free` 输出,并检查是否持续打印 `com batch timeout, resend current reg`。这几项可以直接区分锁等待、堆耗尽和状态机无限重试。
|
||||
|
||||
未修改任何文件。
|
||||
Reference in New Issue
Block a user