diff --git a/docs/README.md b/docs/README.md index 28541d50..4c53bb44 100644 --- a/docs/README.md +++ b/docs/README.md @@ -176,6 +176,7 @@ docs/ |---|---| | [`项目审查与优化建议报告.md`](审查记录/项目审查与优化建议报告.md) | 项目全景审查:文档/代码/架构/可靠性问题清单(D-1~D-7、N-xx、Px-xx) | | [`项目审计改进清单.md`](审查记录/项目审计改进清单.md) | 审计待办清单(P0~P3,带状态与日期) | +| [`下一步任务规划.md`](审查记录/下一步任务规划.md) | **项目状态盘点 + 下一步任务排序(阶段 A/B/C/D)**——每轮迭代后更新盘点 | ### 8.2 环境与部署 diff --git a/docs/审查记录/下一步任务规划.md b/docs/审查记录/下一步任务规划.md new file mode 100644 index 00000000..1f0a25fa --- /dev/null +++ b/docs/审查记录/下一步任务规划.md @@ -0,0 +1,135 @@ +# 下一步任务规划(2026-09-26) + +> **用途**:在 L3 pending 一期闭环、文档体系重组、App/test 目录重整之后,对**当前项目状态** +> 做一次全量盘点,并给出**明确、可执行、有优先级**的下一步任务清单。 +> **上游**:`docs/审查记录/项目审计改进清单.md`(审计待办全量)+ `docs/问题记录/错误码传播审计.md` +> (25 项未闭环发现)+ `docs/待处理内容.md`(登记簿,5 项)。 +> **本文件不重复登记明细**——每项只给「做什么 / 为什么现在 / 入口在哪 / 完成判据」; +> 细则一律指向上游文档对应条目,避免三处维护。 + +--- + +## 1. 当前状态盘点(2026-09-26) + +### 1.1 已闭环(近三周) + +| 领域 | 成果 | +|---|---| +| 网络阻塞模型 | **L3 pending 一期(RECV)**:netTask 零占用、分片协议退役、队列响应性 28554ms→92.5ms(`a45c0cb`+`7ff2958`,已入主线并推送) | +| 网络稳定性 | Trap 21(TC-NET-604 判定非缺陷)/ Trap 22(快速复连监听静默死亡)/ Trap 23(中止后首笔静默截断)均闭环 | +| 错误码 | E-03 `s_net_errno` 跨任务竞态 → 按任务隔离槽位(`1b46e2a`) | +| 内存 | 预算基线重建 v1.2:产品构建 RW_IRAM1 82,048/131,072 B(62.6%)、RW_CCM 75.0%;lftpd 大局部缓冲静态化后 ftpTask 栈峰值 1184B/4096(余量 71%) | +| 工程卫生 | 文档体系重组 + D-7 一致性门禁(3 个新钩子)、Git LFS 托管 jpg、图床图片迁回本地、自建目录 README 体系(26 份)、App/test 目录按职责重整 | + +### 1.2 未闭环(按严重度) + +| 级别 | 数量 | 关键项 | +|---|---|---| +| **P0** | 0(1 项待真机验证) | P0-1 cfg_file 堆分配改造——**真机 `@test` 尚未执行**(模拟器宿主堆照不出) | +| **P1** | 4 | **P1-1 ADC 采样路径产品构建中死亡**、P1-7 MAC 写入顺序、**P1-8 网络重建失败无恢复**、P1-10 测试固件构建闭环 | +| **P2** | 13 | E-04 `remo_send` 丢返回值、E-34 CH395F `HAL_MAX_DELAY`、GD5F 错误通道族(E-10/42/53)、P2-1~P2-15 | +| **P3** | 11 | 卫生项,可顺手处理 | +| 登记簿 | 5 | N-2(49s 日志静默)、N-3(L5 基线重验)、N-4(pending 二期)、S-1(defaultTask 栈贴线)、S-2(boot_log 存档流程) | + +### 1.3 三个结构性判断 + +1. **网络层已是全项目最成熟子系统**(pending 机制 + 四份 Trap 记录 + 量化基线 + 探针族)。 + 剩余网络工作都是"二期扩展/收口",**没有紧急项**。 +2. **真正的产品功能缺口在 ADC 与 RS-485 采集链路**:`adc_task`/`rs485_task` 均为空桩, + DTU 的核心职责(数据采集与转发)**尚未实现**。这既是 P1-1 的根源,也是 E-59 那条 + "休眠缺陷闸门"的另一面——不是缺陷难修,而是**功能没接**。 +3. **验证债重于代码债**:多个已改但未真机验证的项(P0-1)+ 测试固件构建未实测(P1-10) + + 栈/基线未重锁(N-3、S-1)。这些不修不会立刻出事,但会让"0 错误 0 警告"的含金量下降。 + +--- + +## 2. 下一步任务规划 + +排序原则:**先降风险(产品级故障)→ 再补功能(核心链路)→ 再清结构债 → 最后卫生**。 + +### 阶段 A:立即执行(改动小、风险高、当周可完) + +> 目标:清掉"会静默坏事"的产品级风险 + 还清验证债。全部是一档性质,无架构变更。 + +| 序 | 任务 | 为什么现在 | 入口 | 完成判据 | +|---|---|---|---|---| +| **A1** | **P0-1 真机 `@test` 验证**(cfg_file 堆改造) | 模拟器照不出该缺陷,这是 P0 修复的最后一道门;不做等于 P0 只是"理论已修" | `& '.venv\Scripts\python.exe' test/host/run_boot_test.py` | 日志出现 `load: OK`,Forbidden `FAIL` 零命中 | +| **A2** | **P1-8 网络重建失败恢复** | 一次 PHY 抖动 → 网络永久不可用且**零日志**,现场无法定位;产品不可接受 | `App/task/net_task.c` reconfig 路径 | 失败写 syslog 自检 + 周期重试(30s)+ 不自驻留;`@test` 通过 | +| **A3** | **E-34 CH395F 字节命令超时** | 3 处 `HAL_MAX_DELAY`(`ch395f.c:181/195/210`)= netTask 可被 SPI 卡死;**一行级改动** | 同上 | 有限超时(100~500ms)+ 返回值检查;构建 0/0 | +| **A4** | **E-04 `remo_send` 返回值** | 短写静默截断 → 客户端 CRC 失败 → 画面卡死黑屏;**设备侧日志全正常**(最难定位类) | `Drivers/BSP/UI/ui_net_sink.c:103` | 改返回 `int` 并校验 `ret == len`;短写 `DBG_ERROR` + 连续失败强制重连;UI 推流压力回归 | +| **A5** | **P1-7 MAC 写入顺序** | 先确认 CH395F 手册 §9.2.1(INIT 是否锁存 MAC)再改;若锁存则 UI 配置 MAC 不生效、设备在线身份错误 | `Drivers/BSP/NET/net_socket.c` `net_init()` | 依手册结论调整顺序或文档化"无需调整" | + +**阶段 A 出口**:`@build` 0/0 + 一轮 `@test` 闭环 + ST-LINK 全片回读;登记簿/清单同步销项。 + +### 阶段 B:核心功能补齐(产品价值最高,需产品定义确认) + +> 目标:让 DTU 真正采到数——这是当前**最大的功能空洞**。 + +| 序 | 任务 | 前置 | 说明 | +|---|---|---|---| +| **B0** | **产品定义确认**(阻塞项) | — | 需甲方明确:① 采集是否持续 ② 采样率/通道 ③ 转发目标(MODBUS RTU?上行 TCP?)④ `APP_ADC_ENABLE`/`APP_RS485_ENABLE` 语义。**B1/B2 的规模完全取决于此** | +| **B1** | **ADC 采集链路实现**(消化 P1-1、E-58) | B0 + **E-59 门禁** | 周期 CONVST 重触发 + `adc_task` 消费 `get_buf`;**先修 E-58**(原子快照 API,否则接入即撕裂);栈预算核算(两任务未入 `freertos.c` 栈表) | +| **B2** | **RS-485 采集链路实现**(消化 E-50、E-49) | B0 + **E-59 门禁** | 双缓冲 + 序号(防包撕裂)+ DE 收尾(超时路径等 TC)+ 协议状态机(`App/protocol/` 正是为此预留) | +| **B3** | **E-59 门禁固化入 `AGENTS.md`** | — | 把"接消费者前必须先修 E-36/E-44/E-45/E-58 + 栈预算"写成强制门禁,与"改栈前跑 `stack_report.py`"同级。**这是防止 B1/B2 带缺陷上线的关键** | + +**建议**:B3 与 B0 并行推进(前者是纪律,后者问甲方);**E-58 修复应在 B1 动消费者之前完成**。 + +### 阶段 C:结构债清理(二期)+ 存储侧健壮性 + +| 序 | 任务 | 说明 | 出处 | +|---|---|---|---| +| **C1** | **pending 二期(ACCEPT + SEND)** | 沿一期机制扩展;同期消化 E-25(net_select 语义)、E-24/E-27/E-28(跨上下文收口);基线重锁 + 探针族扩充 + 看门狗接 selftest | 第二档 #2 / N-4 | +| **C2** | **GD5F 错误通道统一** | E-10/E-42/E-53/E-54/E-56/E-57 一次做齐:`wait_busy` 区分超时/真故障、`bbt_scan` 失败**放弃持久化**(保护好副本)、init 全链路检查 + 重试、回读 B0h 校验 ECC | 第二档 #1 | +| **C3** | **存储侧日志管线**(消化 N-2) | 49s 全系统日志静默的复现与定位(NAND GC 长尾 vs UART 卡滞)+ 队列满丢弃计数/告警(现静默丢行);与 S8(非阻塞日志)合并评估 | N-2 / S-1 | +| **C4** | **defaultTask 栈余量**(S-1) | 2872B/4096(余量 29.9%,差 4.8B 未达 30% 判据)→ 栈 4096→4608(CCM 堆余量可覆盖)或 GC 链局部削减 | S-1 | +| **C5** | 一档剩余 + P2 收口 | E-49(RS485 DE 收尾)、E-15(ringbuf 定去留——**已确认零调用者,倾向删除**)、E-61(SD2506 注释订正)、E-16(cfg_file 段名长度)、E-26(UDP 定案,倾向移除)、E-23、E-30 | 一档/二档 | + +### 阶段 D:验证与卫生(常态化) + +| 序 | 任务 | 说明 | +|---|---|---| +| **D1** | **P1-10 测试固件构建闭环** | CH395F 套件 7 配置实际构建确认链接通过 + `@test`;**不建议**扩 scatter 到 192KB | +| **D2** | N-3 L5 基线重锁 | 下次 Phase 13 窗口顺带做(低风险:阈值余量 >8×) | +| **D3** | S-2 boot_log 快照存档流程 | 固化 `%TEMP%\opencode\boot_log_*.txt` 进 `docs/测试说明/` 存档目录 | +| **D4** | P3 卫生项 | P3-1~8/11/12/13(含 E-13 `test_parse_ip` 门控析取、P3-3 死代码 `app_main_run`)顺手清理 | + +--- + +## 3. 建议执行顺序(一图) + +``` +【阶段 A 立即】 A1 验证债 ─┬─ A3 E-34(一行级) + ├─ A4 E-04(UI 推流风险) + ├─ A2 P1-8(网络不可恢复) + └─ A5 P1-7(待手册确认) + ↓ +【阶段 B 功能】 B0 产品定义确认 ─┬─ B3 E-59 门禁固化(并行) + └─ E-58 原子快照 → B1 ADC → B2 RS485 + ↓ +【阶段 C 结构】 C1 pending 二期 ─ C2 GD5F 错误通道 ─ C3 日志管线 ─ C4 栈 ─ C5 收口 + ↓ +【阶段 D 常态】 D1 测试固件闭环 / D2 N-3 / D3 boot_log / D4 P3 +``` + +--- + +## 4. 诊断基线(供后续对照) + +| 项 | 当前值 | 出处 | +|---|---|---| +| 产品构建 RW_IRAM1 | 82,048 / 131,072 B(62.6%) | `RAM使用分析与优化方案.md` v1.2 | +| 产品构建 RW_CCM | 49,184 / 65,536 B(75.0%) | 同上 | +| 实测堆 | free=8,784 / min_ever=4,848 B | 同上 | +| ftpTask 栈峰值 | 1184B / 4096(余量 71%) | `stack_report.py` | +| defaultTask 静态最坏链 | 2872B / 4096(余量 29.9%,**贴线**) | 同上 | +| 队列响应性(挂起客户端) | 92.5ms(pending 一期) | `e02_probe.py` | +| M3 压力档帧周期 | med 60.1 / p95 66.4 / max 69.3ms,零尖峰 | `链路延迟测试说明.md` | + +--- + +## 5. 维护约定 + +- 本文件**不是登记簿**——它是"盘点 + 排序",明细以 `项目审计改进清单.md` / `错误码传播审计.md` / + `待处理内容.md` 为权威。**任一项完成后,先销上游登记,再更新本文件的盘点表**。 +- 阶段 A 完成后重跑本盘点;阶段 B 完成后重写 §1.2(届时 ADC/RS485 应从未闭环消失)。 +- 新增发现按既有惯例:能立即修的进阶段 A/C5;需排期的进上游三文档;跨版本观察的进 `待处理内容.md`。 diff --git a/docs/审查记录/项目审计改进清单.md b/docs/审查记录/项目审计改进清单.md index 39fca22f..904c5dc3 100644 --- a/docs/审查记录/项目审计改进清单.md +++ b/docs/审查记录/项目审计改进清单.md @@ -6,6 +6,7 @@ - **严重度**:P0 = 确认功能 bug | P1 = 疑似 bug 或健壮性风险 | P2 = 改进 | P3 = 卫生 - **置信度**:high = 代码级确证 | medium = 依赖外部条件(手册/硬件/产品定义) - **删改**:2026-09-21 —— 已完成条目删除,仅保留待办;已修项见文末「已完成(勿重复报)」;各保留条目均于 2026-09-21 对照源码复核现态 +- **执行排序**:⚠️ 本清单是**全量池**,不表达优先级顺序;实际"先做哪个"见 [`下一步任务规划.md`](下一步任务规划.md)(阶段 A/B/C/D 排序 + 诊断基线) ## 结论总览 diff --git a/docs/待处理内容.md b/docs/待处理内容.md index e678f7aa..b70ed9e2 100644 --- a/docs/待处理内容.md +++ b/docs/待处理内容.md @@ -24,4 +24,4 @@ | # | 事项 | 说明 | 出处 | |---|---|---|---| -| N-4 | **pending 机制二期:ACCEPT + SEND 阻塞路径 pending 化**——E-06(accept 轮询计时失真)、E-20(lftpd 控制通道 errno 分类/501)、E-21(timeSync errno 分级)剩余分量共用该机制扩展;届时 L3/L5 基线再重锁、探针族扩充(close/reconfig/EOF/disconnect-during-pending)、看门狗接 selftest 留痕、模拟器差距清单 +1 | 一期(RECV)已落地:`f16dc9a`;设计基线与门禁结果见 `docs/问题记录/Net层阻塞模型业界对照与演进方案.md` §6.7 | 同左 + `错误码传播审计.md` 第二档 #1 | +| N-4 | **pending 机制二期:ACCEPT + SEND 阻塞路径 pending 化**——E-06(accept 轮询计时失真)、E-20(lftpd 控制通道 errno 分类/501)、E-21(timeSync errno 分级)剩余分量共用该机制扩展;届时 L3/L5 基线再重锁、探针族扩充(close/reconfig/EOF/disconnect-during-pending)、看门狗接 selftest 留痕、模拟器差距清单 +1 | 一期(RECV)已落地并入主线:`a45c0cb`(+文档闭环 `7ff2958`);设计基线与门禁结果见 `docs/问题记录/Net层阻塞模型业界对照与演进方案.md` §6.7 | 同左 + `错误码传播审计.md` 第二档 #2 | diff --git a/docs/测试说明/链路延迟测试说明.md b/docs/测试说明/链路延迟测试说明.md index 80db3f4b..7ce9b4fa 100644 --- a/docs/测试说明/链路延迟测试说明.md +++ b/docs/测试说明/链路延迟测试说明.md @@ -611,7 +611,7 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复 147ms→89ms(芯片沉降约 60ms); 2. **(历史,阶段一)E-02 分片长度 = 其他任务的最坏等待**:100ms 片下受压帧仍 ~750ms(多片排队),收到 **5ms** 片后才降到 ~75ms——该"分片即上限"模型已于 2026-09-22 由 **L3 pending 化**取代 - (f16dc9a):执行器零占用后,其他任务的最坏等待 = 一个主循环节拍(~1ms),与任何片长无关 + (`a45c0cb`):执行器零占用后,其他任务的最坏等待 = 一个主循环节拍(~1ms),与任何片长无关 (M3 压力档实测:med 60.1ms/max 69.3ms,p99 尾部较分片档 133.5ms 压缩近半); 3. **(历史,阶段一)分片循环必须显式让出 CPU**:某片可能因 `net_poll` 自身耗时首轮即返回超时 → 本任务 (Normal)立即重排队会形成忙循环并**饿死 BelowNormal 的 uiTask**(实测 2.4~3.4s 周期尖刺); @@ -683,7 +683,7 @@ sock4 auto-relisten ok / relisten retry ok ← 监听器随后自行恢复 |---|---|---|---| | D1 | CH395F TCP 重传固定周期(非 RTT 自适应) | 台架链路(有线 100M 全双工)多轮零重传尖峰 → 台架无丢包;**弱网(Wi-Fi/双工不匹配)仍是秒级卡顿首要嫌疑**,200ms 粒度台阶 = 重传指纹(旧固件 500ms) | **已修(P0-a,2026-09-22)**:`NET_RETRANS_PERIOD_MS` 500→**200**(窗口 8×200=1.6s,原 4s)。台架验收说明:`GET_CMD_STATUS` 对 SET 类命令无判别力(500/2000ms 超限对照均读 0x10 陈旧残留,§9.2)→ 接受性以手册范围(≤1000ms)+ 全套件零回归(§6.2/§6.3)+ 与 500ms 行为同构为准。验收探针(3×20ms 状态轮询 + 证据行)验收完成后已从代码移除,结论保留于本文档 | | D2 | 3209B × 1KB 发送 FIFO 停止等待(4 次串行分块握手,期间 netTask 全程占用) | `chunk_wait` 219µs/块 ≈ SPI 搬运成本的 1/7 → 握手本身**不是瓶颈**;每帧固定成本小(MCU 净 7.1ms) | 基线成本,可接受;根治 = 差量/RLE + 帧版本号(待办 S2) | -| D3 | netMsgQueue 头阻塞(= §8.1 E-02) | 已量化:一条空闲 FTP 控制连接冻结全网 17.7s | **两阶段闭环**:① 5ms 分片有界读 + 让出(f8d6e90/4a1605b,缓解)② **L3 pending 化**(f16dc9a,结构性——执行器零占用,缺陷类消灭,§8.3 要点 4) | +| D3 | netMsgQueue 头阻塞(= §8.1 E-02) | 已量化:一条空闲 FTP 控制连接冻结全网 17.7s | **两阶段闭环**:① 5ms 分片有界读 + 让出(f8d6e90/4a1605b,缓解)② **L3 pending 化**(`a45c0cb`,结构性——执行器零占用,缺陷类消灭,§8.3 要点 4) | | D4 | PC 端延迟 ACK 与"每段等 ACK"停止等待交互 | `--ack-fast` 带内实验证实:关掉延迟 ACK 尾批自行消失(52→3.9ms) | **已客户端侧解决**(生产客户端关延迟 ACK/轻推兜底,§7.4) | | D5 | uiTask 节拍:10ms 循环 + 每拍一次 64B recv + step 在 poll 前 | `ui_loop` 10986µs ≈ 设计值(渲染 ~1ms);拾取调度 ~2.3ms(P0-b 删 per-recv 日志后,原 ~6.8ms) | 基线行为;可选优化 S7(poll 前置 + while 排空),低优先 | | D6 | 阻塞式日志在关键路径(fputc 逐字节 @115200) | `logtax_line` 4402µs/行;per-recv 日志已删(2026-09-22):`queue_rt_recv` 5997→1223µs、帧周期 66.0→61.6ms、首字节 13.9→9.6ms | **部分已修(P0-b,2026-09-22)**;S8 非阻塞日志仍待办(同时是开机日志并发丢行 §9.2 的根治项) | diff --git a/docs/问题记录/Net层阻塞模型业界对照与演进方案.md b/docs/问题记录/Net层阻塞模型业界对照与演进方案.md index f4b95e6e..21456766 100644 --- a/docs/问题记录/Net层阻塞模型业界对照与演进方案.md +++ b/docs/问题记录/Net层阻塞模型业界对照与演进方案.md @@ -1,14 +1,12 @@ # Net 层阻塞模型:业界对照与演进方案 > **模块名称**:Net 层(net_socket / netTask)阻塞等待模型 -> **文档目的**:E-02 修复(2026-09-22)的配套调研留档——(1) 本项目"分片有界等待"方案与主流开源/ -> 成熟项目模式的对照;(2) 演进方向"RECV 消息 → pending 请求"(正统事件驱动形态)的利弊与改动量估算。 -> **留作备用**:第二档 #1(E-02+E-06+E-20+E-21 阻塞模型改造)排期实施时,以 §6 为设计基线、 -> §6.5 为验证门禁。 -> **实施状态(2026-09-22 当日更新)**:**一期(RECV pending 化)已实施并全量台架复验**—— -> commit `f16dc9a`(分支 `wip/v2-pending-recv`,基线 v2-on-ui `2438de8`);实现与 §6.1 基线的 -> 偏差、§6.5 门禁逐条结果与实测数据见 **§6.7 实施记录**;二期(ACCEPT/SEND)未动,仍按 §7 排期。 -> **日期**:2026-09-22 +> **文档目的**:E-02 修复(2026-09-22)的配套调研留档——本项目已落地的"pending 请求"形态 +> 与主流开源/成熟项目模式的对照,及二期(ACCEPT/SEND)的设计基线。 +> **当前状态(2026-09-26 复核)**:**一期(RECV pending 化)已实施、已并入主线 `v2-on-ui`、 +> 已推送远程**——落地提交 `a45c0cb`(实现)+ `7ff2958`(文档闭环)。实现与设计基线的偏差、 +> 门禁逐条结果与实测数据见 **§6.7**。二期(ACCEPT/SEND)未动,设计基线见 §6.1,按 §7 排期。 +> **日期**:2026-09-22(2026-09-26 复核修订) > **配套文档**:`错误码传播审计.md`(E-02 明细 / 修复计划 / 已闭环记录)、`docs/测试说明/链路延迟测试说明.md` > (M3 基线 / §8.3 实现要点 / §9 故障对照)、`AGENTS.md`(SPI 铁律 / 就绪屏障 / 内存预算) @@ -16,21 +14,20 @@ ## 1. 结论速览 -1. **已落地的修复(分片有界等待)在本项目约束类里是标准做法,且有官方认可**——不是野路子, - 无需推翻;已经台架 A/B 实证(挂起对时客户端下 FTP banner 28554ms → 109ms/75ms)与 - M3 压力档复验(帧周期 17.7s 尖峰 → 零尖峰,med 65.0ms)。 +1. **已落地的修复(pending 请求模型)在本项目约束类里是标准做法,且有官方认可**——不是野路子, + 无需推翻;已经台架 A/B 实证(挂起对时客户端下 FTP banner 28554ms → 92.5ms)与 + M3 压力档复验(p99 尾部 133.5ms → 69.3ms,零尖峰)。 2. **业界最正统形态("执行者永不被占")在本项目不可达**——其前提是"数据到达可被执行者免费 观察到"(LwIP:网卡 DMA/IRQ 递 pbuf → tcpip 线程 O(1) signal 消费者)。本项目的"数据到达" 观察手段是一次 SPI 事务,而 SPI 总线由 netTask 独占(铁律)→ 等待只能在 netTask 内执行, - 自由度只剩"每次占多久"与"等待期间还做什么"。 + 自由度只剩"每次占多久"与"等待期间还做什么"。pending 方案 = 把第二个自由度用满 + (占用降到主循环节拍级)。 3. **演进方向 = pending 请求**(控制块登记 + 主循环节拍完成 + 取消必通知):消灭头阻塞缺陷类 (不再依赖调用方纪律)、调用方从分片协议解放、成为 E-06/E-20/E-21 的公共地基。库内已有 生产验证先例——D10 的 `net_relisten_retry_tick`(登记→到点完成→全程不阻塞,恢复 588ms→89ms) 就是该模式的缩小版。 -4. **这是结构演进,不是紧急修复**:现状(分片)已验证、残余最坏可控(队列滞后 ≤5ms、发送 - stall ≤1s/分片,M3 基线已按此锁定)。实施时机 = 第二档 #1 排期窗口;路线 RECV 先行 → - ACCEPT/SEND 跟进。代码改动不大(一期 ~200-400 行、公开 API 不变),**大的是验证**(台架 - 天量级)。 +4. **一期已闭环,二期(ACCEPT/SEND)待排期**:残余分量 = 发送 stall、accept 轮询计时失真、 + errno 分级。代码改动不大(再 ~300-500 行、公开 API 不变),**大的是验证**(台架天量级)。 --- @@ -41,18 +38,22 @@ `net_poll`+`ch395f_get_recv_len`)期间,队列整体停摆最长 30s——FTP 客户端空闲 >30s、对时客户端 连上不发数据,均为常见触发工况。09-21 事故(errno 覆盖误断连)的姊妹缺陷。 -**已落地修复**(2026-09-22,两提交 + 闭环留档): +**已落地修复**(2026-09-22 一期;2026-09-26 复核更正提交号): | 提交 | 内容 | |---|---| -| `f8d6e90` | lftpd 侧:新增 `net_recv_to` 接口(片长经 `msg.timeout_ms` 入队、片超时独立返回码 -2);`lftpd_inet_read_line`/`lftpd_inet_read` 改 5ms 分片 + 30s 总预算 + 片间 `vTaskDelay(1)` 让出;M3 压力档复验(17.7s 尖峰 → med 65.0ms/max 133.5ms/零尖峰) | -| `4a1605b` | timeSync 侧:`time_sync_handle_conn` 同模式改造(5ms 片/30s 总预算/跨片累积容忍 TCP 分段/超时留痕 `time_sync: no TIME packet in 30s, drop conn`);产品构建 `@test` 闭环 PASS | -| `3b28c56` | 审计闭环留档 + 专项回归探针 `test/host/e02_probe.py` | +| `954a145` | 本调研文档留档(五类业界模式对照 + pending 演进方案) | +| `a45c0cb` | **一期实现**:阻塞 `recv` 改"挂起请求 + 主循环节拍完成",netTask 零占用;lftpd/timeSync 分片协议退役 | +| `7ff2958` | 文档闭环:E-02 审计两阶段闭环 + 本文 §6.7 实施记录 + M3 基线重锁(pending 构建)+ `待处理内容.md` 建立 | +| `93bae35` | 台架旁路:TC-NET-604 根因闭环(Trap 21,判定非缺陷),MSS=512 方案证伪回退 | + +> **注**:早期版本本文档把一期提交记为 `f16dc9a` / 分支 `wip/v2-pending-recv`——该分支与哈希 +> **在当前仓库不存在**(应为当时临时工作区,后续以 `a45c0cb` 正式落入 `v2-on-ui`)。以本表为准。 **A/B 台架实证**(`test/host/e02_probe.py`,2026-09-22,设备 192.168.1.100):挂起对时客户端连 :8888 不发数据,2s 后 PC 测 FTP(:21) banner 时延 = 队列响应性——旧固件 **28554ms**(设备日志实证芯片侧 -握手与 MCU 侧处理相隔 27s;旧代码 30s 后静默拆连零日志)vs 修复后 **109ms**(产品构建)/ **75ms** -(测试构建)。30s 预算拆连与监听恢复行为两侧同构(~8s,2 次 Trap 09 重试)。 +握手与 MCU 侧处理相隔 27s;旧代码 30s 后静默拆连零日志)vs 一期修复后 **92.5ms**。30s 截止按主循环 +节拍精确触发。监听恢复 6.6s(2 次 Trap 09 重试,既有行为同构)。 **当前架构**(netTask 主循环,`App/task/net_task.c`): @@ -60,8 +61,9 @@ for (;;) { [reconfig 检查 → net_stack_bring_up 重建] net_poll(); // 读芯片中断状态、分发事件、维护 readable 标志 - net_process_messages(); // ① net_relisten_retry_tick(D10:到点重试) - // ② while(队列非空) 取一条消息处理到完成 → 通知调用方 + net_process_messages(); // ① net_pending_tick(一期:按节拍了结挂起 recv) + // ② net_relisten_retry_tick(D10:到点重试) + // ③ while(队列非空) 取一条消息;无数据阻塞 recv 则登记 pending 即返回 osDelay(1); // 主循环节拍 ≈1ms } ``` @@ -122,36 +124,54 @@ B 类(LwIP netconn)的三个构件在本项目的对应物: **不得**在 ISR 或其他地方单独处理"——纪律上禁止。 2. **ISR 里读 per-socket 状态**:需要 SPI 事务,CH395F 驱动非 ISR 安全(设计即"中断集中读取")。 -**结论**:本项目的等待只能在 netTask 内执行,设计自由度 = ①每次占用时长(现状:分片封顶 5ms) -②等待期间是否/如何做其他事(现状:仅片间隙排空队列)。pending 方案 = 把自由度 ② 用满。 +**结论**:本项目的等待只能在 netTask 内执行,设计自由度 = ①每次占用时长 ②等待期间是否/如何 +做其他事。**pending 方案 = 把两个自由度同时用满**:占用降到"登记一次 + 每轮 tick 一次读标志" +(主循环节拍级),等待期间 netTask 正常排空整个队列。 --- -## 5. 现状方案(分片有界等待)的正当性 +## 5. 方案的正当性(两代形态) + +本项目的等待模型经历两代,二者都属 E 类"有界执行者等待"的合法变体: + +| 代次 | 形态 | 状态 | +|---|---|---| +| 一代(分片有界等待) | `net_recv_to` + 5ms 片 + 30s 总预算 + 片间 `vTaskDelay(1)` 让出;调用方持有预算循环 | **已退役**(一期被 pending 取代) | +| 二代(pending 请求,现行) | 无数据即登记 pending、netTask 零占用返回;`net_pending_tick` 按主循环节拍(~1ms)了结 | **一线现行**(`a45c0cb`) | + +**一代的正当性**(留档,说明当时的取舍不是野路子): 1. **官方认可**:Node 文档把"分片 + 定期让出"(partitioning)列为不阻塞事件循环的**一等官方解法** - (与 offload 并列;offload 在本项目不可行——SPI 独占)。5ms 片 + `vTaskDelay(1)` 让出即逐字 - 对应。 + (与 offload 并列;offload 在本项目不可行——SPI 独占)。5ms 片 + `vTaskDelay(1)` 让出即逐字对应。 2. **生产级先例**:librdkafka 后台 I/O 线程 = 短超时 poll + 间隙排空队列,"有界执行者等待" 在生产级客户端里被接受。 -3. **E 类标准实践**:offload 芯片驱动族的通行形态 = 非阻塞原语 + 调用方预算循环 - (`net_recv_to` + 5ms 片 + 30s 总预算与之同构)。 -4. **变体选择有据**:审计方向 ① 原文为 "DONTWAIT 分片 + 调用方重投",实际落地选**有界阻塞片**。 +3. **变体选择有据**:审计方向 ① 原文为 "DONTWAIT 分片 + 调用方重投",实际落地选**有界阻塞片**。 理由:DONTWAIT 每片一次完整队列往返(入队+调度+取消息+通知,L5 实测 ~1.2ms),30s 等待 = ~2.5 万次往返;有界片让 netTask 片内连续多次 `net_poll`,片内到达的数据当片即收。M3 实测 支持:100ms 片受压帧 ~750ms → 5ms 片 ~10ms。 -5. **已知残余**(如实留档): - - 队列最坏滞后一个片(5ms)+ 往返——pending 方案可压到 ~1ms(主循环节拍); - - SEND 的 1s/分片 stall 仍冻结队列(审计 E-02 位置明列)——pending 方案二期覆盖; - - **依赖调用方纪律**:新消费者若随手调 `net_recv(fd, buf, len, 0)`(默认 30s、无分片)即复发 - 头阻塞——pending 方案结构性免疫。 + +**二代相对一代的消除项**(为什么一期要推进): + +- 队列最坏滞后从"一个片(5ms)+ 往返"压到 **~1ms**(主循环节拍); +- **不再依赖调用方纪律**:新消费者写裸 `net_recv(fd, buf, len, 0)`(默认 30s、无分片)也**不会** + 复发头阻塞——一代的核心残留缺陷,二代结构性免疫; +- 调用方分片循环 + `vTaskDelay(1)` 让出协议(lftpd/timeSync 各 ~30 行)删除。 + +**仍存的残余**(转二期): + +- **SEND 的 1s/分片 stall 仍冻结队列**(审计 E-02 位置明列)——二期 pending 覆盖; +- 超时精度退化到主循环节拍(被长操作占用时顺延)——现状反向打平,需文档化。 --- ## 6. 演进方案:RECV 消息 → pending 请求 +> **一期(RECV)已实施**(`a45c0cb`),实现实测与基线的偏差见 §6.7;下文 §6.1 保留为设计基线 +> 原文(含二期扩展),§6.2~§6.6 为当初的利弊/改动量/门禁评估,§6.7 为落地实录。 + ### 6.1 机制 + - `net_sock_t` 增加 pending 字段:`buf 指针 / len / flags / deadline(tick) / caller 句柄 / active` (≈20B/fd × 8 fd ≈ 160B 静态增长); - `net_process_messages` 的 RECV 分支:数据已在 → 立即读出(**现快速路径不变**);数据未至且 @@ -228,7 +248,7 @@ B 类(LwIP netconn)的三个构件在本项目的对应物: - pending 最大龄自检(selftest 留痕,防"永不完成"); - 排障文档同步(`错误码传播审计.md` / `链路延迟测试说明.md` §9 故障对照表)。 -### 6.7 实施记录(一期,2026-09-22,commit f16dc9a) +### 6.7 实施记录(一期,2026-09-22,commit `a45c0cb`) **实现与 §6.1 基线的一致性/偏差**(diff 已逐行评审): @@ -267,19 +287,30 @@ B 类(LwIP netconn)的三个构件在本项目的对应物: ## 7. 结论与实施建议 -1. **一期(RECV pending 化)已于 2026-09-22 实施并全量台架复验**(f16dc9a,`wip/v2-pending-recv`; - 门禁逐条结果见 §6.7):头阻塞缺陷类结构性消灭、调用方分片协议退役、M3 压力档 p99 尾部 - 133.5ms→69.3ms、队列响应性 92.5ms、RAM +200B。**二期(ACCEPT/SEND)待排期**—— - E-06/E-20/E-21 剩余分量(SEND 1s/分片 stall、accept 轮询计时失真、errno 分级)共用同一 - pending 机制扩展;届时 L3/L5 基线再重锁。 -2. **实施时机 = 第二档 #1 排期窗口**:E-06/E-20/E-21 迟早要做,pending 是四条的公共地基, - 一次做齐比四个独立补丁便宜。 -3. **路线**:一期 RECV(影子验证)→ 二期 ACCEPT → SEND(最复杂:多分片/短发送/L3 探针钩子)。 -4. **启动动作**:以 §6.1 为设计基线评审 → 按 §6.5 门禁执行 → 完成后按 E-01/E-31/E-32/E-02 - 的留档惯例更新 `错误码传播审计.md`(明细"已修复"+ 页脚闭环注记 + 提交记录)。 +1. **一期(RECV pending 化)已实施、已全量台架复验、已并入主线并推送**(`a45c0cb` + `7ff2958`, + `v2-on-ui`;门禁逐条结果见 §6.7):头阻塞缺陷类结构性消灭、调用方分片协议退役、M3 压力档 + p99 尾部 133.5ms→69.3ms、队列响应性 92.5ms、RAM +200B。**本项(E-02)至此闭环,无遗留动作。** +2. **二期(ACCEPT/SEND)待排期**——E-06/E-20/E-21 剩余分量(SEND 1s/分片 stall、accept 轮询 + 计时失真、errno 分级)共用同一 pending 机制扩展;届时 L3/L5 基线再重锁。 + **实施时机 = `项目审计改进清单.md` 第二档 #2**(与 GD5F 错误通道统一并列,见第 2 项)。 +3. **路线**:~~一期 RECV(影子验证)~~ ✅ 已完成 → 二期 ACCEPT → SEND(最复杂:多分片/短发送/ + L3 探针钩子)。 +4. **二期启动动作**:以 §6.1 为设计基线评审(含 §6.7 一期实测对基线的修正) → 按 §6.5 门禁执行 + → 按 E-01/E-31/E-32/E-02 的留档惯例更新 `错误码传播审计.md`(明细"已修复"+ 页脚闭环注记 + + 提交记录)+ 销 `docs/待处理内容.md` N-4。 **一句话**:好处 = 缺陷类消灭 + 调用方解放 + E-06/20/21 共用机制;坏处 = 执行器状态机复杂化 -+ 天量级台架验证;改动本身不大(一期数百行、API 不变),大的是验证。 ++ 天量级台架验证;改动本身不大(一期 ~200-400 行、API 不变),大的是验证。**一期已验证此判断 +成立**(RAM +200B / Flash +608B,验证成本远超代码量)。 + +--- + +## 9. 修订记录 + +| 日期 | 内容 | +|---|---| +| 2026-09-22 | 初版:五类业界模式对照 + pending 演进方案 + §6.7 一期实施记录 | +| 2026-09-26 | 复核修订:①"已落地修复"由一代分片改为二代 pending(§1/§2/§5),一代降级为留档;②更正一期提交号 `f16dc9a`/`wip/v2-pending-recv` → `a45c0cb`(实测该分支与哈希不存在,应为临时工作区);③§7 一期标闭环、二期实施时机锚定审计清单第二档 #2 | --- diff --git a/docs/问题记录/错误码传播审计.md b/docs/问题记录/错误码传播审计.md index 65e38f1a..367395fc 100644 --- a/docs/问题记录/错误码传播审计.md +++ b/docs/问题记录/错误码传播审计.md @@ -100,7 +100,7 @@ |---|---|---|---| | **E-01** | `App/cfg/cfg_file.c` | `cfg_file_poll` 的"超长=损坏"检查是**死代码**(读长被钉死在阈值)→ >4096B 配置被截断当有效解析 → 界外内容静默回工厂默认 → 基线粘住错误状态 → 下次开机 load 守卫判"损坏" → **用户配置文件整体销毁** | `d030cb0` + 关联 `70f1cc8`(`f_rename` 不覆盖既有目标) | | **E-31** | `nand_ftl.c` / `journal.c` | 扫描读失败被**覆盖为 `TOO_BAD`** → 非空白卷在 I/O 故障下被误判"全新"→ 直接 `clear` → 映射表整体丢失 | `da1a6e8` | -| **E-02** | `net_socket.c` / `lftpd` / `time_sync` | 一条空闲 FTP 控制连接把 netTask 独占 30s → **全体消费者队列停摆**(实测冻结远端显示 17.7s) | `f8d6e90`+`4a1605b`(分片)→ `f16dc9a`(L3 pending 化,结构性消灭) | +| **E-02** | `net_socket.c` / `lftpd` / `time_sync` | 一条空闲 FTP 控制连接把 netTask 独占 30s → **全体消费者队列停摆**(实测冻结远端显示 17.7s) | `f8d6e90`+`4a1605b`(分片)→ `a45c0cb`(L3 pending 化,结构性消灭) | ### 2.2 P1 十五项