L3 pending化一期:阻塞recv改挂起请求+主循环节拍完成(netTask零占用),lftpd/timeSync分片协议退役

This commit is contained in:
2026-09-22 16:29:31 +08:00
parent 4ce966dfb8
commit a45c0cb883
3 changed files with 372 additions and 109 deletions
+22 -24
View File
@@ -25,16 +25,15 @@
#define TIME_SYNC_PORT 8888
/*
* E-02 头阻塞修复(timeSync 侧;lftpd 侧见 lftpd_inet.c):
* E-02 头阻塞修复 + L3 pending 化(timeSync 侧;lftpd 侧见 lftpd_inet.c):
* 旧实现用单次 30s 阻塞 net_recv(默认 NET_RECV_TIMEOUT_MS)等时间包——等待期间
* netTask(全部 socket 操作的唯一执行者)被独占,其余任务的网络操作(远端显示 /
* FTP)整体停摆(docs/问题记录/错误码传播审计.md E-02)。
* 修法:等待切成 ≤TIME_SYNC_NET_SLICE_MS 的片(与 lftpd 同片长 = 其他任务最坏
* 等待,见 docs/测试说明/链路延迟测试说明.md §8.3),调用方累计总预算
* TIME_SYNC_TOTAL_BUDGET_MS——总超时语义与旧 30s 一致,任何时刻 netTask 占用
* 有界;片间 vTaskDelay(1) 让出 CPU,防空闲忙循环饿死低优先级任务。
* 2026-09-22 先落地分片有界读(5ms 片),同日 L3 pending 化后改为"挂起请求":
* 每次等待 = 一次挂起登记(netTask 零占用,完成按主循环节拍检查),调用方
* 单次超时 = 剩余总预算(总等待 ≤ TIME_SYNC_TOTAL_BUDGET_MS,语义与旧 30s
* 一致);等待期间调用方在自己上下文睡任务通知,旧分片协议(片长/片间让出)退役。
*/
#define TIME_SYNC_NET_SLICE_MS 5U
#define TIME_SYNC_TOTAL_BUDGET_MS 30000U
/*
@@ -88,10 +87,10 @@ static int time_sync_open_listener(void) {
* 函数功能:处理一个已建立的时间同步连接
* 入口参数:conn_sock - 已建立的连接 Socket 描述符(单连接模式下即监听 Socket 自身)
* 返回值:无
* 函数说明:分片有界读取 PC 发来的时间包("TIME"+uint32 大端 本地时间),解析后
* 写回 RTC,然后关闭连接。读取用 net_recv_to 5ms 分片(E-02):单片不
* 长期占用 netTask,总预算 30s 与旧实现语义一致;跨片累积,容忍时间包
* 被 TCP 分段送达
* 函数说明:pending 读取 PC 发来的时间包("TIME"+uint32 大端 本地时间),解析后
* 写回 RTC,然后关闭连接。每次等待 = 一次挂起请求(netTask 零占用,
* L3 pending 化),单次超时 = 剩余总预算(≤30s,语义与旧 30s 阻塞一致);
* 跨调用累积,容忍时间包被 TCP 分段送达
*/
static void time_sync_handle_conn(int conn_sock) {
uint8_t buf[16];
@@ -99,9 +98,16 @@ static void time_sync_handle_conn(int conn_sock) {
TickType_t t0 = xTaskGetTickCount();
while (got < (int)sizeof(buf)) {
/* 分片有界读(E-02):单片 netTask 占用 ≤ TIME_SYNC_NET_SLICE_MS */
int n = net_recv_to(conn_sock, buf + got, (int)(sizeof(buf) - got),
TIME_SYNC_NET_SLICE_MS);
/* 单次预算 = 剩余总预算(L3 pending:等待期 netTask 零占用,调用方
* 在自己上下文睡任务通知,旧片间 vTaskDelay(1) 让出退役) */
TickType_t elapsed = xTaskGetTickCount() - t0;
uint32_t rem = (elapsed >= (TickType_t)pdMS_TO_TICKS(TIME_SYNC_TOTAL_BUDGET_MS)) ? 0U
: (uint32_t)((TickType_t)TIME_SYNC_TOTAL_BUDGET_MS - elapsed);
if (rem == 0U) {
DBG_INFO("time_sync: no TIME packet in 30s, drop conn");
break; /* 总预算耗尽(语义同旧 30s 超时) */
}
int n = net_recv_to(conn_sock, buf + got, (int)(sizeof(buf) - got), rem);
if (n > 0) {
got += n;
if (got >= 8) {
@@ -112,18 +118,10 @@ static void time_sync_handle_conn(int conn_sock) {
if (n == 0) {
break; /* 对端关闭(EOF) */
}
if (n != -2) {
break; /* 真错误 */
if (n == -2) {
continue; /* 本次预算等满无数据 → 下轮 rem=0 退出 */
}
/* 本片超时无数据:总预算未耗尽则继续等。
* **必须显式让出 CPU**:若某片因 net_poll 自身耗时在首轮就返回 -2,
* 本任务(Normal)立即重排队会形成忙循环 → 饿死 BelowNormal 的 uiTask
* (lftpd 侧同因,见链路延迟测试说明.md §8.3 第 3 条) */
if ((xTaskGetTickCount() - t0) >= pdMS_TO_TICKS(TIME_SYNC_TOTAL_BUDGET_MS)) {
DBG_INFO("time_sync: no TIME packet in 30s, drop conn");
break; /* 总预算耗尽(语义同旧 30s 超时) */
}
vTaskDelay(pdMS_TO_TICKS(1));
break; /* 真错误 */
}
if (got >= 8) {