sdk: 同步主仓新增模块 cachegov/lazygrid/select/net 通道

This commit is contained in:
AskaEth committed 2026-09-03 05:09:05 +08:00
1 parent 0af6af7c7e
commit 64cde13c8d
15 files changed
+1326 -10

No files matched your search

+302
View File
@@ -0,0 +1,302 @@
/* sdk/meow_cachegov.c — 磁盘缓存 page-cache 治理 SDK 库(A:DONTNEED 主导侧)。
*
* 权威方案:devdocs/plan/OOM_PAGECACHE_GOVERNANCE.md。归属(ARCHITECTURE_PRINCIPLES ③混合):
* A 热路径(register/tick/drop_finished/refault)随 SDK 库;C(/proc/meminfo Cached 监控)
* 归 meow_svc_perf,本库只读其共享状态文件 MEOW_CACHEGOV_C_FILE 作为降载信号。
*
* 红线(务必保留本注释):**L2 禁止**——绝不丢 `[read_pos, ...)` 任何未解码区(含 read_pos 前方
* 刚写入尚未解码的页)。本库所有 drop 的上界只能是 read_pos-keep_hot(L1)或整文件曲终(L0)。
*
* 分层:只依赖 pthread + meow_log + POSIX(posix_fadvise),不依赖 core/App 其它 TU。
* 自含:refault 滚窗 + 三级回退(放大 keep_hot → 降 L0 → 关 A)。
*/
#include "include/meow_cachegov.h"
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* meow_ui.h:SDK 公共伞头(含 meow_log/meow_ui_log.h + 公共类型)。 */
#include "meow_ui.h"
/* 共享协议头:C 侧状态文件路径(meow_svc_perf 写入 / 各 App 读取,同源防漂移)。 */
#include "meow_ipc.h"
#define MEOW_CACHEGOV_MAXREG 8 /* 同时治理的缓存文件上限(ncm 单点播放,远小于此) */
#define MEOW_CACHEGOV_REFAULTRING 64 /* refault 滚窗环形缓冲 */
#define MEOW_CACHEGOV_WINDOW_MS 10000 /* refault 滚窗 10s */
#define MEOW_CACHEGOV_HOTESCALATE 1 /* 回退档>=1 时 keep_hot 放大倍数(翻倍) */
#define MEOW_CACHEGOV_DROP_THROTTLE (64u * 1024u) /* L1 每次至少推进 64KB 才再 drop(省 open/fadvise) */
typedef struct cg_entry {
char path[512];
uint64_t size; /* 标称大小(流式=nominal;命中=实际) */
uint64_t (*read_pos_fn)(void *ctx);
void *ctx;
uint64_t dropped_to; /* 已 DONTNEED 的已读前缀上界(只推进,不回退) */
int used;
} cg_entry_t;
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
static struct meow_cachegov_cfg g_cfg = {
.enabled = 1,
.cached_threshold_kb = 0, /* C 归属 svc_perf,本库只读 */
.dirty_warn_kb = 0, /* 同上 */
.keep_hot_bytes = (256u * 1024u), /* 保 256KB 解码窗口供倒带 */
.min_hot_ahead = (0), /* L2 红线防御值;绝不参与 drop */
.drop_mode = 0, /* 默认仅曲终(L0);L1 需显式开(带 seek 回退) */
.refault_backoff = 3, /* 10s 内 3 次 refault → 回退 */
};
static cg_entry_t g_reg[MEOW_CACHEGOV_MAXREG];
static int g_backoff = 0; /* 0=正常 1=keep_hot放大 2=降L0 3=A关 */
static int64_t g_last_step_ms = -1000000000000LL; /* 上次升/降档时刻(初始很远 → 首次可动作) */
static int64_t g_refault_ring[MEOW_CACHEGOV_REFAULTRING];
static int g_refault_head = 0;
static int g_refault_n = 0;
/* ---- 时钟(宿主测试桩可注入) ---- */
static int64_t g_test_now_ms = -1;
static int64_t now_ms(void)
{
if (g_test_now_ms >= 0) return g_test_now_ms;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
void meow_cachegov_test_set_time_ms(int64_t t) { g_test_now_ms = t; }
/* ---- C 侧状态文件(svc_perf 写 / 本库读) ---- */
static const char *c_file(void)
{
const char *e = getenv("MEOW_CACHEGOV_C_FILE");
return (e && e[0]) ? e : MEOW_CACHEGOV_C_FILE;
}
/* C 状态(svc_perf 写)最近一次解析缓存:下载 cb 每 4KB 块会查一次,避免每块都读文件(200ms TTL)。 */
static int g_cached_c_dl = 0, g_cached_c_pf = 0;
static int64_t g_cached_c_at = 0;
/* 读 C 状态文件(缺省 0=不降载)。解析失败/文件不存在 → 全部 0。 */
static void read_c_state(int *dl_pause, int *pf_pause, long *cached_kb, long *dirty_kb)
{
if (dl_pause) *dl_pause = 0;
if (pf_pause) *pf_pause = 0;
if (cached_kb) *cached_kb = 0;
if (dirty_kb) *dirty_kb = 0;
FILE *f = fopen(c_file(), "r");
if (!f) return;
char line[160];
while (fgets(line, sizeof line, f)) {
if (!strncmp(line, "cached_kb=", 10)) { if (cached_kb) *cached_kb = atol(line + 10); }
else if (!strncmp(line, "dirty_kb=", 9)) { if (dirty_kb) *dirty_kb = atol(line + 9); }
else if (!strncmp(line, "dl_pause=", 9)) { if (dl_pause) *dl_pause = atol(line + 9) ? 1 : 0; }
else if (!strncmp(line, "prefetch_pause=", 15)) { if (pf_pause) *pf_pause = atol(line + 15) ? 1 : 0; }
}
fclose(f);
}
/* ---- refault 滚窗 ---- */
void meow_cachegov_note_refault(void)
{
int64_t t = now_ms();
pthread_mutex_lock(&g_mu);
g_refault_ring[g_refault_head] = t;
g_refault_head = (g_refault_head + 1) % MEOW_CACHEGOV_REFAULTRING;
if (g_refault_n < MEOW_CACHEGOV_REFAULTRING) g_refault_n++;
pthread_mutex_unlock(&g_mu);
}
static int refaults_last_window(void)
{
int64_t now = now_ms();
int cnt = 0, start = (g_refault_head - g_refault_n + MEOW_CACHEGOV_REFAULTRING) %
MEOW_CACHEGOV_REFAULTRING;
for (int i = 0; i < g_refault_n; i++) {
int64_t t = g_refault_ring[(start + i) % MEOW_CACHEGOV_REFAULTRING];
if (t >= now - MEOW_CACHEGOV_WINDOW_MS) cnt++;
}
return cnt;
}
/* 曲终/cur_finished 后:整文件 L0 DONTNEED。 */
int meow_cachegov_drop_finished(const char *path)
{
if (!path) return -1;
pthread_mutex_lock(&g_mu);
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++) {
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) {
g_reg[i].used = 0;
break;
}
}
int lbf = g_backoff, en = g_cfg.enabled;
pthread_mutex_unlock(&g_mu);
if (!en || lbf >= 3) { /* A 已整体关闭:连曲终 L0 也不丢(§2.3 步骤 3) */
meow_log(MEOW_LOG_INFO, "[cachegov] drop_finished skip %s (A off, backoff=%d)", path, lbf);
return 0;
}
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
/* len=0 → 到 EOF(整文件)。曲终播放器已不再读该文件,重播=冷缓存首读(等价未命中)。 */
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
close(fd);
meow_log(MEOW_LOG_INFO, "[cachegov] drop_finished L0 %s", path);
return 0;
}
/* L1:丢已读前缀 [dropped_to, read_pos-keep_hot)。返回 1=有丢。 */
static int entry_drop_l1(cg_entry_t *e, size_t keep)
{
if (!e->read_pos_fn) return 0;
uint64_t pos = e->read_pos_fn(e->ctx);
if (pos <= keep) return 0; /* keep_hot 覆盖整个已读前缀,无需丢 */
uint64_t drop_end = pos - (uint64_t)keep; /* L1 上界:read_pos 前方绝不丢(L2 红线) */
if (drop_end <= e->dropped_to) return 0; /* 无推进(seek 回退 / 前进不足) */
if (drop_end - e->dropped_to < MEOW_CACHEGOV_DROP_THROTTLE && e->dropped_to != 0)
return 0; /* 节流:不足 64KB 不重复 open */
int fd = open(e->path, O_RDONLY);
if (fd < 0) return 0;
off_t off = (off_t)e->dropped_to;
off_t len = (off_t)(drop_end - e->dropped_to);
ssize_t r = posix_fadvise(fd, off, len, POSIX_FADV_DONTNEED);
close(fd);
if (r != 0) return 0;
e->dropped_to = drop_end;
meow_log(MEOW_LOG_INFO, "[cachegov] L1 drop %s off=%lld len=%lld (pos=%lld keep=%zu)",
e->path, (long long)off, (long long)len, (long long)pos, keep);
return 1;
}
int meow_cachegov_tick(void)
{
pthread_mutex_lock(&g_mu);
int64_t now = now_ms();
/* ① 回退评估(滚窗 10s):refault 超门限 → 升一档;滚窗干净 ≥10s → 降一档。
* 每升降一档需间隔 ≥10s(逐级回退,不因窗内持续 refault 一次跳过多档,防误回退)。 */
int cnt = refaults_last_window();
if (cnt >= g_cfg.refault_backoff && g_cfg.refault_backoff > 0 && g_backoff < 3 &&
(now - g_last_step_ms) >= MEOW_CACHEGOV_WINDOW_MS) {
g_backoff = g_backoff + 1;
g_last_step_ms = now;
meow_log(MEOW_LOG_INFO, "[cachegov] backoff -> %d (refault=%d/%lds)",
g_backoff, cnt, MEOW_CACHEGOV_WINDOW_MS / 1000);
} else if (cnt == 0 && g_backoff > 0 &&
(now - g_last_step_ms) >= MEOW_CACHEGOV_WINDOW_MS) {
g_backoff = g_backoff - 1;
g_last_step_ms = now;
meow_log(MEOW_LOG_INFO, "[cachegov] backoff -> %d (clean window)", g_backoff);
}
size_t keep = g_cfg.keep_hot_bytes;
if (g_backoff >= MEOW_CACHEGOV_HOTESCALATE) keep = g_cfg.keep_hot_bytes * 2; /* 档1:放大 keep_hot */
int l1_on = g_cfg.enabled && g_cfg.drop_mode >= 1 && g_backoff < 2; /* 档2:L1 关 */
int en = g_cfg.enabled;
/* ② L1 丢弃 */
int dropped = 0;
if (en && l1_on) {
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++)
if (g_reg[i].used) dropped += entry_drop_l1(&g_reg[i], keep);
}
pthread_mutex_unlock(&g_mu);
return dropped;
}
/* ---- 注册 / 注销 ---- */
int meow_cachegov_register(const char *path, uint64_t size,
uint64_t (*read_pos_fn)(void *ctx), void *ctx)
{
if (!path) return -1;
pthread_mutex_lock(&g_mu);
int slot = -1;
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++) {
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) { slot = i; break; }
if (!g_reg[i].used && slot < 0) slot = i;
}
if (slot < 0) { pthread_mutex_unlock(&g_mu); return -1; }
snprintf(g_reg[slot].path, sizeof g_reg[slot].path, "%s", path);
g_reg[slot].size = size;
g_reg[slot].read_pos_fn = read_pos_fn;
g_reg[slot].ctx = ctx;
g_reg[slot].dropped_to = 0;
g_reg[slot].used = 1;
pthread_mutex_unlock(&g_mu);
meow_log(MEOW_LOG_INFO, "[cachegov] register %s size=%llu", path, (unsigned long long)size);
return 0;
}
int meow_cachegov_unregister(const char *path)
{
if (!path) return -1;
pthread_mutex_lock(&g_mu);
int rc = -1;
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++)
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) { g_reg[i].used = 0; rc = 0; break; }
pthread_mutex_unlock(&g_mu);
return rc;
}
/* ---- 降载查询(A 回退 OR svc_perf C) ---- */
static void c_state(int *dl_pause, int *pf_pause)
{
int64_t now = now_ms();
pthread_mutex_lock(&g_mu);
if (g_cached_c_at == 0 || (now - g_cached_c_at) > 200) {
read_c_state(&g_cached_c_dl, &g_cached_c_pf, NULL, NULL);
g_cached_c_at = now;
}
if (dl_pause) *dl_pause = g_cached_c_dl;
if (pf_pause) *pf_pause = g_cached_c_pf;
pthread_mutex_unlock(&g_mu);
}
int meow_cachegov_download_paused(void)
{
pthread_mutex_lock(&g_mu);
int a = (g_backoff >= 2);
pthread_mutex_unlock(&g_mu);
int c = 0;
c_state(&c, NULL);
return (a || c) ? 1 : 0;
}
int meow_cachegov_prefetch_paused(void)
{
pthread_mutex_lock(&g_mu);
int a = (g_backoff >= 3);
pthread_mutex_unlock(&g_mu);
int c = 0;
c_state(NULL, &c);
return (a || c) ? 1 : 0;
}
/* ---- 配置 ---- */
int meow_cachegov_set_cfg(const struct meow_cachegov_cfg *cfg)
{
if (!cfg) return -1;
pthread_mutex_lock(&g_mu);
g_cfg = *cfg;
pthread_mutex_unlock(&g_mu);
return 0;
}
int meow_cachegov_get_cfg(struct meow_cachegov_cfg *cfg)
{
if (!cfg) return -1;
pthread_mutex_lock(&g_mu);
*cfg = g_cfg;
pthread_mutex_unlock(&g_mu);
return 0;
}
int meow_cachegov_backoff_level(void)
{
pthread_mutex_lock(&g_mu);
int v = g_backoff;
pthread_mutex_unlock(&g_mu);
return v;
}