从 DRAM 压缩页面到 zpool 的详细过程
1. 触发页面压缩
当系统检测到内存压力(例如 swappiness
设定较高或 kswapd
触发内存回收)时,zswap
可能会主动压缩部分页面并存入 zpool
,具体触发路径如下:
- 进程释放内存时,
page reclaim
机制会尝试回收不常访问的页面。 swapout
发生时,系统会将部分页面标记为可交换。zswap
在frontswap
机制下拦截交换请求,并决定是否压缩页面存入zpool
。
2. zswap
拦截页面交换请求
zswap
作为 frontswap
的后端,负责在页面真正写入 swap
设备之前拦截并尝试压缩存入 zpool
。
zswap_frontswap_store()
负责拦截swap
写入请求。- 该函数决定是否将页面压缩存入
zswap
,如果zswap
满了,则继续传统swapout
过程。
int zswap_frontswap_store(unsigned type, pgoff_t offset,
struct page *page);
3. 在 zpool
中分配存储空间
在 zpool
中,zswap
选择合适的后端来存储压缩数据,目前支持:
- zbud:一个物理页最多存储 2 个压缩页。
- zsmalloc:支持多个小页面共享一个物理页,提高空间利用率。
zpool
负责管理这部分内存,并通过 zpool_malloc()
申请空间。
void *zpool_malloc(struct zpool *pool, size_t size);
4. 压缩页面数据
zswap
选择合适的压缩算法(如 LZ4
、ZSTD
)对页面数据进行压缩。
size_t compressed_size = LZ4_compress_default(input_page, output_buffer,
PAGE_SIZE, max_compressed_size);
压缩过程:
zswap_compress()
选择合适的压缩算法。zpool_malloc()
申请存储压缩数据的空间。zswap_store()
将压缩后的数据存入zpool
。
5. 创建 zswap_entry
记录映射
成功存入 zpool
后,zswap
需要在 rb_tree
里维护 swap entry
到 zpool
句柄的映射。
struct zswap_entry {
struct rb_node rbnode;
unsigned long handle; // 指向 zpool 里的压缩数据
unsigned type;
pgoff_t offset;
};
插入 rb_tree
以便后续查询:
zswap_rb_insert(entry);
6. 释放原页面
压缩存储完成后,原始 DRAM 页可以释放,减少物理内存占用。
__free_page(page);
完整流程总结
swapout
触发页面交换。zswap_frontswap_store()
拦截swapout
,决定是否压缩存入zpool
。zpool_malloc()
申请存储空间。LZ4_compress_default()
或zstd_compress()
进行数据压缩。zswap_store()
将压缩数据存入zpool
。zswap_rb_insert()
维护swap entry
到zpool
句柄的映射。- 释放原始 DRAM 页以节省内存。
如果你希望修改 zpool
存储流程,比如:
- 更换压缩算法(如 Brotli、Snappy)。
- 优化
NUMA
亲和性,使zpool
更合理地管理 CXL 和 DRAM。 - 添加额外的压缩元数据(如 CRC 校验)。
可以修改 zswap_store()
和 zpool_malloc()
相关代码。
你是希望改进 zswap
以适配 CXL 体系结构吗?