Net 层阻塞模型架构设计 §2 架构总览改用 SVG 矢量图

This commit is contained in:
2026-09-29 06:00:48 +08:00
parent 0485e79da0
commit 0ddf8c9aca
4 changed files with 402 additions and 30 deletions
+86
View File
@@ -0,0 +1,86 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1030" viewBox="0 0 1440 1030" font-family="Microsoft YaHei, Segoe UI, PingFang SC, sans-serif">
<rect x="0" y="0" width="1440" height="1030" fill="#ffffff"/>
<text x="40" y="44" font-size="21" font-weight="700" fill="#0f172a">Net 层阻塞模型 · pending 请求架构</text>
<text x="40" y="70" font-size="13" fill="#64748b">把「等数据」从执行者内的自旋,改为执行者外的登记 —— 调用方在自己上下文睡眠,netTask 零占用地服务整个队列</text>
<rect x="40.0" y="100" width="16" height="16" rx="4" fill="#ffffff" stroke="#2563eb" stroke-width="2"/>
<text x="64.0" y="112" font-size="12.5" fill="#475569">调用方任务</text>
<rect x="150.5" y="100" width="16" height="16" rx="4" fill="#ffffff" stroke="#7c3aed" stroke-width="2"/>
<text x="174.5" y="112" font-size="12.5" fill="#475569">netTask(唯一执行者)</text>
<rect x="336.8" y="100" width="16" height="16" rx="4" fill="#ffffff" stroke="#0d9488" stroke-width="2"/>
<text x="360.8" y="112" font-size="12.5" fill="#475569">CH395F 芯片</text>
<rect x="460.5" y="100" width="16" height="16" rx="4" fill="#ffffff" stroke="#d97706" stroke-width="2"/>
<text x="484.5" y="112" font-size="12.5" fill="#475569">主循环节拍 ~1ms</text>
<rect x="40" y="140" width="440" height="600" rx="14" fill="#f8fafc" stroke="#64748b" stroke-opacity="0.28"/>
<text x="58" y="168" font-size="15.0" font-weight="700" fill="#334155">调用方任务(消费者)</text>
<rect x="64.0" y="196.0" width="392.0" height="74.0" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.55" stroke-width="1.6"/>
<text x="80.0" y="228.0" font-size="14.0" font-weight="700" fill="#0f172a">net_recv(fd, buf, len, timeout)</text>
<text x="80.0" y="252.0" font-size="12.0" fill="#64748b">公开 API 与调用契约不变(签名 / 返回值 / 超时语义)</text>
<polyline points="260.0,270.0 260.0,300.0" fill="none" stroke="#2563eb" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M 260.0 300.0 L 254.5 289.0 L 265.5 289.0 z" fill="#2563eb"/>
<rect x="64.0" y="300.0" width="392.0" height="74.0" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.55" stroke-width="1.6"/>
<text x="80.0" y="332.0" font-size="14.0" font-weight="700" fill="#0f172a">数据已在?</text>
<text x="80.0" y="356.0" font-size="12.0" fill="#64748b">readable 标志由 net_poll 每轮刷新</text>
<polyline points="260.0,374.0 260.0,404.0" fill="none" stroke="#2563eb" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M 260.0 404.0 L 254.5 393.0 L 265.5 393.0 z" fill="#2563eb"/>
<rect x="64.0" y="404.0" width="392.0" height="74.0" rx="10" fill="#f5f3ff" stroke="#7c3aed" stroke-opacity="0.55" stroke-width="1.6"/>
<text x="80.0" y="436.0" font-size="14.0" font-weight="700" fill="#0f172a">登记 pending 到 s_net_pending[fd]</text>
<text x="80.0" y="460.0" font-size="12.0" fill="#64748b">占用执行者到此结束(微秒级)· 每 fd 至多一个,不覆盖</text>
<polyline points="260.0,478.0 260.0,508.0" fill="none" stroke="#7c3aed" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M 260.0 508.0 L 254.5 497.0 L 265.5 497.0 z" fill="#7c3aed"/>
<rect x="64.0" y="508.0" width="392.0" height="74.0" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.55" stroke-width="1.6"/>
<text x="80.0" y="540.0" font-size="14.0" font-weight="700" fill="#0f172a">调用方在自己上下文睡任务通知</text>
<text x="80.0" y="564.0" font-size="12.0" fill="#64748b">与 LwIP 消费者线程模型同构 · 取消必通知,永不挂死</text>
<text x="468.0" y="330.0" font-size="12" font-weight="700" fill="#16a34a">是</text>
<text x="78.0" y="396.0" font-size="12" font-weight="700" fill="#dc2626">否,timeout &gt; 0</text>
<rect x="540.0" y="300.0" width="830.0" height="74.0" rx="10" fill="#f0fdfa" stroke="#0d9488" stroke-opacity="0.55" stroke-width="1.6"/>
<text x="556.0" y="332.0" font-size="14.0" font-weight="700" fill="#0f172a">立即 SPI 读出</text>
<text x="556.0" y="356.0" font-size="12.0" fill="#64748b">GET_REMV_LEN / READ → 通知调用方(结果 = 字节数)</text>
<polyline points="456.0,337.0 538.0,337.0" fill="none" stroke="#16a34a" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M 538.0 337.0 L 527.0 342.5 L 527.0 331.5 z" fill="#16a34a"/>
<polyline points="456.0,441.0 538.0,441.0" fill="none" stroke="#7c3aed" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M 538.0 441.0 L 527.0 446.5 L 527.0 435.5 z" fill="#7c3aed"/>
<text x="498.0" y="418.0" font-size="11.5" fill="#64748b" text-anchor="middle">登记即返回</text>
<text x="498.0" y="432.0" font-size="11.5" fill="#64748b" text-anchor="middle">(微秒级)</text>
<rect x="540" y="392" width="830" height="100" rx="12" fill="#fffbeb" stroke="#d97706" stroke-opacity="0.5" stroke-width="1.6"/>
<text x="556" y="418" font-size="14.0" font-weight="700" fill="#b45309">netTask 主循环(~1ms 节拍)—— 挂起期间照常服务整个队列</text>
<rect x="556.0" y="434.0" width="190.5" height="46" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.6"/>
<text x="566.0" y="454.0" font-size="12.5" font-weight="700" fill="#0d9488">net_poll()</text>
<text x="566.0" y="472.0" font-size="11" fill="#64748b">刷新 readable</text>
<rect x="758.5" y="434.0" width="190.5" height="46" rx="8" fill="#ffffff" stroke="#d97706" stroke-opacity="0.6"/>
<text x="768.5" y="454.0" font-size="12.5" font-weight="700" fill="#d97706">net_pending_tick()</text>
<text x="768.5" y="472.0" font-size="11" fill="#64748b">了结挂起</text>
<rect x="961.0" y="434.0" width="190.5" height="46" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.6"/>
<text x="971.0" y="454.0" font-size="12.5" font-weight="700" fill="#7c3aed">net_relisten_retry_tick()</text>
<text x="971.0" y="472.0" font-size="11" fill="#64748b">重试对账</text>
<rect x="1163.5" y="434.0" width="190.5" height="46" rx="8" fill="#ffffff" stroke="#64748b" stroke-opacity="0.6"/>
<text x="1173.5" y="454.0" font-size="12.5" font-weight="700" fill="#64748b">排空消息队列</text>
<text x="1173.5" y="472.0" font-size="11" fill="#64748b">服务所有消费者</text>
<rect x="40" y="766" width="1360" height="234" rx="14" fill="#f8fafc" stroke="#64748b" stroke-opacity="0.28"/>
<text x="58" y="794" font-size="15.0" font-weight="700" fill="#334155">net_pending_tick():五路完成 / 取消判定(每轮对每个挂起 fd 按序)</text>
<polyline points="955.0,492.0 955.0,764.0" fill="none" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M 955.0 764.0 L 949.5 753.0 L 960.5 753.0 z" fill="#d97706"/>
<text x="66" y="832.0" font-size="14" font-weight="700" fill="#0d9488">①</text>
<text x="96" y="832.0" font-size="12.5" font-weight="700" fill="#0f172a">数据到达</text>
<text x="270" y="832.0" font-size="12.5" fill="#475569">→ SPI 读出,通知调用方(结果 = 字节数)</text>
<text x="66" y="859.0" font-size="14" font-weight="700" fill="#d97706">②</text>
<text x="96" y="859.0" font-size="12.5" font-weight="700" fill="#0f172a">到点 deadline</text>
<text x="270" y="859.0" font-size="12.5" fill="#475569">→ 通知 NET_ERR_TIMEDOUT(对外 -2 映射保持)</text>
<text x="66" y="886.0" font-size="14" font-weight="700" fill="#7c3aed">③</text>
<text x="96" y="886.0" font-size="12.5" font-weight="700" fill="#0f172a">EOF / DISCONNECT</text>
<text x="270" y="886.0" font-size="12.5" fill="#475569">→ 按 0(EOF) 完成 + auto-relisten + 通知</text>
<text x="66" y="913.0" font-size="14" font-weight="700" fill="#2563eb">④</text>
<text x="96" y="913.0" font-size="12.5" font-weight="700" fill="#0f172a">同 fd 后续操作</text>
<text x="270" y="913.0" font-size="12.5" fill="#475569">→ 先行结算该 fd 挂起,再执行新操作</text>
<text x="66" y="940.0" font-size="14" font-weight="700" fill="#dc2626">⑤</text>
<text x="96" y="940.0" font-size="12.5" font-weight="700" fill="#0f172a">网络重建(芯片复位)</text>
<text x="270" y="940.0" font-size="12.5" fill="#475569">→ 取消 + 必须通知(否则调用方永久挂死)</text>
<text x="96" y="969" font-size="12" font-weight="700" fill="#dc2626">看门狗:到点后 1s 仍未了结 = 完成逻辑缺位 → 强制了结 + DBG_ERROR</text>
<rect x="1120" y="812" width="250" height="62" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.55" stroke-width="1.6"/>
<text x="1134" y="838" font-size="13.5" font-weight="700" fill="#2563eb">xTaskNotify</text>
<text x="1134" y="858" font-size="11.5" fill="#64748b">唤醒调用方(结果 = 字节数 / 负错误码)</text>
<polyline points="1080.0,843.0 1118.0,843.0" fill="none" stroke="#2563eb" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M 1118.0 843.0 L 1107.0 848.5 L 1107.0 837.5 z" fill="#2563eb"/>
<polyline points="1245.0,874.0 1245.0,988.0 50.0,988.0 50.0,545.0 56.0,545.0" fill="none" stroke="#2563eb" stroke-width="2.2" stroke-linejoin="round" stroke-dasharray="7 5"/>
<path d="M 56.0 545.0 L 45.0 550.5 L 45.0 539.5 z" fill="#2563eb"/>
<text x="40" y="1006" font-size="12" fill="#64748b">关键性质:挂起期间 netTask 零额外占用(判据复用主循环既有 net_poll 的 readable,不引入额外 SPI 事务);公开 API 不变,既有调用方 / 测试 / 模拟器零改动。</text>
</svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

+4
View File
@@ -44,6 +44,10 @@
| `gen_frame_svg.py` | `docs/images/RemoDispBus帧格式.svg` | `FIELDS` / `TITLE` / `SUB` / `FOOT` |
| `gen_storage_stack_svg.py` | `docs/images/存储栈分层.svg` | `L_*` / `ROWS` / `TITLE` / `SUB` / `FOOT` |
| `gen_partition_svg.py` | `docs/images/物理分区布局.svg` | `FIELDS` / `TITLE` / `SUB` / `FOOT`(MB 计量) |
| `gen_net_pending_svg.py` | `docs/images/Net层阻塞模型.svg` | `CALLER_STEPS` / `LOOP_STEPS` / `PHASES` |
| `gen_cfg_arch_svg.py` | `docs/images/配置文件分层架构.svg` | 配置引擎分层 |
| `gen_net_stack_svg.py` | `docs/images/BSD网络栈分层.svg` | BSD 网络栈分层 |
| `gen_memory_svg.py` | `docs/images/内存布局_*.svg` | RAM / FLASH 布局 |
---
+13 -30
View File
@@ -36,36 +36,16 @@ netTask 是**全部 socket 操作的唯一执行者**——CH395F 的 SPI 总线
核心思想:把"等数据"从**执行者内的自旋**改为**执行者外的登记**。
```
调用方任务 netTask(执行者) CH395F 芯片
────────── ───────────────── ────────────
net_recv(fd, buf, len, timeout)
│
├─ 数据已在?── 是 ──→ 立即 SPI 读出 ────────────────────→ GET_REMV_LEN/READ
│ 通知调用方(结果 = 字节数)
│
└─ 否,且 timeout>0
│
├─ 登记 pending 到 s_net_pending[fd] ← 占用执行者到此结束(微秒级)
└─ 在自己上下文睡任务通知 ⟵──────────────┐
│
┌────────────────┴─────────────────┐
│ netTask 主循环(~1ms 节拍) │
│ net_poll() ← 刷新 readable
│ net_pending_tick() ← 了结挂起
│ net_relisten_retry_tick() │
│ 排空消息队列 │
└──────────────────────────────────┘
│
┌──────────────────────────────────────────────────┘
│ 五路完成/取消判定(每轮对每个挂起 fd)
│ ① 数据到达 → SPI 读出,通知调用方(结果 = 字节数)
│ ② 到点 deadline → 通知 NET_ERR_TIMEDOUT
│ ③ EOF/DISCONNECT → 按 0(EOF) 完成 + auto-relisten + 通知
│ ④ 同 fd 后续操作 → 先行结算该 fd 的挂起,再执行新操作
│ ⑤ 网络重建 → 取消 + **必须通知**(否则调用方永久挂死)
└─→ xTaskNotify → 唤醒调用方
```
![Net 层阻塞模型 · pending 请求架构](../images/Net层阻塞模型.svg)
上图四层结构:
| 区域 | 内容 |
|---|---|
| **左栏 · 调用方任务** | `net_recv` → 判"数据已在?" → **是**走快速路径;**否**(`timeout > 0`)登记 pending 后在自己上下文睡任务通知 |
| **中栏 · netTask 主循环** | ~1ms 节拍:`net_poll`(刷新 readable)→ `net_pending_tick`(了结挂起)→ `net_relisten_retry_tick` → 排空消息队列 |
| **下带 · 五路判定** | 每轮对每个挂起 fd 按序判定:数据到达 / 到点 / EOF / 同 fd 后续操作 / 网络重建;含 1s 看门狗 |
| **回环** | 判定完成经 `xTaskNotify` 唤醒调用方(结果 = 字节数 / 负错误码) |
**关键性质**:
@@ -75,6 +55,9 @@ netTask 是**全部 socket 操作的唯一执行者**——CH395F 的 SPI 总线
- 公开 API 与调用契约**不变**——`net_recv` / `net_recv_to` 的签名、返回值、
超时语义均与改造前一致,既有调用方、测试与模拟器零改动。
> 图形由 `tools/arch_diagram/gen_net_pending_svg.py` 生成(改流程后重跑;见
> `docs/架构设计/图形绘制流程.md`)。
---
## 3. 数据结构