fix(N34): E14 丢中断复核——芯片不丢事件;listen 排事件挪至 LISTEN 前+全局读收口+全库归因更正
48 轮直读 SINT 矩阵(8 周期×6 变体)复核历史"丢中断"说法:芯片侧事件 丢失 = 0。归类:drain 读清消费 3/48(drain-int 探针现场抓获)、门控 竞态 2/48、主机同步 1/48、幽灵态 0/48 未复现。 根除性修复:net_listen_locked / net_listener_try 的排事件读清从 listen 后挪至 LISTEN 命令之前——新连接 CONNECT 落进该窗即被本方读清 吞掉,即"数据被吞"的生产侧自伤机制(台架 3/48 为同形态放大版)。 全局读收口:全局中断寄存器唯一读者 = net_poll(I7);生产/测试/台架 游离全局读全部删除,ch395f.h 声明处加护栏注释。 归因更正:17 文件约 40 处"丢中断"措辞改为"事件未收到/读清消费竞态" (机制全部保留,仅归因更正);设计文档 §5.2 新增 E14 锚点;Trap Records TRAP-NET-17/-20/B3 归因更正段;配图生成器与 SVG 重生成。 台架:事件未收到复核探针保留(drain-int / SINT 补读×3 / 超时归类), 下次跑矩阵自动归类;实验注册(main.c/uvprojx)已完全还原。 验证:@build 0 错误 0 警告;ci_local L0 32/0 + gitlint + L0.5 953/953;doc-links --all --sections-strict 通过;@test PASS(无 [ERR],net init 619ms);回归 :7003 300ms×10 = 10/10、:21 n30×50 = 0/50 失败。
This commit is contained in:
+15
-81
@@ -2,7 +2,8 @@
|
||||
|
||||
> **模块名称**:Net 层(`net_socket.c` / netTask)阻塞等待模型
|
||||
> **文档定位**:**现状架构设计说明**——描述本层**当前如何组织等待与完成**、为什么这样设计、
|
||||
> 边界条件与已验证的性能特征。**不是历史记录**;演进过程与门禁实测数据作为溯源附于文末。
|
||||
> 边界条件与已验证的性能特征。**不是历史记录**:历史演进、门禁实测记录与修订日志
|
||||
> 一律不保留在文档内(需要时查 git 历史)。
|
||||
> **状态**:设计已落地并全量台架验证(`v2-on-ui`)。
|
||||
> **日期**:2026-09-29
|
||||
> **相关文档**:
|
||||
@@ -43,8 +44,8 @@ netTask 是**全部 socket 操作的唯一执行者**——CH395F 的 SPI 总线
|
||||
| 区域 | 内容 |
|
||||
|---|---|
|
||||
| **左栏 · 调用方任务** | `net_recv` → 判"数据已在?" → **是**走快速路径;**否**(`timeout > 0`)登记 pending 后在自己上下文睡任务通知 |
|
||||
| **中栏 · netTask 主循环** | ~1ms 节拍:`net_poll`(刷新 readable + 全量刷新驱动状态缓存)→ 3 个非阻塞 tick(`net_listener_retry_tick` D10 → `net_pending_tick` 了结挂起 → `net_reconcile_tick` TRAP-NET-17 对账 + 监听重建唯一入口,N34 阶段 3 起原 `net_listener_pump` 已并入)→ 排空消息队列 |
|
||||
| **下带 · 五路判定** | 每轮对每个挂起 fd 按序判定:数据到达 / 到点(仅显式预算)/ EOF / 同 fd 后续操作 / 网络重建;tripwire 60s 只留痕(N34 阶段 1.5 由 1s 看门狗降级) |
|
||||
| **中栏 · netTask 主循环** | ~1ms 节拍:`net_poll`(刷新 readable + 全量刷新驱动状态缓存)→ 3 个非阻塞 tick(`net_listener_retry_tick` D10 → `net_pending_tick` 了结挂起 → `net_reconcile_tick` TRAP-NET-17 对账 + 监听重建唯一入口)→ 排空消息队列 |
|
||||
| **下带 · 五路判定** | 每轮对每个挂起 fd 按序判定:数据到达 / 到点(仅显式预算)/ EOF / 同 fd 后续操作 / 网络重建;tripwire 60s 只留痕 |
|
||||
| **回环** | 判定完成经 `xTaskNotify` 唤醒调用方(结果 = 字节数 / 负错误码) |
|
||||
|
||||
**关键性质**:
|
||||
@@ -52,12 +53,10 @@ netTask 是**全部 socket 操作的唯一执行者**——CH395F 的 SPI 总线
|
||||
- 等待期间 netTask **零额外占用**——`net_pending_tick` 只读标志与到期时间,
|
||||
数据到达判据 `readable` 由主循环既有的 `net_poll()` 每轮刷新,**不引入额外 SPI 事务**;
|
||||
- 调用方**在自己的上下文睡眠**(任务通知),与 LwIP 的消费者线程模型同构;
|
||||
- 公开 API 与调用契约**不变**——`net_recv` / `net_recv_to` 的签名、返回值、
|
||||
超时语义均与改造前一致,既有调用方、测试与模拟器零改动。
|
||||
(**N34 阶段 1.5 更新**:本条描述 L3 改造当时口径;2026-10-13 起 `net_recv`
|
||||
默认预算 30s 删除、改为无限阻塞,`net_recv_to` 成为唯一显式预算入口——
|
||||
挂起模型与"等待不占 netTask"性质不变,变的是超时归属:见
|
||||
`docs/网络/N34-自愈审计.md` §5.3.5 与 BSD 指南 §3.5。)
|
||||
- 公开 API 与调用契约**稳定**——`net_recv`(默认无限阻塞)/ `net_recv_nb` +
|
||||
`net_setblocking` / `net_recv_to`(唯一显式预算入口)的签名与返回值不变,
|
||||
既有调用方、测试与模拟器零改动;超时归属语义详见
|
||||
`docs/网络/网络自愈架构设计.md` §4.5 与 `docs/网络/BSD_Socket_API_使用指南.md` §3.5。
|
||||
|
||||
> 图形由 `tools/arch_diagram/gen_net_pending_svg.py` 生成(改流程后重跑;见
|
||||
> `docs/架构设计/图形绘制流程.md`)。
|
||||
@@ -71,7 +70,7 @@ netTask 是**全部 socket 操作的唯一执行者**——CH395F 的 SPI 总线
|
||||
| 项 | 设计 |
|
||||
|---|---|
|
||||
| 存储 | **独立静态数组** `s_net_pending[NET_MAX_SOCKETS]`(不置于 `net_sock_t` 内) |
|
||||
| 字段 | `active` / `infinite` / `tripwire_logged` / `buf` / `len` / `deadline` / `registered` / `caller`(`infinite`/`tripwire_logged` 为 N34 阶段 1.5 新增:0 预算 = 无限等待、tripwire 只留痕一次) |
|
||||
| 字段 | `active` / `infinite` / `tripwire_logged` / `buf` / `len` / `deadline` / `registered` / `caller`(`infinite`:0 预算 = 无限等待;`tripwire_logged`:tripwire 只留痕一次) |
|
||||
| 容量 | 每 fd **至多一个**挂起请求 |
|
||||
| 占用 | ≈24B/fd × 8 fd = **192B**(+4B 任务句柄),实测 SRAM +200B |
|
||||
|
||||
@@ -97,7 +96,7 @@ socket 生命周期解耦,与 `net_listener_retry_tick` 的重试表同构。
|
||||
| # | 条件 | 动作 | 设计要点 |
|
||||
|---|---|---|---|
|
||||
| 1 | **数据到达** | SPI 读出 + 通知(结果 = 字节数) | 由 `net_poll` 维护的 `readable` 门控,**零额外 SPI**;仅 ESTABLISHED / UDP_OPEN 态可读;spurious readable 需清除 |
|
||||
| 2 | **到点** `now >= deadline`(仅显式预算登记;无限等待登记无 deadline——N34 阶段 1.5) | 通知 `NET_ERR_TIMEDOUT`(对外 -2 映射保持) | 状态无关;用有符号回绕安全差值,防 tick 回绕;这是 `net_recv_to` 调用方策略的正常兑现,不是伪造 |
|
||||
| 2 | **到点** `now >= deadline`(仅显式预算登记;无限等待登记无 deadline) | 通知 `NET_ERR_TIMEDOUT`(对外 -2 映射保持) | 状态无关;用有符号回绕安全差值,防 tick 回绕;这是 `net_recv_to` 调用方策略的正常兑现,不是伪造 |
|
||||
| 3 | **CLOSED**(EOF/DISCONNECT) | 排空:有数据则读出;否则按 0(EOF) 完成 + `auto_relisten` + 通知 | CLOSE 先行完成挂起 = `net_close` 的 settle 钩子(排空→数据否则 CONNRESET) |
|
||||
| 4 | **同 fd 后续操作** | 先结算挂起,再执行新操作 | per-fd 串行——冻结面从"全网"缩到"本 fd" |
|
||||
| 5 | **网络重建**(`net_stack_bring_up`,芯片复位) | **取消 + 必须通知** | `net_init` 顶部 `net_pending_cancel_all(CONNRESET)`,先于芯片复位;**取消不通知 = 调用方永久挂死** |
|
||||
@@ -105,11 +104,11 @@ socket 生命周期解耦,与 `net_listener_retry_tick` 的重试表同构。
|
||||
**状态推断的克制**:CONNECTING / LISTENING 等中间态**保持等待**,**不做**状态推断式取消——
|
||||
CONNECT → ESTABLISHED 是合法迁移,推断取消会误杀正在建链的等待。
|
||||
|
||||
### 4.1 tripwire(原"看门狗",N34 阶段 1.5 降级)
|
||||
### 4.1 tripwire
|
||||
|
||||
挂起超 60s(`NET_RECV_TRIPWIRE_MS`)= "完成逻辑缺位"探针 → **只 `DBG_ERROR` 留痕一次,
|
||||
绝不强制了结、绝不伪造 TIMEDOUT 返回值**(旧设计"到点 +1s 强制了结"会掩盖完成路径
|
||||
缺陷,且与"无限等待"的新语义矛盾——伪造返回值违背调用方意图)。
|
||||
绝不强制了结、绝不伪造 TIMEDOUT 返回值**——强制了结会掩盖完成路径缺陷,伪造返回值
|
||||
违背调用方意图。
|
||||
定位:留痕成本极低的开发期探针;唤醒责任在 ①数据 ②到点(仅显式预算)③断连 与
|
||||
显式 cancel 路径(close / 网络重建 / 陈旧重同步),见 §4 五路。
|
||||
|
||||
@@ -125,9 +124,6 @@ CONNECT → ESTABLISHED 是合法迁移,推断取消会误杀正在建链的
|
||||
| 剩余预算循环 | 调用方保留"总预算 − 已耗时"循环骨架(`n == -2` 续轮,预算耗尽退出)——这是**公开契约要求**(30s 总超时语义 + 跨 TCP 分段累积),与本架构无关 |
|
||||
| 超时精度 | 完成判定随主循环节拍(~1ms);主循环被长操作占用时,超时**最多顺延**相应时长(已知代价,见 §7) |
|
||||
|
||||
> **已退役形态**:改造前调用方需自行实现"5ms 分片 + `vTaskDelay(1)` 让出"协议。
|
||||
> 该协议已随本架构落地而删除——分片是"执行者被占"前提下的妥协,pending 模型下不再需要。
|
||||
|
||||
---
|
||||
|
||||
## 6. 性能特征(已验证)
|
||||
@@ -135,11 +131,9 @@ CONNECT → ESTABLISHED 是合法迁移,推断取消会误杀正在建链的
|
||||
| 指标 | 值 | 条件 |
|
||||
|---|---|---|
|
||||
| 队列响应性(挂起连接下) | **92.5ms** | 挂起对时客户端 + FTP banner 探测 |
|
||||
| 改造前同场景 | 28554ms | — |
|
||||
| M3 静默档帧周期 | med 58.9 / p95 69.9 / max 72.1ms | 零尖峰 |
|
||||
| M3 压力档帧周期 | med 60.1 / p95 66.4 / max 69.3ms | 零尖峰,p99 尾部 133.5→69.3ms |
|
||||
| netTask 栈峰值 | 488B / 4096(余量 88%) | 零占用设计落点 |
|
||||
| RAM / Flash 增量 | +200B / +608B | 相对改造前 |
|
||||
|
||||
---
|
||||
|
||||
@@ -150,8 +144,8 @@ CONNECT → ESTABLISHED 是合法迁移,推断取消会误杀正在建链的
|
||||
| **SEND 路径未覆盖** | 发送仍为分块阻塞(1s/chunk stall) | 该期间队列仍被冻结;二期 pending 化解决 |
|
||||
| **ACCEPT 路径未覆盖** | accept 仍用轮询计时 | 需 pending 化(二期) |
|
||||
| **超时精度** | 随主循环节拍(~1ms),被长操作占用时顺延 | 一期文档化、不修;二期 SEND 化后该顺延面**收窄** |
|
||||
| **tripwire 未接自检** | 只 `DBG_ERROR` 留痕(N34 阶段 1.5 起不再强制了结),未写 selftest | 二期补齐 |
|
||||
| **模拟器不可建模** | `net_socket_sim.c` 单线程 Winsock 无法复现**挂起模型内部机制**(外部语义已由包装层按 blocking 属性/预算模拟,N34 阶段 1.5) | 挂起表/tick/通知路径在模拟器上零覆盖,只能硬件验证;**监听生命周期 `net_listener.c` 亦为真机专属**(内部头只被 `net_socket.c` include,CMakeLists 不编译) |
|
||||
| **tripwire 未接自检** | 只 `DBG_ERROR` 留痕(不再强制了结),未写 selftest | 二期补齐 |
|
||||
| **模拟器不可建模** | `net_socket_sim.c` 单线程 Winsock 无法复现**挂起模型内部机制**(外部语义已由包装层按 blocking 属性/预算模拟) | 挂起表/tick/通知路径在模拟器上零覆盖,只能硬件验证;**监听生命周期 `net_listener.c` 亦为真机专属**(内部头只被 `net_socket.c` include,CMakeLists 不编译) |
|
||||
|
||||
**二期扩展设计**(ACCEPT / SEND 的挂起化、门禁清单、改动量)见
|
||||
`docs/任务规划/基线与待确认.md` §2.1。
|
||||
@@ -167,63 +161,3 @@ CONNECT → ESTABLISHED 是合法迁移,推断取消会误杀正在建链的
|
||||
4. **不做状态推断式取消**——中间态(CONNECTING/LISTENING)合法迁移存在,
|
||||
推断取消会误杀正常建链等待。
|
||||
5. **数据到达不引入额外 SPI**——判据复用主循环既有 `net_poll` 维护的 `readable` 标志。
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:演进溯源
|
||||
|
||||
> 本架构由 E-02 缺陷修复演进而来(明细见 `错误码传播审计.md` E-02)。**以下为历史信息,
|
||||
> 描述"如何演到这里",不作为当前设计依据。**
|
||||
|
||||
**E-02 缺陷**:netTask 每次取一条消息处理到完成才取下一条;任一消费者发起的 30s 阻塞
|
||||
`net_recv` 期间队列整体停摆最长 30s(FTP 客户端空闲 >30s、对时客户端连上不发数据均可触发)。
|
||||
|
||||
**两代形态**:
|
||||
|
||||
| 代次 | 形态 | 状态 | 提交 |
|
||||
|---|---|---|---|
|
||||
| 一代 | 分片有界等待:`net_recv_to` + 5ms 片 + 30s 预算 + 片间 `vTaskDelay(1)` 让出 | 已退役 | `f8d6e90` / `4a1605b` |
|
||||
| 二代 | **pending 请求(本文档所述,现行)** | **一线现行** | `a45c0cb` |
|
||||
|
||||
**实施与初始设计基线的偏差**(如实留档):
|
||||
|
||||
| 基线条目 | 实施 | 说明 |
|
||||
|---|---|---|
|
||||
| pending 控制块 ≈20B/fd | ≈24B/fd × 8 = 192B | 实测 SRAM +200B,同量级 |
|
||||
| 控制块置于 `net_sock_t` 内 | **独立静态数组** | 见 §3.1 理由 |
|
||||
| 无数据 → 登记、跳过通知 | 哨兵 `NET_PENDING_REGISTERED`(0x70000000)→ `do_notify=0` | 逐字一致 |
|
||||
| 调用方改单次 `net_recv_to(…, 30000)` | **保留剩余预算循环** | 公开契约要求(见 §5) |
|
||||
|
||||
**验证门禁逐条实测**(2026-09-22,设备 192.168.1.100):
|
||||
|
||||
| # | 门禁 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | CH395F NET 层套件 | **PASS**:F1 组合 Phase 6/9/11(64B/1460B 回显 11/11、select 回显 11/11、断开重连、KeepAlive + auto-relisten);F2 组合 Phase 8-RECV(100KB 阻塞接收 50/50 块 + CRC32 整包一致 0x9A0E0C8C)。Phase 7/10 未跑(DONTWAIT 路径另由 M3 探针族 L5~L7 覆盖)。TC-NET-604 FAIL = 既有芯片侧问题(2026-09-23 结案非缺陷,判读教训见 `docs/问题记录/CH395F_Trap_Records.md` 台架纪律) |
|
||||
| 2 | M3 基线重锁 | **PASS**:见 §6 |
|
||||
| 3 | e02_probe A/B | **PASS 且优于分片方案**:队列响应性 92.5ms;30s 截止按节拍精确触发(30.1s);监听恢复 6.6s |
|
||||
| 4 | ftp_regression / E-06 假 425 | 随 F1/F2 + M3 压力档覆盖(FTP 半登录态即其场景) |
|
||||
| 5 | UI 远端推流压力 | **PASS**:`:7003` 100Hz 全帧 CRC 50/50,零尖峰 |
|
||||
| 6 | timeSync 三场景 | **PASS**:挂起 / 正常包;部分包由 Phase 8-RECV 跨段累积等效覆盖 |
|
||||
| 7 | 产品 `@test` + 全片回读 | **PASS**:Required 全中 / Forbidden 零命中;回读 `No difference found` |
|
||||
| 8 | RAM/栈前后对比 | **PASS**:见 §6 |
|
||||
|
||||
**未执行 / 延后项**:
|
||||
|
||||
- 未保留分片路径作影子对照 → 分片协议直接退役;对照由"旧镜像 A/B"承担,回退 = git revert
|
||||
(单提交、API 不变);
|
||||
- 探针族扩充(close/reconfig/EOF/disconnect-during-pending 专项)→ 二期;
|
||||
- 看门狗接 selftest 留痕 → 二期;
|
||||
- 模拟器差距 +1(`net_socket_sim.c` 无法建模挂起)→ 二期更新清单;
|
||||
- 台架期间另发现一次 49s 全系统日志静默(defaultTask/NAND syslog 排空停滞,net 栈照常工作)
|
||||
——既有存储侧现象,与本层无关,见 `docs/任务规划/README.md` N-2。
|
||||
|
||||
## 附录 B:修订记录
|
||||
|
||||
| 日期 | 内容 |
|
||||
|---|---|
|
||||
| 2026-09-22 | 一期落地并全量台架验证(原《Net 层阻塞模型业界对照与演进方案》实施记录) |
|
||||
| 2026-09-26 | 调研文档退役,客观实施记录独立成文 |
|
||||
| 2026-09-29 | **改写为架构设计文档**:以"当前如何设计/为何如此"为主线重组织;历史演进与门禁实测降级为附录 A;迁入 `docs/网络/` |
|
||||
| 2026-10-03 | **网络重建失败恢复(P1-8 / A2)落地并真机验证**:首启组网失败 = 自检 `net init fail` + 30s 周期重试(1s 分片喂心跳,**不自驻留**);重建(reconfig)失败 = 自检 `net reinit fail` + 30s 延迟重触发事件位(tick 回绕安全),成功即清零;PHY 链路超时仍走既有 `comms fault` 降级(语义不变)。真机:开机 `net init: OK` / `PHY: 100M FULL` / `netTask: started`,无 `init FAIL` / `retry in 30s` 误触发(**正常路径零回归**);芯片级 `net_init` 失败分支 / 拔线负向场景待专项窗口(`待处理内容.md` N-16)。**执行判据见 `测试说明/实机测试实施指南.md` §7.1 / §10** |
|
||||
| 2026-10-12 | **N-32 监听生命周期模块化 + 单一所有者**:监听生命周期抽为 `net_listener.c`(`net_listener_try` / `net_listener_clear` / `net_listener_retry_tick` / `net_listener_pump` / `net_listener_settle`,真机专属、模拟器不编译);**重建动作收敛到唯一所有者 `net_listener_pump`**(100ms,去抖+冷却),对账 `net_reconcile_tick` 只剩①②;主循环自愈 tick 由 3 个增至 **4 个**。详见 `docs/子系统设计/网络Socket监听生命周期设计说明.md` §10 |
|
||||
| 2026-10-13 | **N34 阶段 3:对账与 pump 合并 + 忙等统一 + E-02 收口**(实施规格 `docs/网络/N34-自愈审计.md` §5.5):`net_listener_pump` 删除、并入 `net_reconcile_tick`(单一 100ms tick,形态①②③④⑤+U11,判据读驱动状态缓存,分级去抖 ②1/③④⑤2/①4 轮 + 1s 冷却)——**主循环自愈 tick 由 4 个回到 3 个**,单一重建所有者不变量不变(=形态③④);`net_listen_locked`/`net_connect_locked`/`net_close_locked` 的盲等改 `net_sock_reset_sync` 确定性排渣(GRACEFUL 100ms 底 / 600ms 封顶),close/connect 不再读清全局中断寄存器(F-NET-7);新增 `net_drop_conn()`(拆会话保监听,`NET_MSG_DROP_CONN`)+ `net_poll` 每轮步进 `ch395f_sock_reset_poll`。挂起 recv 模型本身不变(`net_drop_conn` 复用 `net_pending_settle_close` 先了结挂起)。台架回归全绿(Code=211348) |
|
||||
|
||||
Reference in New Issue
Block a user