经过大量调查,我对原有的理解存在几个问题。
首先,页面缓存是驻留在内存中的非连续页面集合。很容易陷入认为“页面缓存”是单个连续的内存块/页面(类似于硬件缓存)的陷阱,但实际上,页面缓存只是内存中某些页面的集合,可供将来访问使用。
这些“页面缓存”中的页面甚至没有虚拟地存储在一起。也就是说,内核
不会将所有页面缓存中的页面保存在一个全局结构或列表中。相反,更好的思考方式是,页面缓存实际上是内核中所有LRU列表的并集。
这一点以及mm/vmscan.c有很多关于mem_cgroups的引用,并且有一个名为shrink_node_memcgs的函数,使我认为每个cgroup都有自己的页面缓存。
如上所述,每个cgroup都有自己的LRU列表。然而,每个cgroup并没有自己的页面缓存。但是,如果你汇总所有cgroup的LRU列表,你将持有页面缓存的所有页面。
我知道页面缓存是由结构体address_space表示的。
这是不正确的。一个
struct address_space
确实代表了页面缓存中的一些页面,但它本身并不代表整个页面缓存。实际上,
struct address_space
表示单个
inode
(文件)或块设备中缓存的页面(请参见
address_space
结构中的
host
成员)。事实上,原始帖子中
其中之一的链接引用了这句话:“
struct address_space
的一个更好的名称可能是
page_cache_entity或
physical_pages_of_a_file。”(
第16章,第327页)
您如何找出与给定的struct address_space相关联的cgroup?
由于address_space对应于单个inode(文件),而不是单个cgroup,因此并非所有在此结构中的页面都归入同一cgroup,并且因此驻留在相同的cgroup LRU列表上。例如,想象一下,cgroup 1中的一个进程读取大文件的开头,但是cgroup 2中的另一个进程读取该文件的结尾。每个进程访问的页面来自相同的inode和struct address_space,但其中一些页面将位于cgroup 1的LRU列表中,而其他页面将位于cgroup 2的LRU列表中。
因此,无法从struct address_space中找到cgroup。相反,在理论上,您可以遍历struct address_space页面,然后找到与每个单独页面对应的cgroup(page->mem_cgroup->css.cgroup)。
请注意,一个页面可能被分配给一个cgroup,但仍然可以被不同cgroup中的另一个进程共享/访问。有关为共享内存收费的规则,请参见v1版本(第2.3节)和v2版本("内存所有权")。
补充:
在我的研究中,我遇到了
这篇文章,它增加了我的困惑并让我认为
address_space
与单个cgroup相关联。图4.2似乎表明
address_space
被嵌套在
mm_struct
中;由于
mm_struct
特定于进程,因此这个
address_space
也应该对应于一个进程,并且通过推广,对应于该进程的cgroup。实际上,这个
mm_struct
对应的进程持有一个文件描述符(由
struct file
表示)和这个文件描述符导致文件
inode
及其相应的
address_space
。需要这个
address_space
才能在“页面缓存”中找到来自该文件的特定页面。