do_try_to_free_pages
/*
* This is the main entry point to direct page reclaim.
*
* If a full scan of the inactive list fails to free enough memory then we
* are "out of memory" and something needs to be killed.
*
* If the caller is !__GFP_FS then the probability of a failure is reasonably
* high - the zone may be full of dirty or under-writeback pages, which this
* caller can't do much about. We kick the writeback threads and take explicit
* naps in the hope that some of these pages can be written. But if the
* allocating task holds filesystem locks which prevent writeout this might not
* work, and the allocation attempt will fail.
*
* returns: 0, if no pages reclaimed
* else, the number of pages reclaimed
*/
这是个超级重要的函数,是回收页的主逻辑。
do_try_to_free_pages -> shrink_zones -> shrink_node -->shrink_node_memcg -->
mem_cgroup_soft_limit_reclaim
进行内存回收的时候,balance_pgdat和shrink_zones的时候,都是先去回收soft_limit
只在内存回收的时候起作用,只有全局回收的时候才会去
mem_cgroup_soft_limit_reclaim --> mem_cgrop_soft_reclaim -->mem_cgroup_shrink_node-->shrink_node_memcg--> ... -->shrink_page_list
最终都会计入
所以shrink_nodes是真正的shrink page的地方,在shrink page的地方,只会去回收那些clean的页,在这个函数中先会回收mem soft limit,然后会去刷全局的page
shrink_zones只在一个地方调用,就是do_write_data_pages,这里是所有直接内存回收的地方,包括page_alloc,包括memcgroup缩容,都是在这里, do_try_to_free_pages这里是,这是直接内存回收的路径,① kwapd_shrink_node--->shrink_node-->shrink_node_memcg
shrink_node_memcg函数就是所有函数的殊途同归的地方,shrink_node是啥子呢?
② node_reclaim-->__node_reclaim-->shrink_node 这个是快速回收方法, shrink_node就是
③ do_try_to_free_pages --> shrink_nodes --> shrink_node
所以shrink_node从三个地方来,所以地方一是慢速回收,地方二是用do_try_to_free_pages慢速回收,再就是后台回收了,前两者都是全局的回收,do_try_to_free_pages就包括全局回收,也包括cgroup回收啦,mem_cgroup_soft_limit_reclaim就完全是和主流的回收流程并行的一套回收逻辑了。咋说呢?mem_cgroup_soft_limit_recliam的出发点有两个1> balance_pgdat 2>使用在使用shrink_zones的时候,do_try_to_free_pages的时候要用,并且在是在全局回收的时候调用。
所以我们就知道了,soft_limit_reclaim在什么时候起作用呢?就是在全局内存内存回收的时候。也就是在上图中的1)和3)两条线,机型内存回收时,如果是全局的回收,在调用最后的shrink_node_memcg之前
shrink_zones直接内存回收
【疑问一:】那page的lru到底是放在了cgroup的lru列表里,还是放到了全局的lru列表中去呢?
shrink_node_memcg 函数中,如果没有cgroup,那么就使用pgdat->lruvec全局的lru的全局表,如果否则放到了cgroup的lru表中,啥时候把页放到lru中呢?看函数__page_cache_alloc,中有mem_cgroup_try_charge
memcg_kmem_charge_memcg,啥时候
memory_cgroup_commit_charge --> commit_charge ---> unlock_page_lru 是不是所有的lru表并不是所有的页全都在lru表上的呢
函数mm/filemap.c __add_to_page_cache_locked函数中会charge page:
lru_cache_add函数是把page增加到lru链表中
add_to_cache_lru 函数中增加到lru链表中去,
page-cache增加页:add_to_cache_lru函数中1)__add_to_cache_lru, 2) lru_cache_add
普通匿名页:lru_cache_add_active_or_unevictable函数中间接调用lru_cache_add
在lru_cache_add函数之前已经调用过了函数mem_cgroup_commit_charge函数,这个函数中已经把 page->mem_cgroup = memcg; 做了这样的处理了。
这是在page-cache中,所以肯定的是,lru有全局表有每个cgroup的表,这些页都是分布在不同的表中。
那么这个page不同的lru列表,shrink的时候是按照LRU表来shrink的,但是页的lru链表是放到不同的cgroup里的,如果遍历这些cgroup表呢?
/**
* mem_cgroup_commit_charge - commit a page charge
* @page: page to charge
* @memcg: memcg to charge the page to
* @lrucare: page might be on LRU already
* @compound: charge the page as compound or small page
*
* Finalize a charge transaction started by mem_cgroup_try_charge(),
* after page->mapping has been set up. This must happen atomically
* as part of the page instantiation, i.e. under the page table lock
* for anonymous pages, under the page lock for page and swap cache.
*
* In addition, the page must not be on the LRU during the commit, to
* prevent racing with task migration. If it might be, use @lrucare.
*
* Use mem_cgroup_cancel_charge() to cancel the transaction instead.
*/
5423 void mem_cgroup_commit_charge(struct page *page, struct mem_cgroup *memcg, bool lrucare, bool compound)
memory_cgroup_soft_limit在哪里用呢?首先是kwapd_shrink_node函数里,在调用shrink_node之前,会首先回收soft_limit超过阈值的页,再就是在函数shrink_zones中,shirik_zones只在一个地方使用do_try_to_free_pages,在强制内存回收的时候,如果是全局的强制内存回收,还是先想着想着回收内存中,
【疑问二:为啥干净的页就能够】
全局管理有个好处,LRU是全局管理,大家一份,但是现在
void mem_cgroup_commit_charge(struct page *page, struct mem_cgroup *memcg,
shrink_node-> shrink_node_memcg