首页
关于
Search
1
2023 年保研经验贴 [计算机-末九Rank50%-入北计清深-北计直博]
349 阅读
2
Linux Kernel-THP阅读分析[Hang up]
255 阅读
3
FreeBSD Kernel-编译环境搭建
223 阅读
4
Linux Kernel-编译调试环境搭建
176 阅读
5
Linux Kernel-Zswap 1.功能、流程与原理
156 阅读
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
技术之外
开发
rcore-arm
登录
Search
yyi
累计撰写
46
篇文章
累计收到
0
条评论
首页
栏目
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
技术之外
开发
rcore-arm
页面
关于
搜索到
6
篇与
的结果
2023-08-01
Linux Kernel-Zswap 1.功能、流程与原理
功能、流程与原理Source Code : zswap.c基础功能zswap 提供了一种 frontswap 的 Hook,使得在内存不足的时候首先调用 zswap 对即将换出的页面进行压缩,并存储在 zswap 的内存池中。由于页面交换的数据量越大,IO 操作越多会导致系统性能越差,zswap 采用了一种 CPU 时间换 IO 时间的策略,通过压缩的方式减小会被换入换出的数据量。流程init_zswap()在 Linux 内核开启 zswap 功能后,init_module()函数就会执行 init_zswap(),首先初始化 zswap_entry_cache,它为未来快速分配内存页面空间提供数据结构基础。注册 cpu 热插拔通知链,分别对应 zswap_dstmem_prepare回调和zswap_dstmem_dead回调。调用__zswap_pool_create_fallback注册 zswap 所使用的内存池, 如果创建成功, 把这个 pool 挂到表头创建一个工作队列,把句柄留在 shrink_wq 中调用 frontswap_register_ops,对 zswap 的相关函数进行注册(包括 store、load、inval-page、inval-area等函数)frontswap_register_ops : zswap 在某个版本后,接入了 frontswap API,通过这个函数注册自己的页面压缩、存储、恢复等操作。linux 默认的内存交换机制并不采用 frontswap,开启 frontswap 后就会使用注册的 swap 函数进行 swap 操作以上的任何一步出错,都会导致 Goto 到清理步骤,并返回错误。zswap_frontswap_init()使用 kzalloc 给对应 type 的页面新建一个树节点,并注册到zswap_trees 数组中,初始化对应树的自旋锁zswap_frontswap_store()获取对应页面的树元信息,组建一个zswap_header结构,就是一个ulong 数据对输入参数进行检查,判断:是否为 Huge 页面,若是 reject判断是否启用 zswap 和是否有对应的 tree,否则 reject验证 objcg 是否允许 zswap,否则 shrink判断 zswap pool 是否已满,如果已满,记录并跳转 shrink如果未满但是曾经满过,检查能否接受当前页面,如果能,把满标记去除,否则 reject准备 entry在 entry_cache里申请一段空间(并且初始化相关信息,如 rb_node等),否则记录失败,reject判断 zswap_same_filled_pages_enabled 选项,如果开启,对同样内容的页(全 0、全 1)进行特殊操作,并跳转到insert_enrty判断zswap_non_same_filled_pages_enabled选项,如果未开启,准备返回错误,freepage通过zswap_pool_current_get找到当前的 pool判断 pool 是否可用,即判断当前的 kref 是否为 0不可用则 NULL,否则返回若没有可用 pool,freepage压缩获取当前 CPU 上的acomp_ctx 信息,加互斥锁初始化两个 scatterlist,分别绑定给 page 和output,向 acompress 实例发送一个异步请求,并直接等待对应 acompctx 的 wait,直到异步请求返回。判断压缩是否成功,否则put_dstmem入池判断当前的压缩池是否支持动态回收,并以此为依据判断是否要为数据 hdr 预留空间。理由是:如果支持动态回收,要单独把头部信息分开,方便回收时修改元数据,否则可以把头部和数据放在一起,节省空间。根据压缩池是否支持可移动内存设置 GFP,并根据 GFP 标志申请内存空间。如果未成功,put_dstmem使用 zpool_map_handle将内存映射到 zswap pool 的地址空间,把 hdr 和压缩后数据写入该空间,然后解除映射、释放 acomp_ctx的锁更新offset、handle 和 entry 长度信息挂树首先判断 objcg 的信息,并进行计数获取对应 rbtree 的锁调用红黑树插入对应 offset 的节点,相应的插入函数会在 offset 重复的时候返回 -EEXIST 枚举,若出现了这种情况,程序会先把相应的重复节点 erase 掉,再处理它的引用计数。并retry 直到返回值不为-EEXIST释放锁、计数、更新压缩池大小释放解除 acomp 上下文的锁释放 pool 的 kref、释放 objcg 的 kref,返回shrink:获取最新的内存池,把 shrink 工作添加到 shrink work 队列,返回 ENOMEM。zswap_frontswap_load()查找通过 type 获取树根给树加锁通过 offset 在红黑树上找到对应的 entry,解锁,如果找不到,返回-1(可能已经被写到交换设备中了)判断length 是否为 0,如果为 0 证明是一个内容全相同的页面,fill 之,跳过后面的步骤,进 stat如果 zpool 不支持睡眠映射,则调用 kmalloc 在 KERNEL 中分配一段大小为 entry 的 length 的内存,如果失败,freeentry解压通过 zpool_map_handler把指定页面映射到 zswap 对应位置,通过zpool_evictable判断之前 store 的时候压没压 hdr 的长度,如果有 hdr,进行跳过如果不支持睡眠映射,把刚刚获得的数据 copy 到之前申请的内存中,解除映射获取 acomp 的上下文,获取 acomp 上下文的锁创建对应的 scatterlist(如果支持睡眠映射,在此处直接映射,否则就要用之前的 copy 过的内存内容),用差不多的方式调用 decompress,等待解压完毕解除锁解除映射或者 free刚刚申请的内存空间计数、获取树的锁、释放节点、释放树的锁zswap_frontswap_invalidate_page()没什么可说的,从树上删掉一个 entry,做好相关的加锁解锁zswap_frontswap_invalidate_area()先序遍历整个 type 的树,全干掉。除了 frontswap 相关 API 的实现,zswap 也需要实现兜底相关的内容zswap_writeback_entry()在系统需要回收内存页时,zswap 会通过这段代码向 swap cache 添加一个页面,然后解压并写入到交换设备中。找到节点并解压的过程类似 load()函数,区别是这里的参数是 zpool 的 handle句柄,程序先通过 handle 映射内存,获得 zswap 的 header,再读取 header信息获得 offset,进而执行后续的解压缩操作。在获得 offset 后、解压前,还进行了一系列验证和清理相关的操作。流程之外流程之外,zswap 提供了诸多管理 pool、entry、tree 的函数,如创建、计数 aquire(get),计数 relese(put)等。但是这些函数都较为简单于望文生义,不在此赘述。
2023年08月01日
156 阅读
0 评论
0 点赞
1
2