net/ui: UI 推流加就绪门控,刷新脱离 netTask 往返(销项 N-10)

新增 net_can_accept() / net_can_read():纯 RAM 读 Socket 控制块,不入队、
不等 netTask 回执,供实时性敏感任务在发 RPC 前判断"有没有活"。
ui_net_sink_poll() 改为先门控再 RPC;无客户端时 RPC 从 100 msg/s 降到 0。

两条兜底(门控引入的新卡死路径,设计阶段未预见):
- I-2 每 32 轮无条件 recv:readable 仅由 RECV_OK 中断置位,Trap 22 三类
  对账不含"RX 非空但 readable=0",纯门控会把已到达的数据永久藏住;
- I-3 门控返 0 时查监听控制块,消失即 g_listen=-1 重建(改前靠每轮
  accept 撞 BADF 暴露,门控会连这条暴露路径一起消掉)。
net_can_read() 对非 ESTABLISHED / 非法 fd 故意返 1,让上层必得 EOF/BADF
走既有错误分支——返 0 会把死连接永久藏住。
模拟器后端恒返 1(Winsock 无 RPC 成本,行为逐位不变)。

实测:uis 50ms→1µs、uiw 50ms→11.4ms(只剩 LVGL);@build 0 错 0 警、
@test 10/10 PASS(uis≤1µs、uisn≡0、0 task stall、0 [ERR])、
check_remo_sink.py e2e 0/25600 位差异、内容页文本断言 50/50。
uip 最坏 61ms 是 LVGL 帧周期地板、与网络无关,判据已按实测修订。

方案 B(把推流搬出 uiTask + 帧 seqlock)已评估并决定不做:搬任务不会
让刷新变快,收益只剩极端并发下的观感隔离,成本含必改 .ioc 的 CubeMX
重生成。残留暴露与观测手段(uis/uisn)如实记录在 §9.4。
N-19 / C7 不入登记簿(当日结案),结论沉淀进问题记录 §9。
This commit is contained in:
2026-10-10 20:47:59 +08:00
parent 35b5b78209
commit 1d1a9d4656
10 changed files with 382 additions and 11 deletions
+11 -4
View File
@@ -40,13 +40,20 @@ extern "C" {
int ui_net_sink_start(uint16_t port);
/*
* 函数功能:非阻塞轮询 RemoDispBus 协议
* 函数功能:轮询 RemoDispBus 协议(接受连接、收帧、逐字节解析)
* 入口参数:无
* 返回值:无
* 限定条件:ui_net_sink_start() 已成功调用
* 函数说明:1. 无客户端时尝试接受连接,成功即上报 INIT
* 2. 有客户端时用 DONTWAIT 收帧并解析(KEEPLIVE/INIT/KEY/LCDMEM)
* 3. 对端关闭或异常时复位客户端,等待下次重监听,不阻塞 LVGL 刷新
* 函数说明:1. 无客户端时先门控(net_can_accept,纯 RAM 读)再尝试接受连接
* 2. 有客户端时先门控(net_can_read)再用 DONTWAIT 收帧并解析
* (KEEPLIVE/INIT/KEY/LCDMEM);门控判为无数据时仍每 32 轮兜底收一次
* 3. 对端关闭或异常时复位客户端,等待下次重监听
* ⚠ **不是"零成本非阻塞"**:门控只消除无谓往返;真有活时仍是
* "入队 + 等 netTask 回执"(osMessageQueuePut(osWaitForever) +
* xTaskNotifyWait(portMAX_DELAY),均无上界;LCDMEM 应答 3204 B 最坏 ≈4 s)
* → 会拖住宿主(uiTask)的刷新;"搬出 uiTask"已完整评估并**决定不做**
* (实测刷新最坏周期 = LVGL 帧周期地板,与网络无关),见
* docs/问题记录/UI任务看门狗停摆分析.md §9.4
*/
void ui_net_sink_poll(void);