Linux内核中页缓存(page cache)、struct address_space与内存控制组(memory cgroups v2)的关联及相关技术疑问
咱们从你提到的几个核心疑问逐个拆解,结合内核代码逻辑来解释:
1. 每个控制组是否拥有独立的页缓存?
答案是否,但每个cgroup会独立管理属于自己的页缓存页面。
页缓存本身是全局的、基于文件/struct address_space的缓存机制,不会为每个cgroup单独创建一套页缓存实例。不过,当cgroup内的进程产生页缓存页面时(比如读取文件),这些页面会被标记上所属的mem_cgroup,并且每个mem_cgroup会维护自己的LRU链表——其中就包含了属于该cgroup的页缓存页面、匿名页面等各类内存页面。
你提到的mm/vmscan.c里的shrink_node_memcgs函数,核心逻辑就是遍历当前节点下的各个mem_cgroup,针对每个mem_cgroup的LRU链表进行页面回收,这也印证了cgroup是按归属关系管理页缓存页面,而非拥有独立的页缓存。
2. 页缓存中的所有页面是否均属于同一个控制组?
答案也是否。
一个struct address_space对应一个文件的页缓存(或类似缓存对象),如果不同cgroup的进程都访问这个文件,新生成的页缓存页面会归属到发起读写操作的进程所在的cgroup。也就是说,同一个address_space下的页缓存页面,可能分属多个不同的mem_cgroup。
举个例子:cgroup A里的进程读取了文件X的前4KB,生成的页缓存页面属于cgroup A;之后cgroup B里的进程读取文件X的后4KB,生成的新页面就属于cgroup B。这两个页面都属于同一个address_space,但分属不同的cgroup。
3. 如何确定给定struct address_space所关联的控制组?
这里要明确:struct address_space本身并不直接关联某个固定的mem_cgroup,因为它的页面可能分属多个cgroup。
如果要确定某个具体页缓存页面所属的cgroup,你可以通过struct page结构体中的mem_cgroup指针(在cgroup v2中,这个指针直接指向该页面归属的mem_cgroup实例)来获取。但绝对不能通过“取address_space的第一个页面来确定整个address_space的关联cgroup”——因为前面已经说明,同一个address_space的页面可能属于不同的cgroup,这种方式得到的结果片面且不准确。
如果需要遍历某个address_space下所有页面的归属cgroup,你需要遍历该address_space的页面链表(比如通过address_space->i_pages),逐个检查每个page的mem_cgroup字段。
内容的提问来源于stack exchange,提问作者wxz

