Workingset 检测机制与 Refault 分析
Workingset 检测机制与 Refault 分析一、双 CLOCK 链表每个 NUMA 节点维护 inactive 和 active 两个链表fault ────────────────────── | -------------- | ------------- reclaim - | inactive | --- 降级 | active | -- -------------- ------------- | | | -------------- 提升 -----------------------新 fault 页面从 inactive 头部插入回收从 inactive 尾部扫描在 inactive 上被再次访问 → 提升到 activeactive 太大 → 降级回 inactiveshrink_active_list二、Refault 距离核心思想精确追踪每个页面的访问频率代价太高但可以通过 refault 距离做合理近似。nonresident_age 计数器每个 lruvec 维护一个单调递增计数器lruvec-nonresident_age。以下事件会推进这个计数器页面从 inactive list 被驱逐时nr_pages在workingset_eviction里页面从 inactive list 被激活时nr_pages在workingset_age_nonresident里本质上这个计数器记录的是有多少个 inactive 页面被访问过无论是驱逐还是激活。E 和 R 的具体含义EEviction snapshot页面被驱逐那一刻的nonresident_age值。// mm/workingset.c:411 (workingset_eviction)evictionatomic_long_read(lruvec-nonresident_age);// ← 这就是 Eworkingset_age_nonresident(lruvec,folio_nr_pages(folio));returnpack_shadow(...,eviction,...);// E 编码进 shadow entryRRefault snapshot页面 refault 回来那一刻的nonresident_age值。// mm/workingset.c:493 (workingset_test_recent)refaultatomic_long_read(eviction_lruvec-nonresident_age);// ← 这就是 R距离计算// mm/workingset.c:511refault_distance(refault-eviction)EVICTION_MASK;// R - ER - E的物理含义从页面被驱逐到页面 refault 回来这段时间内有多少个 inactive 页面被访问过驱逐 激活的总和。这个数字近似于如果 inactive list 足够长需要多长才能把这个页面留住。判定逻辑// mm/workingset.c:521-536workingset_sizeNR_ACTIVE_FILE;if(!file)workingset_sizeNR_INACTIVE_FILE;if(has_swap)workingset_sizeNR_ACTIVE_ANON(file?NR_INACTIVE_ANON:0);returnrefault_distanceworkingset_size;// (R-E) ≤ 可用内存距离 ≤ workingset_size含义如果 inactive list 能再大 (R-E) 个槽位这个页面就不会被驱逐而系统中确实有这么多页面active 页面占着的空间可以腾出来结论值得激活与 active 页面竞争空间操作直接放 active list距离 workingset_size含义就算把全部可用内存都当 inactive list也留不住这个页面结论访问间隔太长激活没意义操作当普通 fault 处理放 inactive list 头部举例内存: inactive 4 页, active 4 页, 共 8 页 nonresident_age 当前值 200 时刻 T1: 页面 P 被驱逐 E atomic_long_read(lruvec-nonresident_age) 200 shadow entry 里存 E200 ...期间其他 5 个 inactive 页面被驱逐或激活... nonresident_age 变成 205 时刻 T2: 页面 P refault 回来 R atomic_long_read(lruvec-nonresident_age) 205 refault_distance R - E 205 - 200 5 workingset_size NR_ACTIVE_FILE 4假设无 swap 5 ≤ 4? → 否 → 不激活P 放 inactive ──────────────── 另一个页面 Q: E 200, R 203 refault_distance 3 3 ≤ 4? → 是 → 激活Q 放 active三、Workingset 标记降级时打标记shrink_active_list将页面从 active 降级到 inactive 时folio_clear_active(folio);// 清掉 active 标志folio_set_workingset(folio);// 打标记曾在 active list 上list_add(folio-lru,l_inactive);驱逐时编码页面被驱逐时workingset 标记连同 eviction count 一起编码进 shadow entry。Refault 时区分路径refault 时从 shadow entry 解码出 workingset 标记用于区分两种 refault 类型。完整代码流程shrink_active_list() [mm/vmscan.c:2068] │ ├─ isolate_lru_folios() 从 active LRU 取出一批页面到 l_hold │ ├─ 遍历 l_hold 中每个 folio: │ │ │ ├─ folio_referenced() 检查是否被访问过 │ │ │ │ │ ├─ 有引用 VM_EXEC file页 → 保留 activerotate │ │ │ list_add(folio-lru, l_active) │ │ │ │ │ └─ 其他情况 → 降级到 inactive │ │ folio_clear_active(folio) ← 清 PG_active │ │ folio_set_workingset(folio) ← 设 PG_workingset │ │ list_add(folio-lru, l_inactive) │ │ │ └─ !folio_evictable → folio_putback_lru (unevictable) │ ├─ move_folios_to_lru(l_active) → 放回 active LRUPG_active1 ├─ move_folios_to_lru(l_inactive) → 放回 inactive LRUPG_active0 │ │ move_folios_to_lru 内部: │ lruvec_add_folio(lruvec, folio) │ → folio_lru_list(folio) 根据 PG_active/PG_swapbacked 选 LRU │ → list_add 到对应 lruvec-lists[lru] │ └─ 统计 PGDEACTIVATE, 更新 NR_ISOLATED驱逐与 Shadow Entry 编码流程shrink_inactive_list() [mm/vmscan.c:1986] │ ├─ isolate_lru_folios() 从 inactive LRU 取出页面 │ └─ shrink_folio_list() [mm/vmscan.c:1209] │ └─ 对每个页面尝试回收 │ ├─ 文件页且 clean → __remove_mapping() │ │ │ └─ workingset_eviction(folio, target_memcg) [mm/workingset.c] │ │ │ ├─ 读取 folio_test_workingset(folio) → 得到 workingset 标记 │ ├─ 读取 nonresident_age 计数器作为 eviction count │ ├─ 将 (workingset | node | memcg_id | eviction) 编码 │ └─ 写入 page cache 的 shadow entry (xarray slot) │ └─ 匿名页 → swap out → shadow entry 存入 swap cacheRefault 解码与激活流程filemap_fault() / do_swap_page() │ └─ 发现 shadow entry曾被驱逐的页面 │ └─ workingset_refault(folio, shadow) [mm/workingset.c:555] │ ├─ 解码 shadow entry → 得到 eviction count workingset 标记 │ ├─ WORKINGSET_REFAULT [所有 refault 都计] │ ├─ workingset_test_recent(shadow, ...) │ │ │ ├─ 计算 refault 距离 (R - E) │ ├─ 比较距离 vs 可用内存 (NR_inactive NR_active) │ │ │ ├─ 距离 可用内存 → return false → goto out不激活 │ └─ 距离 ≤ 可用内存 → return true 传出 workingset 值 │ ├─ folio_set_active(folio) [放入 active list] ├─ WORKINGSET_ACTIVATE │ └─ if (workingset true): [曾在 active 上] ├─ folio_set_workingset(folio) [保留标记供下次使用] ├─ lru_note_cost_refault(folio) [增加回收代价权重] └─ WORKINGSET_RESTORE四、Shadow Entry是什么Shadow entry 是页面被驱逐后留在原来位置上的墓碑标记。文件页在 page cache 里的存储结构是 xarraymapping-i_pages索引是文件偏移量。正常 slot 存 folio 指针驱逐后改存一个特殊的非指针值mapping-i_pages (xarray): offset 0: [folio *] ← 还在内存中 offset 1: [shadow entry] ← 页面被驱逐了留个墓碑 offset 2: [folio *] offset 3: [shadow entry]编码格式通过pack_shadow()把信息压缩进一个unsigned long┌──────────────────────────────────────────────────────────────┐ │ eviction_count │ memcg_id │ node_id │ workingset(1bit) │ tag │ └──────────────────────────────────────────────────────────────┘ 一个 unsigned long (64位)// mm/workingset.c:202staticvoid*pack_shadow(intmemcgid,pg_data_t*pgdat,unsignedlongeviction,bool workingset,bool file){evictionfile?EVICTION_MASK:EVICTION_MASK_ANON;eviction(evictionMEM_CGROUP_ID_SHIFT)|memcgid;eviction(evictionNODES_SHIFT)|pgdat-node_id;eviction(evictionWORKINGSET_SHIFT)|workingset;returnxa_mk_value(eviction);}各字段含义eviction_count驱逐时 nonresident_age 的快照用于计算 refault 距离memcg_id属于哪个 memory cgroupnode_id在哪个 NUMA 节点被驱逐workingset驱逐前是否在 active list 上待过1 bittagxarray 用最低位标识这是 value 不是指针xa_mk_value什么时候写入shrink_folio_list() → __remove_mapping() → workingset_eviction(folio) → pack_shadow(...) → 存入 xarray slot什么时候读取下次访问同一文件偏移时filemap_fault在 xarray 里找到 shadow entry// mm/filemap.centry__filemap_get_folio(mapping,index,...);if(xa_is_value(entry)){// 这是 shadow entry不是真实页面// 分配新页面读入数据// 调 workingset_refault(folio, shadow) 判断是否激活}为什么不直接清空 slot如果驱逐时直接清空refault 时就不知道这个页面曾经来过——无法计算 refault 距离无法做 workingset 判定。Shadow entry 就是为了在页面离开内存后保留这一点历史信息。生命周期页面 fault 进入 → xarray slot 存 folio 指针 页面被驱逐 → slot 改存 shadow entry墓碑 页面 refault 回来 → 读 shadow entry 做判定然后替换为新 folio 指针 shadow entry 过多时 → shadow entry shrinker 清理过期条目避免内存浪费五、PG_workingset 与 PG_active 标志位流程图标志位含义PG_active页面当前在 active list 上受保护不会被直接回收PG_workingset页面曾经在 active list 上待过历史标记用于 refault 判定完整生命周期流转含 referenced 判定细节首次 fault │ ▼ ┌───────────────────────┐ ┌──▶│ Inactive List │ │ │ PG_active0 │ │ │ PG_workingset0 │ │ │ PG_referenced0 │ │ └───────────┬────────────┘ │ │ │ ▼ │ shrink_folio_list() 扫描 inactive 尾部 │ │ │ ▼ │ folio_check_references(folio, sc) [vmscan.c:860] │ │ │ ├─ folio_referenced(folio, 1, memcg, vm_flags) │ │ 扫描所有映射该页的 PTE 的 accessed bit │ │ 返回 referenced_ptes 数量收集 vm_flags │ │ │ ├─ VM_LOCKED → FOLIOREF_ACTIVATE移到 unevictable │ │ │ ├─ referenced_ptes 0: │ │ │ │ │ ├─ folio_set_referenced(folio) ← 设 PG_referenced │ │ │ │ │ ├─ PG_referenced 之前已设 OR pte引用1: │ │ │ → FOLIOREF_ACTIVATE两次访问确认 → 提升 │ │ │ │ │ ├─ VM_EXEC file 页: │ │ │ → FOLIOREF_ACTIVATE可执行代码优先提升 │ │ │ │ │ └─ 其他: → FOLIOREF_KEEP再给一轮机会 │ │ │ └─ referenced_ptes 0: │ ├─ PG_referenced file页 → FOLIOREF_RECLAIM_CLEAN │ └─ 其他 → FOLIOREF_RECLAIM可以回收 │ │ 根据返回值: │ │ │ ├─ FOLIOREF_ACTIVATE → 提升到 Active List │ │ folio_set_active(folio) PG_active1 │ │ ┌─────────────────────┐ │ │ │ Active List │ │ │ │ PG_active1 │ │ │ └──────────┬────────────┘ │ │ │ (见下方 shrink_active_list) │ │ ▼ │ │ │ ├─ FOLIOREF_KEEP → 留在 inactiverotate 到头部 │ │ stat-nr_ref_keep │ │ (下轮再扫时如果还有引用 → 双重确认 → 提升) │ │ │ ├─ FOLIOREF_RECLAIM → 回收 │ │ │ │ │ ▼ │ │ __remove_mapping() → workingset_eviction() │ │ pack_shadow(workingsetPG_workingset的值, Enonresident_age) │ │ 存入 xarray → [Shadow Entry] │ │ │ └─ FOLIOREF_RECLAIM_CLEAN → 仅回收 clean 页 │ │ │ ════════════════════════════════════════════ │ shrink_active_list() 降级流程 [vmscan.c:2068] │ ════════════════════════════════════════════ │ │ 从 Active List 取出一批页面逐个判断: │ │ │ ▼ │ folio_referenced(folio, 0, memcg, vm_flags) │ (注意第2个参数0不清除 PTE accessed bit) │ │ │ ├─ 返回 ! 0有引用: │ │ │ │ │ ├─ VM_EXEC file 页: │ │ │ → rotate 回 Active可执行代码额外保护 │ │ │ list_add(folio-lru, l_active) │ │ │ PG_active 保持1 │ │ │ │ │ └─ 其他有引用: │ │ → 依然降级active list 只给 exec 特殊待遇 │ │ ↓ 落入下方降级路径 │ │ │ └─ 返回 0无引用或非 exec 有引用: │ → 降级到 Inactive │ folio_clear_active(folio) PG_active: 1→0 │ folio_set_workingset(folio) PG_workingset: 0→1 │ list_add(folio-lru, l_inactive) │ │ │ ▼ │ ┌───────────────────────┐ │ │ Inactive List │ │ │ PG_active0 │ │ │ PG_workingset1 ← 新! │ │ └───────────┬────────────┘ │ │ │ ▼ │ (再次被 shrink_folio_list 扫描流程同上) │ 如果被回收: │ workingset_eviction() │ pack_shadow(workingset1, Enonresident_age) │ │ │ ▼ │ [Shadow Entry] │ workingset1 │ │ │ ▼ refault │ workingset_refault() │ │ │ 距离 ≤ 可用内存? │ ├─ 否 → 放 inactivePG_active0, PG_ws0 │ └─ 是: │ folio_set_active PG_active1 │ WORKINGSET_ACTIVATE │ workingset1? │ ├─ 否 → 仅 ACTIVATE │ └─ 是: │ folio_set_workingset PG_ws1 │ lru_note_cost_refault │ WORKINGSET_RESTORE │ → 放 Active List └────────────────────────── (循环)referenced 判定的两次确认机制在 inactive list 上的页面不会因为一次访问就提升到 active。需要两次确认才能提升防止 streaming IO 的一次性页面污染 active list第一次扫描: folio_referenced() 发现 PTE accessed1 → folio_set_referenced(folio) 设 PG_referenced 标记 → 返回 FOLIOREF_KEEP再给一次机会留在 inactive 第二次扫描: folio_referenced() 又发现 PTE accessed1 → referenced_ptes 0 且 referenced_folio1PG_referenced 已设 → 双重确认真的被反复访问 → 返回 FOLIOREF_ACTIVATE → 提升到 Active例外VM_EXEC的文件页代码段一次访问就提升代码执行比普通文件更值得保护多个 PTE 同时引用referenced_ptes 1一次扫描就提升共享页面更重要folio_mark_accessed不经过页表的访问标记除了folio_referenced()扫描 PTE accessed bit内核还有另一条路径标记页面访问folio_mark_accessed()。这是软件层面主动调用的不依赖硬件页表 accessed bit。调用场景文件读取路径: read() → filemap_read() → folio_mark_accessed() 文件 mmap fault: filemap_fault() → folio_mark_accessed() shmem 访问: shmem_get_folio() → folio_mark_accessed() GUP (get_user_pages): follow_page → folio_mark_accessed() splice / sendfile: splice_read → folio_mark_accessed()状态转换逻辑// mm/swap.c:496voidfolio_mark_accessed(structfolio*folio)folio_mark_accessed() 的状态机 ┌─────────────────────────┬──────────────────────────────────────┐ │ 当前状态 │ 动作 │ ├─────────────────────────┼──────────────────────────────────────┤ │ inactive, PG_ref0 │ 设 PG_referenced │ │ (第一次访问) │ → inactive, PG_ref1 │ ├─────────────────────────┼──────────────────────────────────────┤ │ inactive, PG_ref1 │ 立即激活! folio_activate() │ │ (第二次访问) │ 清 PG_referenced │ │ │ 调 workingset_activation() │ │ │ → active, PG_ref0 │ ├─────────────────────────┼──────────────────────────────────────┤ │ active, PG_ref0 │ 设 PG_referenced │ │ │ → active, PG_ref1 │ ├─────────────────────────┼──────────────────────────────────────┤ │ active, PG_ref1 │ 无变化已经在 active 且有引用 │ └─────────────────────────┴──────────────────────────────────────┘与 folio_referenced 的区别folio_referenced()被动检测回收时调用: - 扫描所有映射该页的 PTE 的 hardware accessed bit - 清除 accessed bit为下一轮检测做准备 - 由 shrink_folio_list / shrink_active_list 在回收扫描时调用 - 适用于有页表映射的页面mmap 的文件页、匿名页 folio_mark_accessed()主动标记访问时调用: - 软件层面直接设置 PG_referenced 或激活页面 - 不涉及硬件页表 - 由内核代码在访问页面时主动调用 - 适用于通过 read/write 系统调用访问的文件页不经过 mmap无 PTE关键点两条路径共用同一个两次确认机制无论是folio_mark_accessed还是folio_referenced都操作PG_referenced标志场景 1: read() 访问文件页无 mmap 第一次 read() → folio_mark_accessed() → 设 PG_referenced 第二次 read() → folio_mark_accessed() → PG_ref 已设 → 立即激活 场景 2: mmap 访问文件页 用户访问 → 硬件设 PTE accessed bit 回收扫描 → folio_referenced() 发现 accessed → 设 PG_referencedKEEP 再次访问 → 硬件再设 accessed bit 再次扫描 → folio_referenced() PG_referenced 已设 → ACTIVATE 场景 3: 混合路径 read() → folio_mark_accessed() → 设 PG_referenced 之后回收扫描 → folio_referenced() 发现 PTE accessed PG_ref 已设 → referenced_folio1 referenced_ptes0 → ACTIVATE标志位状态转换表事件 PG_active PG_workingset 所在位置 ───────────────────────────────────────────────────────────────────── 首次 fault 0 0 Inactive 在 inactive 被再次访问(提升) 1 0 Active shrink_active_list 降级 0 1 Inactive 在 inactive 被再次访问(提升) 1 1 Active 再次被降级 0 1 Inactive 被驱逐 - - Shadow Entry (ws1) refault 距离够短 1 1 Active (RESTORE) refault 距离太远 0 0 Inactive (重置)关键区分Shadow Entry workingset0 refault 且距离够短: → ACTIVATE工作集可能在扩张/迁移 → 给一次机会进 active 竞争 Shadow Entry workingset1 refault 且距离够短: → ACTIVATE RESTORE工作集在 thrashing → 进 active 额外增加回收代价lru_note_cost_refault → 告诉系统不是新页面在竞争是老页面被反复踢出来四、两种 Refault 路径路径 A从未上过 activeInactive Refaultfault → inactive → 驱逐 → refault shadow entry: workingset0含义冷页面再次被需要工作集可能在切换/扩张。路径 B曾在 active 上Active Refaultfault → inactive → active → 降级回 inactive → 驱逐 → refault shadow entry: workingset1含义热页面被挤出去了内存不够撑住当前工作集现有工作集在 thrashing。五、三个计数器代码流程// mm/workingset.c: workingset_refault()mod_lruvec_state(lruvec,WORKINGSET_REFAULT_BASEfile,nr);// 所有 refaultif(!workingset_test_recent(shadow,file,workingset,true))gotoout;// 距离太远不激活folio_set_active(folio);mod_lruvec_state(lruvec,WORKINGSET_ACTIVATE_BASEfile,nr);// 距离够短激活if(workingset){// 曾在 active 上folio_set_workingset(folio);lru_note_cost_refault(folio);mod_lruvec_state(lruvec,WORKINGSET_RESTORE_BASEfile,nr);// 热页面恢复}层级关系所有 refault (有 shadow entry 的缺页) │ ├── 距离太远不激活放 inactive → 仅 REFAULT │ └── 距离够短激活放 active → REFAULT ACTIVATE │ ├── workingset0路径 A 的页面 → 仅 ACTIVATE │ 含义新页面 refault工作集在迁移 │ └── workingset1路径 B 的页面 → ACTIVATE RESTORE 含义热页面被恢复工作集在 thrashing严格子集关系REFAULT ⊇ ACTIVATE ⊇ RESTORE六、实际数据分析数据workingset_refault_anon 33 workingset_refault_file 685014 workingset_activate_anon 32 workingset_activate_file 685008 workingset_restore_anon 0 workingset_restore_file 634385解读计算值含义activate/refault685008/685014 99.99%几乎所有 refault 距离都够短restore/activate634385/685008 92.6%绝大部分是曾经的热页面纯 activate路径 A685008 - 634385 50623新页面 refault 被激活未激活的 refault685014 - 685008 6极少有距离太远的诊断结论典型的内存不足 工作集 thrashing同一批热页面反复被降级 → 驱逐 → refault → 激活 → 降级…inactive list 周转极快页面刚进去就被推到尾部赶出去文件缓存严重不足活跃的文件工作集塞不进内存七、三种典型模式对比Thrashing内存不足健康工作集切换activate/refault~100%~5%~90%restore/activate~90%~20%~10%特征同一批热页面反复颠簸偶尔冷页面 refault大部分驱逐是合理的新页面大量涌入旧工作集退出诊断内存不够撑住工作集正常运行负载变化短暂过渡健康系统示例workingset_refault_file 100000 workingset_activate_file 5000 (5%) workingset_restore_file 1000 (1%)95% 的 refault 距离超过可用内存 —— 说明这些页面两次访问间隔很久驱逐它们是正确的决策。工作集切换示例workingset_refault_file 200000 workingset_activate_file 180000 (90%) workingset_restore_file 20000 (10%)activate 高但 restore 低 —— 大量 refault 被激活但没上过 active list路径 A说明一批新页面在密集涌入不是同一批页面在颠簸。

相关新闻

UE4逆向实战:快速定位UWorld与GObjectArray的核心技巧

UE4逆向实战:快速定位UWorld与GObjectArray的核心技巧

1. 项目概述:为什么我们需要快速定位UWorld和GObjectArray?在UE4游戏安全、外挂对抗、Mod开发甚至是引擎深度定制这些领域里,逆向分析是绕不开的基本功。你可能会为了分析一个游戏的内存结构,或者想给某个游戏功能打上自己的补丁&…

2026/8/3 6:27:05 阅读更多 →
虚拟电厂微网调度:蒙特卡洛与随机优化实战解析

虚拟电厂微网调度:蒙特卡洛与随机优化实战解析

1. 项目背景与核心挑战虚拟电厂作为分布式能源聚合调度的新型模式,正在重塑电力系统的运行逻辑。这个项目直指虚拟电厂微网调度中最棘手的双重不确定性难题——既要应对电源侧(风电、光伏等)的出力波动,又要处理负荷侧的需求随机性…

2026/8/3 6:27:05 阅读更多 →
萍乡家电清洗值得做吗

萍乡家电清洗值得做吗

1. 家电清洗,真的只是“擦擦灰”吗?很多萍乡家庭觉得家电清洗就是表面擦一擦,花几十块买个清洗剂自己就能搞定。但真相是,空调、洗衣机、油烟机内部积累的细菌、霉菌和油垢,普通擦拭根本清不掉。一台使用三年的洗衣机&…

2026/8/3 6:26:05 阅读更多 →

最新新闻

STM32 ADC精度提升实战:内部参考电压VREFINT校准原理与应用

STM32 ADC精度提升实战:内部参考电压VREFINT校准原理与应用

1. 项目缘起:为什么需要关注ADC的内部参考电压?在嵌入式开发,尤其是基于STM32这类MCU进行精密数据采集的项目里,ADC(模数转换器)的精度是绕不开的核心指标。很多工程师在项目初期,可能会直接使用…

2026/8/4 9:36:37 阅读更多 →
Matlab实现综合能源系统优化调度与多能互补技术

Matlab实现综合能源系统优化调度与多能互补技术

1. 项目概述:综合能源系统的优化调度 在能源转型的大背景下,如何高效整合多种能源形式成为行业焦点。这个Matlab项目针对含光热电站、有机朗肯循环(ORC)和电转气(P2G)技术的综合能源系统,开发了一套优化调度方案。作为一名长期从事能源系统建…

2026/8/4 9:36:37 阅读更多 →
BLE Mesh设计解析:从去中心化拓扑到安全组网实战

BLE Mesh设计解析:从去中心化拓扑到安全组网实战

1. 项目概述:从BLE到BLE Mesh的跨越如果你玩过智能家居,大概率听说过“蓝牙Mesh”这个词。从智能灯泡到门锁,很多设备都开始支持这个协议。但很多人,包括一些刚入行的开发者,对它的理解可能还停留在“就是蓝牙组网嘛”…

2026/8/4 9:36:37 阅读更多 →
耐烤蛋糕杯门店上新前怎么打样?让包装跟上新、打包和展示一起落地

耐烤蛋糕杯门店上新前怎么打样?让包装跟上新、打包和展示一起落地

节日款出样时,耐烤蛋糕杯先别只看空杯照片,先把真实样品做出来。贴纸、卡套和礼盒比例是否合适,要在装杯、加盖、陈列和外带状态里一起确认。 一、先从上新打样开始打样 把耐烤蛋糕杯用于轻乳酪时,建议记录装杯量、出炉或冷藏后的…

2026/8/4 9:36:37 阅读更多 →
嵌入式DMA三件套:串口高效通信的核心配置与实战

嵌入式DMA三件套:串口高效通信的核心配置与实战

1. 先搞清楚“DMA三件套”到底是什么,以及它解决了什么问题如果你是第一次接触嵌入式开发,拿到一块开发板或者一个项目资料包,里面有个叫“DMA三件套”的东西,大概率会有点懵。这名字听起来像是什么神秘工具包,其实它指…

2026/8/4 9:36:37 阅读更多 →
Blender MMD Tools插件深度解析:从格式鸿沟到创作自由的完整技术路径

Blender MMD Tools插件深度解析:从格式鸿沟到创作自由的完整技术路径

Blender MMD Tools插件深度解析:从格式鸿沟到创作自由的完整技术路径 【免费下载链接】blender_mmd_tools MMD Tools is a blender addon for importing/exporting Models and Motions of MikuMikuDance. 项目地址: https://gitcode.com/gh_mirrors/bl/blender_mm…

2026/8/4 9:35:36 阅读更多 →

日新闻

AI Agent白手起家26: 使用标准事件驱动大模型实践

AI Agent白手起家26: 使用标准事件驱动大模型实践

纲要 练习目标:掌握大模型标准事件的调用回顾 LangChain 中的核心标准事件 invokestreambatchastream_eventswith_structured_output 环境准备实战代码:多种事件调用对比 同步调用与流式输出批量处理异步事件流监听结构化输出 运行说明与预期结果总结与扩…

2026/8/4 0:00:40 阅读更多 →
dealsea是什么?跨境卖家必知的美国deal站入门指南

dealsea是什么?跨境卖家必知的美国deal站入门指南

说实话,第一次听说美国这个老牌折扣网站的跨境卖家,十个有八个会问同一个问题:这个平台到底是干嘛的?我见过一个做家居出口的朋友,他在亚马逊上月销二十万美金,却从来没用过它。我给他看了首页——一屏一屏…

2026/8/4 0:01:40 阅读更多 →
清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

通讯作者:邓兵、刘建国通讯单位:清华大学DOI:https://doi.org/10.1021/acs.est.6c00603研究背景稀土元素(REEs)是清洁能源技术与电子器件不可或缺的核心原料,然而传统提取方式依赖能耗高、排放大的采矿与强…

2026/8/4 0:01:40 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/3 4:58:13 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/3 1:53:31 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/4 5:26:40 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/3 13:07:03 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/3 5:19:38 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/3 8:27:36 阅读更多 →