sdk: 同步主仓新增模块 cachegov/lazygrid/select/net 通道
This commit is contained in:
1 parent
0af6af7c7e
commit
64cde13c8d
15 files changed
+1326
-10
No files matched your search
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user