L3 pending化一期:阻塞recv改挂起请求+主循环节拍完成(netTask零占用),lftpd/timeSync分片协议退役
This commit is contained in:
@@ -6,18 +6,17 @@
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* E-02 头阻塞修复(2026-09-22 M3 台架实证):
|
||||
* E-02 头阻塞修复 + L3 pending 化(2026-09-22,M3 台架实证):
|
||||
* netTask 是全部 socket 操作的唯一执行者;lftpd 原先用默认 30s 的**阻塞**
|
||||
* net_recv 等待对端下一行/下一块数据 → 只要客户端连上却不发完整行,netTask
|
||||
* 就被长期占用,其余任务(含远端显示)的网络操作整体冻结(实测一次空闲 FTP
|
||||
* 登录让 LCDMEM 整帧等待 17.7s)。
|
||||
* 修法:把等待切成 ≤LFTPD_NET_SLICE_MS 的片(每片 netTask 占用有界),
|
||||
* 调用方累计总预算 LFTPD_TOTAL_BUDGET_MS —— **总超时语义与旧实现一致**,
|
||||
* 但任何时刻都不再有"秒级冻结"。单片超时由 net_recv_to 返回 -2 区分(无 errno 竞态)。
|
||||
* 分片长度即"其他任务最坏等待":实测 100ms 片下受压帧仍达 ~750ms(多片排队),
|
||||
* 收到 5ms 后受压帧降到 ~10ms 量级(见 m3_data 修复后数据)。
|
||||
* 2026-09-22 先落地分片有界读(5ms 片;实测 100ms 片下受压帧仍达 ~750ms,
|
||||
* 收到 5ms 后降到 ~10ms 量级,见 m3_data 修复后数据),同日 L3 pending 化
|
||||
* 后改为"挂起请求":每次等待 = 一次挂起登记(netTask 零占用,完成按主循环
|
||||
* 节拍检查),调用方单次超时 = 剩余总预算(总等待 ≤ LFTPD_TOTAL_BUDGET_MS,
|
||||
* **总超时语义与旧实现一致**),旧分片协议退役。
|
||||
*/
|
||||
#define LFTPD_NET_SLICE_MS 5U
|
||||
#define LFTPD_TOTAL_BUDGET_MS 30000U
|
||||
|
||||
int lftpd_inet_listen(int port)
|
||||
@@ -120,25 +119,23 @@ int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len)
|
||||
int total_read_len = 0;
|
||||
|
||||
while (total_read_len < (int)buffer_len) {
|
||||
/* 分片有界读(E-02):单片 netTask 占用 ≤ LFTPD_NET_SLICE_MS */
|
||||
/* 单次预算 = 剩余总预算(L3 pending:等待期 netTask 零占用,调用方
|
||||
* 在自己上下文睡任务通知,旧片间 vTaskDelay(1) 让出退役) */
|
||||
TickType_t elapsed = xTaskGetTickCount() - t0;
|
||||
uint32_t rem = (elapsed >= (TickType_t)pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) ? 0U
|
||||
: (uint32_t)((TickType_t)LFTPD_TOTAL_BUDGET_MS - elapsed);
|
||||
if (rem == 0U) {
|
||||
return -1; /* 总预算耗尽(语义同旧 30s 超时) */
|
||||
}
|
||||
int read_len = net_recv_to(socket,
|
||||
buffer + total_read_len,
|
||||
(int)(buffer_len - total_read_len - 1),
|
||||
LFTPD_NET_SLICE_MS);
|
||||
rem);
|
||||
if (read_len == 0) {
|
||||
return -1; /* 对端关闭 */
|
||||
}
|
||||
if (read_len == -2) {
|
||||
/* 本片无数据:累计总预算,未超则继续等。
|
||||
* **必须显式让出 CPU**:若某片因 net_poll 自身耗时而在首轮就返回 -2,
|
||||
* 本任务(Normal)立即重排队会形成忙循环 → 饿死 BelowNormal 的 uiTask
|
||||
* → 远端显示请求数秒无人拾取(2026-09-22 M3 实测 2.4~3.4s 周期尖刺)。
|
||||
* vTaskDelay(1) 保证低优先级任务每毫秒都有运行机会。 */
|
||||
if ((xTaskGetTickCount() - t0) >= pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) {
|
||||
return -1; /* 总预算耗尽(语义同旧 30s 超时) */
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
continue;
|
||||
continue; /* 本次预算等满无数据 → 下轮 rem=0 退出 */
|
||||
}
|
||||
if (read_len < 0) {
|
||||
return read_len; /* 真错误 */
|
||||
@@ -200,22 +197,25 @@ int lftpd_inet_write(int socket, const void *buf, int len)
|
||||
|
||||
int lftpd_inet_read(int socket, void *buf, int len)
|
||||
{
|
||||
/* 分片有界读(E-02):总预算 30s 不变,但每片 netTask 占用 ≤ LFTPD_NET_SLICE_MS,
|
||||
* 避免空闲数据连接(客户端开了 PASV 却不发数据)冻结全网 */
|
||||
/* L3 pending 读:总预算 30s 不变,等待期 netTask 零占用
|
||||
* (空闲数据连接不再冻结全网;旧分片协议随 L3 pending 化退役) */
|
||||
TickType_t t0 = xTaskGetTickCount();
|
||||
|
||||
for (;;) {
|
||||
int n = net_recv_to(socket, buf, len, LFTPD_NET_SLICE_MS);
|
||||
TickType_t elapsed = xTaskGetTickCount() - t0;
|
||||
uint32_t rem = (elapsed >= (TickType_t)pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) ? 0U
|
||||
: (uint32_t)((TickType_t)LFTPD_TOTAL_BUDGET_MS - elapsed);
|
||||
if (rem == 0U) {
|
||||
return -1; /* 总预算耗尽(语义同旧 30s 超时) */
|
||||
}
|
||||
int n = net_recv_to(socket, buf, len, rem);
|
||||
if (n >= 0) {
|
||||
return n; /* >0 数据 / 0 EOF */
|
||||
}
|
||||
if (n == -1) {
|
||||
return -1; /* 真错误 */
|
||||
}
|
||||
if ((xTaskGetTickCount() - t0) >= pdMS_TO_TICKS(LFTPD_TOTAL_BUDGET_MS)) {
|
||||
return -1; /* 总预算耗尽(语义同旧 30s 超时) */
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1)); /* 让出 CPU:防空闲时忙循环饿死低优先级任务 */
|
||||
/* n == -2:本次预算等满无数据 → 下轮 rem=0 退出 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user