docs(net): N34 阶段0/1 实施结果与台架留痕(E10)

- README:新增"实施状态"表(9ac9879/ae17d36/4c0a0af/b8c6a3d);§2 逐机制
  裁决表补"✅ 已实施"与关键坑位索引
- 实验方案:新增 E10 —— 阶段 0/1 转正验证全记录,含阶段 1 首版台架失败的
  根因链(armed 漏放行 ESTABLISHED → i104 整场饿死 18/18 FAIL → 修复后
  18/18 PASS、127s→8.3s)、M3 无辜旁证、time_sync 连续 6 轮实测、
  两处实施偏差(lftpd 未走 listener_try;阶段 0 提交合并)
- 实施方案:阶段 0/§2、阶段 1/§3 标注完成与验证判据;P0 转正标注
- 盘点:F-NET-2/3/4/13/14 补实施结论;⚠ F-NET-4 补"门控迁移必须给
  net_listener_armed 补 ESTABLISHED 放行"的坑位说明;net_listener_try
  调用点计数 5→4;§2/§4 的缺口表述改闭环状态
- 顺带修正两条既有 doc-links 违规(实验方案 §4.4 → 实施方案 §4 第 4 项;
  盘点 §5.29 → docs/芯片手册/CH395F.md §5.29),该 4 份文档 doc-links
  现 Passed

无代码改动(纯文档)。
This commit is contained in:
2026-10-13 06:46:40 +08:00
parent b8c6a3dab0
commit d5bdc6eb54
4 changed files with 120 additions and 17 deletions
+20 -6
View File
@@ -8,6 +8,20 @@
> "静默回收"非 Server 模式条款)。全部证据带 文件:行号,HEAD `09d6195` 实测。
> 性质:纯审计,未改任何代码。本报告是裁决与路线,不是实施记录。
## 实施状态(2026-10-13 更新)
| 提交 | 内容 | 结果 |
|---|---|---|
| `9ac9879` | P0 止血包(M1+M2+M4-lite) | 台架验证(E7b/E8) |
| `ae17d36` | M3 矛盾态可疑门(风暴态免疫,资产门版) | E9/E9b 全绿 |
| `4c0a0af` | **阶段 0** 零风险清理 9 条 | E10:0/20 + 20/20 + 20/20 + 11/11 |
| `b8c6a3d` | **阶段 1** API 面收口(对账⑤ + 删 3 API + time_sync/lftpd) | E10:全绿回归(详见实验方案 E10) |
阶段 0/1 已完成并转正;阶段 2/3(驱动原语 / net 层收敛)未开始。阶段 1 实施
过程中的一条关键台架发现已留痕(实验方案 E10):**`net_listener_armed` 必须
放行 ESTABLISHED**——单连接模型的"待 accept"电平,漏放行时 i104(200ms 周期)
抢不过对账② 的 ~400ms 去抖而整场饿死(18/18 FAIL → 修复后 18/18 PASS,127s→8.3s)。
## 0. 结论先行(直接回答本次审计的问题)
**问:「芯片/本层状态对账与重监听自愈」可以简化或删除吗?可以内聚到 ch395f 驱动层吗?**
@@ -50,19 +64,19 @@ T1(net 层视角)判"机制主体保留、API 可删";T3(主流对照视
|---|---|---|---|
| `auto_relisten` | net_types.h:172 | **保留**(模型基石) | 芯片永不自动回 LISTEN,监听常驻是软件承诺(P4) |
| D10 `net_listener_retry_tick` | net_listener.c:394-432 | **短期保留+小修;长期降级** | 小修=补 pump 同型状态门(F-NET-10)+ give-up 文案(F-NET-14);长期=驱动 `cmd_done()` 后内联进事务、删独立定时器(F-DRV-8) |
| Trap 22 `net_reconcile_tick` | net_socket.c:548-641 | **功能保留,形态重构** | 与 pump 判据合并为**单一 reconciler**、读驱动缓存、一套去抖(F-DRV-1);①扩展覆盖"本层 ESTABLISHED+芯片已回 LISTEN"缺口(F-NET-2,当前无自动机制认领) |
| Trap 22 `net_reconcile_tick` | net_socket.c:548-641 | **功能保留,形态重构**(阶段 1 已做①扩展=⑤) | 与 pump 判据合并为**单一 reconciler**、读驱动缓存、一套去抖(F-DRV-1);①扩展覆盖"本层 ESTABLISHED+芯片已回 LISTEN"缺口(F-NET-2)✅ 已实施(bad=5,`b8c6a3d`) |
| N-32 `net_listener_pump` | net_listener.c:465-553 | **保留**(内聚的落点,不是补丁) | 唯一周期重建决策者=不变量 II;T3"改内联"与 P2a 台架实测冲突(删消费者驱动→`[WD] task stall`) |
| Trap 23 排渣隔离 | net_listener.c:102-121/:149-204 | **下沉驱动** | 排空/等 CLOSED 是纯芯片机械;与 net_close_locked:2326-2345 是同一契约两套变体(F-NET-9/F-DRV-4)→ 收口 `ch395f_sock_reset(sn, mode, wait_ms)`,net 层只选 mode |
| `net_relisten` | net_socket.c:3080 | **删除** | 生产零调用;对健康监听器无条件 open→USE→close,自毁 ~1s(footgun) |
| `net_resync_listen` | net_socket.c:3026 | **删除**(先并入对账①) | 语义正确但载体错位:应是自动对账,不是手动 API(F-NET-2/F-MS-4) |
| `net_can_accept` | net_socket.c:1427 | **删除**(先迁 iec104 到 `net_listener_armed`) | 过期门控致 :2404 丢 CONNECT 探针从不运行(F-NET-4,与已修的 :7003 探针失效同型) |
| `net_relisten` | net_socket.c:3080 | **删除** ✅ 已实施(`b8c6a3d`) | 生产零调用;对健康监听器无条件 open→USE→close,自毁 ~1s(footgun) |
| `net_resync_listen` | net_socket.c:3026 | **删除**(先并入对账①)✅ 已实施(语义=对账⑤) | 语义正确但载体错位:应是自动对账,不是手动 API(F-NET-2/F-MS-4) |
| `net_can_accept` | net_socket.c:1427 | **删除**(先迁 iec104 到 `net_listener_armed`)✅ 已实施 | 过期门控致 :2404 丢 CONNECT 探针从不运行(F-NET-4,与已修的 :7003 探针失效同型)。⚠ 迁移时 **armed 必须补 ESTABLISHED 放行**,否则 i104 整场饿死(实验方案 E10) |
| `net_lost_connect_probe` | net_socket.c:1228-1267 | **保留**;驱动持事实后去可去抖 | 丢 CONNECT 是芯片行为(裸机实证);去抖/冷却是对账世界的自偿成本(F-MS-9) |
| L3 pending recv + 看门狗 | net_socket.c:2024-2190 | **保留** | 阻塞模型主体,与主流(FreeRTOS 事件组阻塞)同构 |
| `session_dead` / `released_abnormal` / 幽灵甄别 / closed-with-data | net_types.h | **保留** | 芯片行为的必要建模,P1 不变量 I |
| `net_listen_locked` 3×osDelay(500) / `net_close_locked` 500ms 忙等 | net_socket.c:1092-1104/:2326-2345 | **简化**(P2b) | 根在驱动阻塞原语(F-DRV-8);先补 `cmd_done()` 再统一走 D10 |
| 上层每 32 轮盲收(U15) | ui_net_sink.c:552-558、iec104_serv.c:246-253 | **中期下沉** | "RX 非空但 readable=0"并入 reconciler 后上层退役(F-NET-12) |
| time_sync 每会话 close 监听重建 | time_sync.c:182 | **简化(上层)** | 违反自家"EOF 不得 close 监听 fd"铁律,主动制造 Trap 09 沉降窗(F-NET-13);改 lftpd 模式同 fd 回 accept |
| lftpd 应用层阻塞重试 | lftpd_inet.c:39-70、lftpd.c:473-477 | **删除/简化** | Trap 09 启动形态已被就绪屏障消除,重试理由不存在(F-DRV-9) |
| time_sync 每会话 close 监听重建 | time_sync.c:182 | **简化(上层)** ✅ 已实施(`b8c6a3d`) | 违反自家"EOF 不得 close 监听 fd"铁律,主动制造 Trap 09 沉降窗(F-NET-13);改 lftpd 模式同 fd 回 accept。实测连续 6 轮推送正常(E10) |
| lftpd 应用层阻塞重试 | lftpd_inet.c:39-70、lftpd.c:473-477 | **删除/简化** ✅ 已实施 | Trap 09 启动形态已被就绪屏障消除,重试理由不存在(F-DRV-9)。实施偏差:未走 `net_listener_try` 登记(会把 BOUND/D10 状态语义一并拖入),改单次尝试+失败即 425(E10 登记) |
| 三处全局读清残留 | net_socket.c:1668/:2332、net_listener.c:176 | **收窄**(与驱动护栏合并做) | B3 已证"只读本 Socket SINT"可行;这三处仍在吞别的 Socket 边沿事件(F-NET-7) |
## 3. 目标架构(高内聚低耦合的落点)
@@ -47,7 +47,7 @@
| 中止时段的在途数据在 TIMEOUT **之后**才落进 RX 缓冲(~2KB 残渣被下条连接读走) | Trap 23 一阶段(RST 10/10 复现→修复 10/10) | QUARANTINE 400ms + 持续排空 |
| 上一条连接残留 DISCONNECT 会在复用同 fd 的下一条连接 accept 后 10~20ms 投递 | Trap 23 二阶段(0ms/30ms 沉降无效、100ms 通过) | GRACEFUL 100ms(仅收过数据时登记) |
| close 后重开 Socket 需沉降(PHY 形态 + 拆链形态) | Trap 09;Trap 25 台架(固定沉降 100~3000ms 均证伪"等就好") | D10 到点重试(而非固定延时) |
| 芯片可静默回收连接回监听且不发 DISCONNECT | run8 台架实证(当时 FUN_PARA=0x09,bit3 已=1,git 核实) | 现仅 `net_resync_listen` 覆盖——**自动机制存在覆盖缺口**(F-NET-2) |
| 芯片可静默回收连接回监听且不发 DISCONNECT | run8 台架实证(当时 FUN_PARA=0x09,bit3 已=1,git 核实) | **阶段 1 起由对账⑤自动认领**(原仅 `net_resync_listen` 覆盖 = F-NET-2 缺口,已闭环) |
| 全局中断寄存器整体读即清、无按位清除;SINT 粘滞到被读 | 手册 §5.15;Trap 14 | net_poll 集中读清纪律;也解释了为何"丢弃式读全局"有害(F-NET-7) |
**(b) 早期固件 bug(寄存器数值/引用/时序误用)——根因已修,评估补丁是否仍必要:**
@@ -66,16 +66,16 @@
| 缺口 | 现状 |
|---|---|
| 重建多所有者(曾 12 个) | P2a pump 收敛周期性路径;消费者驱动 3 处经台架定案保留;**显式 API 2 处(RELISTEN/RESYNC)与 net_can_accept 旧门控是残余冗余**(F-NET-2/3) |
| 重建多所有者(曾 12 个) | P2a pump 收敛周期性路径;消费者驱动 3 处经台架定案保留;显式 API 2 处(RELISTEN/RESYNC)与 net_can_accept 旧门控**已于阶段 1 删除**(F-NET-2/3 ✅) |
| EOF 边沿化(抢先 relisten 吞 EOF) | P1 `session_dead` 电平化已消解 |
| 上层直接读 net 层状态镜像 | `lftpd_inet_accept_timeout` 直读 `p_sock->state`(lftpd_inet.c:117)、`net_listener_armed` 用 `readable` 代理"RX 可能有残留"(net_socket.c:1480-1485)、`net_can_read` 自认"对账不含 RX 非空但 readable=0"(:1500-1501)迫使上层保留每 32 轮兜底 recv(ui_net_sink.c:552-558、iec104_serv.c:246-253)——**第三层兜底散在应用层**(F-NET-12) |
| 应用层仍是重建所有者 | time_sync 每会话 close 监听 fd 全量重建(time_sync.c:182,违反"EOF 不得 close 监听 fd"铁律,设计文档第 7.4 节已点名);iec104 `s_drop_conn` close 监听重建(协议违例场景,有意例外)(F-NET-13) |
| 应用层仍是重建所有者 | ~~time_sync 每会话 close 监听 fd 全量重建~~(**阶段 1 已修** `b8c6a3d`:同 fd 跨会话常驻);lftpd 应用层阻塞重试已删(阶段 1);iec104 `s_drop_conn` close 监听重建(协议违例场景,有意例外)(F-NET-13) |
| fd 别名(监听=连接) | 硬件上限(多连接模式不支持多监听,D1 出局),不可消解;只能靠不变量 I/II 约束 |
## 3. 编号发现
**F-NET-1 · 重建入口实测 5 处,与设计文档定案一致;"唯一重建入口"仅指周期性**
证据:`net_listener_try` 调用点共 5——net_listener.c:552(pump,唯一周期性)、net_socket.c:811(NET_MSG_RELISTEN 显式消息)、:1327(accept CLOSED 分支)、:1938(recv DONTWAIT EOF 分支)、:2162(pending_tick ③)。后三处为"消费者动作触发",P2a 台架实测删除会把 CLOSED 窗口拉长到 pump 周期+隔离期并触发 `[WD] task stall`(设计文档第 9 节 P2a 修正记录)。
证据:`net_listener_try` 调用点共 4(阶段 1 删 `NET_MSG_RELISTEN` 后)——net_listener.c:552(pump,唯一周期性)、net_socket.c:1327(accept CLOSED 分支)、:1938(recv DONTWAIT EOF 分支)、:2162(pending_tick ③)。后三处为"消费者动作触发",P2a 台架实测删除会把 CLOSED 窗口拉长到 pump 周期+隔离期并触发 `[WD] task stall`(设计文档第 9 节 P2a 修正记录)。
影响:这不是缺陷,是**已实测裁决的重叠**;但"唯一重建入口"的表述在 BSD 指南与注释中易被读成"仅 pump 一处",误导后续维护者删消费者驱动路径。
建议:保留现状;文档口径统一为"唯一**周期性**入口 + 三处消费者驱动"。
@@ -83,16 +83,20 @@
证据:对账①判据要求芯片 SOCKET_CLOSED 或 TCP_CLOSED(net_socket.c:588-589),芯片回 LISTEN(0x05/0x01)时不命中;对账②要求本层 LISTENING(:591);pump 只裁本层 LISTENING/CLOSED(net_listener.c:509-513);`handle_connect_event` 对本层 ESTABLISHED 的新 CONNECT 中断两个分支都不命中、静默丢弃(net_socket.c:3306 起)。该场景为芯片真实行为(run8 实证"芯片静默回收、监听器健康",git 核实当时 FUN_PARA=0x09 即 bit3=1)。现仅 `net_resync_listen`(net_socket.c:774-799)能修,而其生产调用者为零(仅 net_latency_test.c:783)。
影响:一旦生产出现"静默回收 + 无 DISCONNECT",端口将滞留假 ESTABLISHED,新连接被芯片接住但本层无感知,直到下一次应用 close 或网络重建。
建议:把该形态并入对账①——本层 ESTABLISHED 且芯片 TCP=LISTEN → 按 RESYNC 语义只改本层状态(不发芯片命令,纪律同 pump③ U11 与 :785-788 注释);并入后 `net_resync_listen` API 可删(见 F-NET-3)。
**✅ 已实施(阶段 1,`b8c6a3d`)**:对账判据新增 **bad=5**(本层 ESTABLISHED 且 `st[0]!=SOCKET_CLOSED && st[1]==TCP_LISTEN`)→ RESYNC 语义(state→LISTENING + `last_peer_port` 转登 + `lost_conn_seen` 清零 + `net_pending_cancel(CONNRESET)`),不发任何芯片命令;手动 API 同批删除。台架验证见实验方案 E10。
**F-NET-3 · `net_relisten` / `net_resync_listen` / `net_can_accept` 三个 API 是冗余或过期面**
证据:`net_relisten`(net_socket.c:3080)与 `net_resync_listen`(:3026)生产零调用(grep 全仓仅 test/board/net_latency_test.c:776-801);`net_relisten` 对健康监听器无条件 open→USE→close,自认造成 ~1s 监听中断(:3078 注释),与 pump③"芯片在听绝不重下发"的 U12 教训直接冲突。`net_can_accept`(:1427)唯一生产调用者 iec104_serv.c:227。
影响:公开 API 面越大,上层"手动自愈"路径越多,正是用户怀疑的"补丁式代码难维护"的来源;`net_relisten` 是 footgun。
建议:`net_relisten` 删除(台架 Phase 13 改用 pump 语义或测试专用内部入口);`net_resync_listen` 在 F-NET-2 并入对账后删除;`net_can_accept` 在 F-NET-4 迁移后删除。
**✅ 已实施(阶段 1,`b8c6a3d`)**:三 API 与 `NET_MSG_RELISTEN` / `NET_MSG_RESYNC_LISTEN` 消息类型全部删除(MCU + sim 双端),生产/测试调用者归零;Phase 13 的自愈段交回 net 层自动机制。**⚠ 遗留铁律**:门控迁移必须给 `net_listener_armed` 补 ESTABLISHED 放行,见 F-NET-4 实施注。
**F-NET-4 · iec104(:2404)仍用过期门控 `net_can_accept` → 丢 CONNECT 探针对该端口从不运行**
证据:iec104_serv.c:227 以 `net_can_accept`(只认 ESTABLISHED,net_socket.c:1434)门控 accept;`net_lost_connect_probe` 只在 `net_accept_locked` 内 LISTENING 态运行(:1298-1300),门控挡在门外则永不触发——与 N-32 P1 为 :7003 修复的"探针失效"完全同型(ui_net_sink.c:521-526 注释记载原委)。:2404 的丢 CONNECT 只剩对账②兜底,而②带 400ms 去抖 + 1s 冷却(Trap 25 已实证该节奏对短命监听器"救不全")。
影响:104 上行端口的丢中断恢复被拖慢到秒级;三个常驻监听里 :21/:8888 无条件轮询 accept、:7003 已换 `net_listener_armed`,唯独 :2404 漏换。
建议:iec104 门控换 `net_listener_armed`(双端 sim 已有实现);随后 `net_can_accept` 可退役,消除"两个相似门控语义不同"的长期困惑。
**✅ 已实施(阶段 1,`b8c6a3d`)**:门控已迁 `net_listener_armed`,`net_can_accept` 三处(MCU / 头文件 / sim)删除。
**⚠⚠ 实施中发现的坑(必读,实验方案 E10 有完整证据链)**:`net_listener_armed` 首版只放行 LISTENING / CLOSED+残留,**漏放行 ESTABLISHED**——而单连接模型下"连接已到、应用未 accept"的电平正是 ESTABLISHED(CONNECT 处理把监听 fd 自身转数据通道),也就是旧 `net_can_accept` 唯一放行的状态。后果:i104 200ms 任务周期抢不过对账② 的 ~400ms 去抖,对账②先 standalone accept 转 ESTABLISHED → armed=0 → 应用整场不 accept,**iec104 18/18 全 FAIL**(127s 跑满超时;设备日志:`sock1 standalone accept` 后整场无 `i104: accepted`,直至对端 FIN 才由 closed-with-data 救回)。ui_net_sink 之所以不受影响,是 10ms 周期 + `net_accept_locked` 自带 `net_poll` 通常在状态翻转前就拿到 fd——**属竞态幸存,不是设计正确**。修复:armed 放行 ESTABLISHED(此态 accept 必返 fd,一次调用即离开,无 RPC 风暴风险);修复后 18/18 PASS 且耗时 127s→8.3s。
**F-NET-5 · 两套重监听重试策略并存:D10 非阻塞 vs listen 路径 3×osDelay(500) 阻塞**
证据:net_listener.c:71-79(D10,注释明言旧 osDelay(500) 忙等致全网冻结 500ms、实测恢复 588ms);但 net_socket.c:1093-1104 `net_listen_locked` 仍是 open 失败→close→osDelay(500)→重试 ×3(最坏 netTask 冻结 1.5s)。设计文档 P2b 已登记该忙等并**台架依据暂缓**。
@@ -138,17 +142,19 @@
证据:time_sync.c:182 `net_close(conn_sock)`(单连接下 conn==listen)后外层循环 socket+bind+listen 重建(:224);设计文档第 7.4 节契约表已点名"违反『EOF 不得 close 监听 fd』→ close→重建沉降窗(Trap 09)+ 最坏 ~10s 端口下线"。对照正面样板:lftpd 控制通道明确不 close(lftpd.c:746-750/826-827)、iec104 EOF 分支带 `s_client != s_listen` 护栏(iec104_serv.c:259-262)。
影响::8888 每次会话结束都主动制造一次 Trap 09 沉降窗;也是"应用层还能毁监听器"的活例。
建议:裁决"简化(上层)":time_sync 会话收尾不 close,直接回外层在同一 fd 上 accept(lftpd 模式);iec104 `s_drop_conn` 的 close 属协议违例强制断连,语义正当,保留但注释标注为"有意例外"。
**✅ 已实施(阶段 1,`b8c6a3d`)**:`net_close(conn_sock)` 删除,`ls` 提到外层循环跨会话常驻(仅网络代次变化/被毁时重建),`conn != ls`(模拟器多连接后端)才关数据 fd。台架实测连续 6 轮推送正常(周期 ~4s),每轮走 `standalone accept → DISCONNECTED, releasing → settle → auto-relisten ok`,零 `[ERR]`。契约变化已同步 `test/host/time_sync_server.py`(板子不再先关,PC 侧 2s 宽限后自关);⚠ 强杀 PC 进程会走芯片 `[ERR] TIMEOUT` + QUARANTINE 沉降(异常收尾,非正常路径)。
**F-NET-14 · D10 的"2s 放弃"不是终态,`relisten give up` 日志误导排障**
证据:net_listener.c:414-419 到期清队列 + DBG_ERROR;但 pump 在冷却后看到"芯片不在听"会再次 `net_listener_try`(net_listener.c:538-552),监听器实际会无限期重试(每 ~1.2s 一轮)。
影响:日志出现 give up 时端口并非永久死亡,排障者可能误判严重度;同时"无限重试"缺少上限留痕(每轮一条 pump 日志)。
建议:give up 文案改为"重试预算耗尽,交 pump 周期接管";或把 pump 的重触发也纳入同一预算计数(二选一,倾向改文案——保留最终兜底)。
**✅ 已实施(阶段 0,`4c0a0af`)**:文案改为 `relisten retry budget exhausted, pump takes over`;同批补 0-4 retry_tick 状态门(仅 LISTENING/CLOSED/BOUND 之外的状态清陈旧登记,防 open→close 摧毁活连接)。
## 4. 重叠/兜底关系速览(谁兜谁)
- 监听器重建:pump(周期唯一)→ 失败落 D10(30ms×2s)→ D10 放弃后 pump 冷却再触发(无限);消费者三处即时重建把 CLOSED 窗口压到 ~0(防上层 RPC 空转)。
- 丢 CONNECT:探针(accept 内联,快)↔ 对账②(周期,兜底网);两者共享幽灵甄别;:2404 因门控过期只剩②(F-NET-4)。
- 丢 DISCONNECT/静默回收:对账①(芯片 CLOSED 形态)↔ RESYNC API(芯片 LISTEN 形态,无自动兜底 = F-NET-2 缺口)。
- 丢 DISCONNECT/静默回收:对账①(芯片 CLOSED 形态)↔ 对账⑤(芯片回 LISTEN 形态,阶段 1 起自动认领 = F-NET-2 已闭环)。
- 会话结束感知:session_dead 电平(P1)兜住"抢先 relisten 吞 EOF";pending_tick ③ 与 recv DONTWAIT 双路径给 EOF;看门狗兜"完成逻辑缺位"。
- 芯片收尾残留:Trap 23 沉降/隔离(重建前)↔ close_locked 排空循环(显式关闭)↔ listen/relisten 成功尾读本 Socket SINT——三处清理纪律,两套命令变体(F-NET-9 下沉候选)。
- 数据面丢中断:RECV_OK 电平重放(Trap 08)+ readable 门控 + 上层 32 轮盲收(F-NET-12,唯一未内聚的一层)。
@@ -259,7 +265,7 @@ E7b 的"残余 ~3%"不是稳定值:同固件同负载,故障率随设备状
**与 Trap 14 的张力消解**:Trap 14 的残留锁存不是 close 命令制造的,而是**拆链完成事件**——"DISCONNECT 可能在与 CLOSED 状态达成几乎同时、甚至之后才锁存"(Trap 记录 Trap 14 根因节),粘滞跨会话(B3 条目"SINT 粘滞到被读")。锁存的生成者是拆链过程(多为对端 FIN 的被动拆链收尾),不是 close 命令。
**风暴窗口的直接反证**:E8 时间线里 kill 发生在 `auto-relisten ok` **之前**(窗口内 relisten 尚未完成,沉降段 close 还没跑或没再跑)——那几枚 11ms 杀连接锁存**不可能由沉降段 close 产生**。真实来源两条:(a) 上一条 STOR 的客户端 FIN(FTP 正常行为,发完即关,Trap 25 定案)的被动拆链收尾迟到完成(Trap 14/23 的 10~20ms,Trap 25 的 >2s 不可观测窗);(b) 卡死 socket(0x05/0x00)把新 SYN 接进来又自行拆掉(§5.29 语义域),每轮自产一对 CONNECT/DISCONNECT。
**风暴窗口的直接反证**:E8 时间线里 kill 发生在 `auto-relisten ok` **之前**(窗口内 relisten 尚未完成,沉降段 close 还没跑或没再跑)——那几枚 11ms 杀连接锁存**不可能由沉降段 close 产生**。真实来源两条:(a) 上一条 STOR 的客户端 FIN(FTP 正常行为,发完即关,Trap 25 定案)的被动拆链收尾迟到完成(Trap 14/23 的 10~20ms,Trap 25 的 >2s 不可观测窗);(b) 卡死 socket(0x05/0x00)把新 SYN 接进来又自行拆掉(`docs/芯片手册/CH395F.md` §5.29 语义域),每轮自产一对 CONNECT/DISCONNECT。
**但"沉降末轮 SINT 读清不能保证静默"的实操结论仍成立**,经两条路径:
1. 拆链完成滞后于末轮读清(lead 说的硬币——生成者是拆链,不是 close);
+18 -4
View File
@@ -16,9 +16,9 @@
| 阶段 2(驱动原语) | E2、E3、E4 | 回读成本;GET_CMD_STATUS 判别力;reset 序列的正确内部顺序 |
| 阶段 3(net 层收敛) | E1 ✅、E6 | 对账/pump 真实命中频率(已量化);U15 失配频率 |
## 1.5 P0 止血包(E7b 已验证,实验提交待转正)
## 1.5 P0 止血包(E7b 已验证,✅ 已转正 `9ac9879`)
三处一行级改动(分支已实施并台架验证,**未提交**,转正需用户点头):
三处一行级改动(分支已实施并台架验证,**已提交**):
| # | 改动 | 位置 | 效果(E7b 实测) |
|---|---|---|---|
@@ -34,7 +34,14 @@ Trap 23 RST/FIN 5/5+5/5 + @test PASS + 0 错 0 警。**E8 深查修正**(见
**常备回归补挂**:`n30_repro --n 20` 纳入 Ch395F 回归集(本轮 46/50 潜伏的测试面根因);
`MDK-ARM/ftp_abort_probe.py` 迁 `test/host/`。
## 2. 阶段 0 · 零风险清理(先行,不依赖实验)
## 2. 阶段 0 · 零风险清理(先行,不依赖实验)✅ 已完成(`4c0a0af`,E10 验证)
> 实施结果与下表逐条对应;提交切分因 0-A 与 0-5 改动在同一文件内交织而
> **合并为一笔**(8 项,`4c0a0af`),0-3/0-4/0-6 的逻辑改动在提交信息中逐条
> 列出并有冒烟证据。实施中额外抓出并修复:`CH395F_FUN_PARA_SOCKET_CLOSE`
> 重复定义(首版增量构建 9 条 `#47-D` 警告,已并入既有宏块)。
> 验证:全量重编 0 错 0 警 + @test PASS + n30 0/20、:21 @0ms 20/20、
> :7003 20/20、ftp_regression 11/11。
| # | 改动 | 位置 | 验证 |
|---|---|---|---|
@@ -50,7 +57,14 @@ Trap 23 RST/FIN 5/5+5/5 + @test PASS + 0 错 0 警。**E8 深查修正**(见
提交切分:0-1/0-2/0-7/0-8/0-9 一笔(纯注释/死码);0-3/0-4 一笔(一行逻辑,需 @test);0-5/0-6 一笔(init 路径,需 @test)。
## 3. 阶段 1 · API 面收口(门:E5 结果)
## 3. 阶段 1 · API 面收口(门:E5 结果)✅ 已完成(`b8c6a3d`,E10 验证)
> 6 项全部实施,两个偏差与一条关键台架发现见**实验方案 E10**:
> ① `net_listener_armed` 必须补 **ESTABLISHED 放行**(首版漏放行 → iec104
> 18/18 全 FAIL,修复后 18/18 PASS 且 127s→8.3s);② 第 6 项未走
> `net_listener_try` 登记(会把 BOUND/D10 语义拖入),改单次尝试+失败即 425。
> 验证:iec104 18/18、n30 0/50、:21 @0ms 50/50、:7003 50/50、
> ftp_regression 11/11、Trap23 RST/FIN 5/5+5/5、time_sync 连续 6 轮正常。
1. **对账①扩展**(先修缺口再删 API):`net_reconcile_tick` 判据①增一形态——
本层 ESTABLISHED 且芯片 `open=1 && TCP=LISTEN` → 按 RESYNC 语义只改本层
+70 -1
View File
@@ -65,7 +65,7 @@
SET_PROTO_TYPE)发命令后以 ~1ms 节奏轮询 cmd status,记录"变 BUSY→终态"延迟与假阳性
(命令未发时读到的残留态)。
- **判据**:某命令判别力成立(发后 ≤N ms 内变终态、无残留假阳性)→ `ch395f_cmd_done` 可用该判据;
不成立 → 完成判据改用状态回读(实施方案 §4.4 的 B 方案)。
不成立 → 完成判据改用状态回读(`docs/网络/N34-自愈审计/实施方案.md` §4 第 4 项的回退方案)。
- **门禁**:阶段 2 第 4 项形态。
### E4 · sock_reset 内部顺序 A/B(裸机芯片台架 + 生产路径复现)
@@ -303,3 +303,72 @@ n30 ×50(预置后)**0/50** + ×20 **0/20**;ftp_regression 11/11;:21 @0m
M1 存在无基线可考(历史日志未入库,又一"脚本在库、日志未入库"实例)。
**裁决**:M3(R3 资产门版)具备转正条件;风暴态免疫在"探针预置残渣"最恶劣前置下实证。
### E10 结果(2026-10-13 05:40–06:50,阶段 0 + 阶段 1 转正验证,台架锁 N34)
**范围**:M3 转正提交(`ae17d36`)+ 阶段 0 九项(`4c0a0af`)+ 阶段 1 六项
(`b8c6a3d`,16 文件 +193/-371)。三笔均 `@build` 0 错 0 警 + `@test` PASS
(回读无差异)。
**阶段 0 验证**(Code=209580):全量重编 0 错 0 警(含强制重编依赖 TU 以暴露
头文件改动引入的警告——首次增量构建 9 条 `#47-D` 宏重定义警告即由此抓出:
`CH395F_FUN_PARA_SOCKET_CLOSE` 与既有宏块重复定义,已并入原宏块);
冒烟 n30 0/20、:21 @0ms 20/20、:7003 20/20、ftp_regression 11/11。
**阶段 1 首版台架失败与根因(本记录的核心留痕)**:
| 项 | 首版 | 根因 | 修复后 |
|---|---|---|---|
| iec104_master | **18/18 全 FAIL**(127s 跑满超时) | `net_listener_armed` 首版只放行 LISTENING / CLOSED+残留,**漏了 ESTABLISHED** | 18/18 PASS,**8.3s** |
| n30 / :21 / :7003 / ftp / Trap23 | 全绿 | 未受影响(见下) | 全绿 |
**机制**(为什么只有 i104 中招,是一条非显然的坑):
- 单连接模型下"连接已到、应用未 accept"的电平就是 **ESTABLISHED**(CONNECT
处理把监听 fd 自身转数据通道)——这正是旧 `net_can_accept` 唯一放行的状态。
- 漏放行后能否活下来纯看**消费者任务周期与对账②去抖赛跑**:
ui_net_sink 10ms 周期 → `net_accept_locked` 内自带 `net_poll` 通常先处理
CONNECT 并在同一次调用里返回 fd(探针/在途 accept 抢先);
**i104 200ms 周期抢不过对账② 的 ~400ms 去抖** → 对账② standalone accept
先转 ESTABLISHED → armed=0 → 应用整场不 accept、芯片 RX 数据无人读。
- 设备日志证据链:`sock1 standalone accept`(06:24:23)→ 之后整场无
`i104: accepted`,直到对端 FIN 才由 `closed-with-data accept` 救回
(`i104: accepted fd=1` 06:26:30)。期间主站 I 帧全部无响应。
- **修复语义**:armed = 旧 `net_can_accept` 的 ESTABLISHED 放行态 ∪ LISTENING
∪ CLOSED+残留。此态 accept 必返 fd,一次调用即离开分支,**无 RPC 风暴风险**
(P2a 的 task stall 约束针对 CLOSED 无残留态,不适用)。
**M3 无辜旁证(顺带实证)**:同一窗口 `sock1 suspect DISCONNECT on live conn
(chip 0x05/0x00), defer to reconcile` 命中 → 对账① 1.2s 后接管 →
`closed-with-data accept (drain pending)` 把**未被 accept 的 RX 数据**救回
——正是 M3 资产门的设计意图(芯片 RX 有在途数据 = 不可毁的消费者资产)。
**阶段 1 全绿回归(修复版,Code=209580,@test PASS)**:
iec104 **18/18**(8.3s,较基线 127s 大幅缩短:连接即刻被 accept);
n30 ×50 **0/50**;:21 @0ms×50 **50/50** p50=512;:7003 100ms×50 **50/50**
p50=517;ftp_regression **11/11**;Trap23 RST **5/5** + FIN **5/5**。
**time_sync 第 5 项(会话收尾不 close)实测**:连续 6 次推送成功(RTC 逐次
更新),每轮 `standalone accept → DISCONNECTED, releasing → relisten settle →
auto-relisten ok → 下一轮 accept`(周期 ~4s = PC 侧 2s 宽限 + 2s 间隔),
零 `[ERR]`。契约变化已同步 `test/host/time_sync_server.py` 注释:板子不再先
关,PC 侧 2s 宽限后自行关闭。⚠ 注意:若 PC 进程被**强杀**(非正常关闭),
芯片侧会走 `[ERR] sock0 TIMEOUT` + QUARANTINE 沉降(400ms)再重建——本轮
首次验证即因强杀验证进程复现一次,属异常收尾,非正常路径。
**阶段 1 与实施方案的两处偏差(诚实登记)**:
1. 第 6 项原计划"PASV 监听失败改走 `net_listener_try` 登记",实施时判定该路
径会把 armed/BOUND/D10 状态语义一并拖入(需给 `net_relisten_locked` 补
`auto_relisten`、给 armed 补 BOUND 放行、改 0-4 状态门),代价与一个罕见
故障不匹配 → 改为**单次尝试 + 失败即 425**(主流 FTP 服务器 passive
listen 失败亦如此,客户端自带传输重试语义),并删除 `cmd_pasv` 的
3×200ms 重试。
2. 第 2/3 项原计划拆分两笔提交,实施时因 0-A 与 0-5 的改动在同一文件内交织
→ 阶段 0 合并为一笔(0-3/0-4/0-6 的逻辑改动在提交信息中逐条列出并有
冒烟证据),阶段 1 一笔。判据与验证不受影响。
**编译面验证(阶段 1 附带)**:`net_latency_test.c` 的 Phase 13 清理位于
`#ifdef ENABLE_PHASE13_TESTS` 内、产品构建不编译 → 临时开 CH395F 套件 +
Phase13 宏全量重编 **0 错 0 警**(Code=216632)后还原宏再重编产品镜像,
确认死代码无语法/符号残留。