原文来自CRIU官网的文章《Memory dumps》
翻译此文,用于学习CRIU的dump和pre-dump机制。翻译时没有用翻译软件,不求字字准确,能表达出核心含义,语句通顺就行。甚至会按照我对CRIU的研究拓展一下。
本文作者按个人理解加以描述的文字用(__)来标记。
概述
进程的映射存放在mm.img文件中。但是这些信息仅仅记录了虚拟内存区域。这些区域内的数据都存储在下面描述的文件对中。
内存dump操作包含了单个内存页(大小4k)的内容和这些数据在虚拟内存中的哪个地址。这些image文件和mm.img中的VMA列表没有任何关系,只是按照地址匹配的原则使数据放在合适的位置。
内存dump的内容有:
- 匿名私有映射的内存页(这里一般指addr为NULL的mmap映射的匿名内存页)
- 匿名共享映射的内存页
- 文件私有映射的私有(拷贝)内存页(指用mmap将文件私有映射到内存中的内存页)
image结构
内存dump操作会产生两个image类型的文件。Pagemap
这是一个存放着一系列表项的列表。每个表项是一对数据,它记录了数据应该在内存的哪个位置的,以及它包含的内存页数量。Pages
这是一组内存页。每个内存页大小4k,存储了进程使用的所有内存数据。例子
假设下面是我们有的两个pagemap的表项:
这个例子表示共有12个内存页,按每个内存页4k来算,它的总大小为48k。然后前4个内存页(大小为16k,也就是记录在pagemap第一个表项的内容)会从images文件中被读取,并放在地址为0x1000000的位置,按照它占用得空间来算,它的内存尾地址为0x1000000 + 4 4096 = 0x1004000。最后的8个内存页(大小32k,在pagemap第二个表项)读取后被放到起始地址为0xCF000000上,它的尾地址应该就是0xCF000000 + 8 4096了。{ 0x1000000, 4}
{ 0xCF000000, 8}
叠加image
像《incremental dumps》文章中所述,对于每一次迭代,一个父符号链接会被创建,image文件就会依赖他们各自的父image。(即第一次pre-dump后产生第一个image,第二次pre-dump后产生第二个image,第二个image会依赖第一个image)
在这种场景年,pagemap表象中会出现第三个参数域,叫做in_parent。这是一个布尔变量,如果被置为1,则表示目前这个表项所对应的数据已经在它的父image中存在。当在父image中寻找数据时,同样的算法也会被使用到:首先解析pagemap,然后在page中找到数据。对于有父image的场景来说,数据可以部分或全部在父image中找到。
自然而然,最开始的那个image是没有任何的in_parent被置为1的。例子
和上一个例子差不多,现在我们有如下两个pagemap的表项(也就是上一个例子加上一个in_parent)
在这个例子中,page.img就只有32k的大小了。因为前4k大小已经存储在父image中,在本次的page.img不再记录。这样父pagemap.img就会包含一个或者更多的pagemap表项,来覆盖内存地址区间0x1000000到0x1004000,对于父image,如下这个例子:{ 0x1000000, 4, in_parent }
{ 0xCF000000, 8 }
这意味着,前2个内存页在此父image中是有效的,他会去找对应的page.img获取内存地址区间0x1000000到0x1002000的内容。而后2个内存页,则需要在爷爷辈的image中才能找到了。{ 0x1000000, 2 }
{ 0x1002000, 2, in_parent }
重删
参见《Memory images deduplication》查看更多